Compare commits
41 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 |
@@ -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,42 @@
|
||||
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>`_)
|
||||
|
||||
@@ -47,12 +47,14 @@ set(${PROJECT_NAME}_SRCS
|
||||
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
|
||||
@@ -64,6 +66,7 @@ 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
|
||||
@@ -140,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()
|
||||
@@ -213,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()
|
||||
@@ -237,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}
|
||||
@@ -259,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()
|
||||
@@ -324,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})
|
||||
@@ -399,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()
|
||||
@@ -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)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP__CONTEXT_HPP_
|
||||
#define RCLCPP__CONTEXT_HPP_
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@@ -26,6 +27,8 @@
|
||||
#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"
|
||||
@@ -147,21 +150,21 @@ public:
|
||||
* - 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
|
||||
* - if notify_all is true, rclcpp::notify_all is called to unblock some ROS functions
|
||||
* - 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
|
||||
* \param[in] notify_all if true, then rclcpp::notify_all will be called
|
||||
* \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, bool notify_all = true);
|
||||
shutdown(const std::string & reason);
|
||||
|
||||
using OnShutdownCallback = std::function<void ()>;
|
||||
|
||||
@@ -170,8 +173,9 @@ public:
|
||||
* These callbacks will be called in the order they are added as the second
|
||||
* to last step in shutdown().
|
||||
*
|
||||
* These callbacks may be run in the signal handler as the signal handler
|
||||
* may call shutdown() on this context.
|
||||
* 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
|
||||
@@ -211,6 +215,86 @@ 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>
|
||||
@@ -261,6 +345,16 @@ private:
|
||||
|
||||
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.
|
||||
|
||||
@@ -151,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,
|
||||
@@ -164,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(),
|
||||
@@ -218,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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -134,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
|
||||
@@ -155,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_;
|
||||
|
||||
@@ -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_
|
||||
@@ -41,19 +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"
|
||||
@@ -71,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();
|
||||
@@ -116,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
|
||||
@@ -137,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.
|
||||
@@ -147,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.
|
||||
/**
|
||||
@@ -166,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.
|
||||
/**
|
||||
@@ -199,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.
|
||||
/**
|
||||
@@ -222,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.
|
||||
/**
|
||||
@@ -240,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);
|
||||
|
||||
@@ -278,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;
|
||||
@@ -304,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
|
||||
@@ -458,6 +555,127 @@ 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)
|
||||
|
||||
@@ -473,9 +691,12 @@ private:
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
|
||||
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,24 +232,23 @@ 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)
|
||||
{
|
||||
@@ -182,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);
|
||||
@@ -199,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>
|
||||
@@ -216,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>();
|
||||
}
|
||||
@@ -237,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(
|
||||
@@ -244,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;
|
||||
}
|
||||
@@ -258,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
|
||||
|
||||
@@ -64,7 +64,6 @@ private:
|
||||
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
|
||||
|
||||
@@ -54,7 +54,9 @@ public:
|
||||
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
|
||||
@@ -89,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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -31,6 +31,10 @@ class NodeWaitablesInterface
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeWaitablesInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeWaitablesInterface() = default;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
|
||||
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
|
||||
|
||||
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_
|
||||
@@ -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_
|
||||
@@ -51,7 +51,9 @@ public:
|
||||
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::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();
|
||||
@@ -76,6 +78,8 @@ private:
|
||||
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_;
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
#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"
|
||||
|
||||
@@ -184,9 +181,11 @@ shutdown(
|
||||
* If nullptr is given for the context, then the global context is used, i.e.
|
||||
* the context initialized by rclcpp::init().
|
||||
*
|
||||
* Note that these callbacks should be non-blocking and not throw exceptions,
|
||||
* as they may be called from signal handlers and from the destructor of the
|
||||
* context.
|
||||
* 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
|
||||
@@ -196,53 +195,18 @@ RCLCPP_PUBLIC
|
||||
void
|
||||
on_shutdown(std::function<void()> callback, rclcpp::Context::SharedPtr context = nullptr);
|
||||
|
||||
/// 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[in] wait_set Pointer to the rcl_wait_set_t that will be using the
|
||||
* resulting guard condition.
|
||||
* \param[in] context The context associated with the guard condition.
|
||||
* \return Pointer to the guard condition.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rcl_guard_condition_t *
|
||||
get_sigint_guard_condition(rcl_wait_set_t * wait_set, rclcpp::Context::SharedPtr context);
|
||||
|
||||
/// Release the previously allocated guard condition that manages the signal handler.
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* If nullptr is given for the context, then the global context is used, i.e.
|
||||
* the context initialized by rclcpp::init().
|
||||
*
|
||||
* \param[in] wait_set Pointer to the rcl_wait_set_t that was using the
|
||||
* resulting guard condition.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
release_sigint_guard_condition(rcl_wait_set_t * wait_set);
|
||||
|
||||
/// 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 rclcpp::shutdown() or the signal handler.
|
||||
* 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.
|
||||
* \param[in] context which may interrupt this sleep
|
||||
* \return True if the condition variable did not timeout.
|
||||
* \return true if the condition variable did not timeout.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
@@ -250,11 +214,6 @@ sleep_for(
|
||||
const std::chrono::nanoseconds & nanoseconds,
|
||||
rclcpp::Context::SharedPtr context = nullptr);
|
||||
|
||||
/// Notify all blocking calls so they can check for changes in rclcpp::ok().
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
notify_all();
|
||||
|
||||
/// Safely check if addition will overflow.
|
||||
/**
|
||||
* The type of the operands, T, should have defined
|
||||
|
||||
@@ -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.1</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
|
||||
|
||||
@@ -118,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;
|
||||
@@ -127,6 +126,7 @@ 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 =
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#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;
|
||||
@@ -127,7 +128,7 @@ Context::shutdown_reason()
|
||||
}
|
||||
|
||||
bool
|
||||
Context::shutdown(const std::string & reason, bool notify_all)
|
||||
Context::shutdown(const std::string & reason)
|
||||
{
|
||||
// prevent races
|
||||
std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
|
||||
@@ -147,10 +148,9 @@ Context::shutdown(const std::string & reason, bool notify_all)
|
||||
for (const auto & callback : on_shutdown_callbacks_) {
|
||||
callback();
|
||||
}
|
||||
// notify all blocking calls, if asked
|
||||
if (notify_all) {
|
||||
rclcpp::notify_all();
|
||||
}
|
||||
// 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(); ) {
|
||||
@@ -190,6 +190,99 @@ 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()
|
||||
{
|
||||
|
||||
@@ -51,8 +51,7 @@ Executor::Executor(const ExecutorArgs & args)
|
||||
// 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_, args.context));
|
||||
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_);
|
||||
@@ -61,9 +60,8 @@ Executor::Executor(const ExecutorArgs & args)
|
||||
// Store the context for later use.
|
||||
context_ = args.context;
|
||||
|
||||
if (rcl_wait_set_init(
|
||||
&wait_set_, 0, 2, 0, 0, 0, allocator) != RCL_RET_OK)
|
||||
{
|
||||
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);
|
||||
@@ -105,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_, context_));
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -36,7 +36,8 @@ namespace graph_listener
|
||||
{
|
||||
|
||||
GraphListener::GraphListener(std::shared_ptr<rclcpp::Context> parent_context)
|
||||
: is_started_(false),
|
||||
: parent_context_(parent_context),
|
||||
is_started_(false),
|
||||
is_shutdown_(false),
|
||||
interrupt_guard_condition_context_(nullptr),
|
||||
shutdown_guard_condition_(nullptr)
|
||||
@@ -54,12 +55,12 @@ GraphListener::GraphListener(std::shared_ptr<rclcpp::Context> parent_context)
|
||||
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
|
||||
}
|
||||
|
||||
shutdown_guard_condition_ = rclcpp::get_sigint_guard_condition(&wait_set_, parent_context);
|
||||
shutdown_guard_condition_ = parent_context->get_interrupt_guard_condition(&wait_set_);
|
||||
}
|
||||
|
||||
GraphListener::~GraphListener()
|
||||
{
|
||||
this->shutdown();
|
||||
this->shutdown(std::nothrow);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -78,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");
|
||||
@@ -90,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.
|
||||
@@ -337,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)) {
|
||||
@@ -351,7 +354,11 @@ GraphListener::shutdown()
|
||||
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_) {
|
||||
@@ -363,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()
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -29,39 +29,77 @@
|
||||
#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())),
|
||||
@@ -79,15 +117,64 @@ Node::Node(
|
||||
node_topics_,
|
||||
node_services_,
|
||||
node_clock_,
|
||||
initial_parameters,
|
||||
use_intra_process_comms,
|
||||
start_parameter_services
|
||||
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())),
|
||||
use_intra_process_comms_(use_intra_process_comms)
|
||||
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()
|
||||
{}
|
||||
|
||||
@@ -103,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
|
||||
{
|
||||
@@ -263,6 +356,12 @@ 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()
|
||||
{
|
||||
@@ -292,3 +391,29 @@ 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_;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
@@ -44,7 +42,7 @@ 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_, context->get_rcl_context().get(), guard_condition_options);
|
||||
¬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");
|
||||
}
|
||||
@@ -59,76 +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(),
|
||||
context->get_rcl_context().get(), &options);
|
||||
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
|
||||
@@ -195,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()
|
||||
@@ -232,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()
|
||||
{
|
||||
|
||||
@@ -31,15 +31,7 @@ NodeClock::NodeClock(
|
||||
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_,
|
||||
node_logging_);
|
||||
time_source_.attachClock(ros_clock_);
|
||||
}
|
||||
{}
|
||||
|
||||
NodeClock::~NodeClock()
|
||||
{}
|
||||
|
||||
@@ -39,8 +39,10 @@ NodeParameters::NodeParameters(
|
||||
const rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
|
||||
const std::vector<rclcpp::Parameter> & initial_parameters,
|
||||
bool use_intra_process,
|
||||
bool start_parameter_services)
|
||||
: node_clock_(node_clock)
|
||||
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>;
|
||||
@@ -52,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();
|
||||
@@ -173,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);
|
||||
}
|
||||
@@ -202,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"
|
||||
@@ -225,15 +229,19 @@ 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());
|
||||
}
|
||||
}
|
||||
|
||||
std::swap(tmp_map, parameters_);
|
||||
parameter_event->stamp = node_clock_->get_clock()->now();
|
||||
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;
|
||||
}
|
||||
@@ -283,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);
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,9 @@ void TimeSource::attachNode(rclcpp::Node::SharedPtr node)
|
||||
node->get_node_topics_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
node->get_node_logging_interface());
|
||||
node->get_node_logging_interface(),
|
||||
node->get_node_clock_interface(),
|
||||
node->get_node_parameters_interface());
|
||||
}
|
||||
|
||||
void TimeSource::attachNode(
|
||||
@@ -61,17 +63,38 @@ void TimeSource::attachNode(
|
||||
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::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_,
|
||||
@@ -92,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();
|
||||
}
|
||||
|
||||
@@ -103,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)
|
||||
@@ -123,7 +150,9 @@ void TimeSource::detachClock(std::shared_ptr<rclcpp::Clock> 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();
|
||||
}
|
||||
}
|
||||
@@ -257,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,145 +14,16 @@
|
||||
|
||||
#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/logging.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/scope_exit.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
|
||||
|
||||
/// Mutex to protect installation and uninstallation of signal handlers.
|
||||
static std::mutex g_signal_handlers_mutex;
|
||||
/// Atomic bool to control setup of signal handlers.
|
||||
static std::atomic<bool> g_signal_handlers_installed(false);
|
||||
|
||||
/// Mutex to protect g_sigint_guard_cond_handles
|
||||
static std::mutex g_sigint_guard_cond_handles_mutex;
|
||||
/// 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;
|
||||
|
||||
/// Condition variable for timed sleep (see sleep_for).
|
||||
static std::condition_variable g_interrupt_condition_variable;
|
||||
/// 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
|
||||
{
|
||||
#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
|
||||
}
|
||||
|
||||
void
|
||||
#ifdef HAS_SIGACTION
|
||||
signal_handler(int signal_value, siginfo_t * siginfo, void * context)
|
||||
#else
|
||||
signal_handler(int signal_value)
|
||||
#endif
|
||||
{
|
||||
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "signal_handler(signal_value=%d)", 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
|
||||
|
||||
for (auto context_ptr : rclcpp::get_contexts()) {
|
||||
if (context_ptr->get_init_options().shutdown_on_sigint) {
|
||||
// do not notify all, instead do that once after all are shutdown
|
||||
context_ptr->shutdown("signal handler", false /* notify_all */);
|
||||
}
|
||||
}
|
||||
rclcpp::notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::init(int argc, char const * const argv[], const rclcpp::InitOptions & init_options)
|
||||
{
|
||||
@@ -165,46 +36,19 @@ rclcpp::init(int argc, char const * const argv[], const rclcpp::InitOptions & in
|
||||
bool
|
||||
rclcpp::install_signal_handlers()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_signal_handlers_mutex);
|
||||
bool already_installed = g_signal_handlers_installed.exchange(true);
|
||||
if (already_installed) {
|
||||
return false;
|
||||
}
|
||||
#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);
|
||||
#else
|
||||
::old_signal_handler = set_signal_handler(SIGINT, ::signal_handler);
|
||||
#endif
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "signal handler installed");
|
||||
return true;
|
||||
return rclcpp::SignalHandler::get_global_signal_handler().install();
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::signal_handlers_installed()
|
||||
{
|
||||
return g_signal_handlers_installed.load();
|
||||
return rclcpp::SignalHandler::get_global_signal_handler().is_installed();
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::uninstall_signal_handlers()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_signal_handlers_mutex);
|
||||
bool installed = g_signal_handlers_installed.exchange(false);
|
||||
if (!installed) {
|
||||
return false;
|
||||
}
|
||||
#ifdef HAS_SIGACTION
|
||||
set_sigaction(SIGINT, ::old_action);
|
||||
#else
|
||||
set_signal_handler(SIGINT, ::old_signal_handler);
|
||||
#endif
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "signal handler uninstalled");
|
||||
return true;
|
||||
return rclcpp::SignalHandler::get_global_signal_handler().uninstall();
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
@@ -300,14 +144,10 @@ rclcpp::shutdown(rclcpp::Context::SharedPtr context, const std::string & reason)
|
||||
if (nullptr == context) {
|
||||
context = default_context;
|
||||
}
|
||||
|
||||
bool ret = context->shutdown(reason);
|
||||
|
||||
// Uninstall the signal handlers if this is the default context's shutdown.
|
||||
if (context == default_context) {
|
||||
uninstall_signal_handlers();
|
||||
rclcpp::uninstall_signal_handlers();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -321,84 +161,14 @@ rclcpp::on_shutdown(std::function<void()> callback, rclcpp::Context::SharedPtr c
|
||||
context->on_shutdown(callback);
|
||||
}
|
||||
|
||||
rcl_guard_condition_t *
|
||||
rclcpp::get_sigint_guard_condition(rcl_wait_set_t * wait_set, rclcpp::Context::SharedPtr context)
|
||||
{
|
||||
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 {
|
||||
using rclcpp::contexts::default_context::get_global_default_context;
|
||||
if (nullptr == context) {
|
||||
context = get_global_default_context();
|
||||
}
|
||||
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, context->get_rcl_context().get(), options);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't initialize guard condition: ");
|
||||
}
|
||||
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*
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::sleep_for(const std::chrono::nanoseconds & nanoseconds, rclcpp::Context::SharedPtr context)
|
||||
{
|
||||
std::chrono::nanoseconds time_left = nanoseconds;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(::g_interrupt_mutex);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
// this will release the lock while waiting
|
||||
::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;
|
||||
if (nullptr == context) {
|
||||
context = get_global_default_context();
|
||||
}
|
||||
if (time_left > std::chrono::nanoseconds::zero() && ok(context)) {
|
||||
return sleep_for(time_left);
|
||||
}
|
||||
// Return true if the timeout elapsed successfully, otherwise false.
|
||||
return ok(context);
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::notify_all()
|
||||
{
|
||||
{
|
||||
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_interrupt_condition_variable.notify_all();
|
||||
return context->sleep_for(nanoseconds);
|
||||
}
|
||||
|
||||
const char *
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
@@ -55,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
|
||||
@@ -73,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);
|
||||
|
||||
@@ -90,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) {
|
||||
@@ -112,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);
|
||||
|
||||
@@ -131,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);
|
||||
@@ -138,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();
|
||||
|
||||
@@ -205,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);
|
||||
|
||||
@@ -214,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();
|
||||
|
||||
@@ -235,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_);
|
||||
@@ -254,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_);
|
||||
|
||||
@@ -264,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_);
|
||||
}
|
||||
@@ -299,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);
|
||||
@@ -326,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_);
|
||||
@@ -340,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);
|
||||
@@ -349,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());
|
||||
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());
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -394,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_);
|
||||
|
||||
@@ -2,6 +2,18 @@
|
||||
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>`_)
|
||||
|
||||
35
rclcpp_action/Doxyfile
Normal file
35
rclcpp_action/Doxyfile
Normal file
@@ -0,0 +1,35 @@
|
||||
# All settings not listed here will use the Doxygen default values.
|
||||
|
||||
PROJECT_NAME = "rclcpp_action"
|
||||
PROJECT_NUMBER = master
|
||||
PROJECT_BRIEF = "C++ ROS Action Client Library"
|
||||
|
||||
# Use these lines to include the generated logging.hpp (update install path if needed)
|
||||
#INPUT = ../../../../install_isolated/rclcpp/include
|
||||
#STRIP_FROM_PATH = /Users/william/ros2_ws/install_isolated/rclcpp/include
|
||||
# Otherwise just generate for the local (non-generated header files)
|
||||
INPUT = ./include
|
||||
|
||||
RECURSIVE = YES
|
||||
OUTPUT_DIRECTORY = doc_output
|
||||
|
||||
EXTRACT_ALL = YES
|
||||
SORT_MEMBER_DOCS = NO
|
||||
|
||||
GENERATE_LATEX = NO
|
||||
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = YES
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
PREDEFINED = RCLCPP_PUBLIC=
|
||||
|
||||
# Tag files that do not exist will produce a warning and cross-project linking will not work.
|
||||
TAGFILES += "../../../../doxygen_tag_files/cppreference-doxygen-web.tag.xml=http://en.cppreference.com/w/"
|
||||
# Consider changing "latest" to the version you want to reference (e.g. beta1 or 1.0.0)
|
||||
TAGFILES += "../../../../doxygen_tag_files/rclcpp.tag=http://docs.ros2.org/latest/api/rclcpp/"
|
||||
TAGFILES += "../../../../doxygen_tag_files/rcl_action.tag=http://docs.ros2.org/latest/api/rcl_action/"
|
||||
TAGFILES += "../../../../doxygen_tag_files/rcl.tag=http://docs.ros2.org/latest/api/rcl/"
|
||||
TAGFILES += "../../../../doxygen_tag_files/rmw.tag=http://docs.ros2.org/latest/api/rmw/"
|
||||
TAGFILES += "../../../../doxygen_tag_files/rcutils.tag=http://docs.ros2.org/latest/api/rcutils/"
|
||||
# Uncomment to generate tag files for cross-project linking.
|
||||
# GENERATE_TAGFILE = "../../../../doxygen_tag_files/rclcpp_action.tag"
|
||||
@@ -66,10 +66,10 @@ public:
|
||||
action_server_is_ready() const;
|
||||
|
||||
/// Wait for action_server_is_ready() to become true, or until the given timeout is reached.
|
||||
template<typename RatioT = std::milli>
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
bool
|
||||
wait_for_action_server(
|
||||
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_action_server_nanoseconds(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
@@ -148,7 +148,7 @@ protected:
|
||||
/// \internal
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
virtual
|
||||
GoalID
|
||||
GoalUUID
|
||||
generate_goal_id();
|
||||
|
||||
/// \internal
|
||||
@@ -245,7 +245,7 @@ private:
|
||||
/**
|
||||
* This class creates an action client.
|
||||
*
|
||||
* Create an instance of this server using `rclcpp_action::create_client()`.
|
||||
* To create an instance of an action client use `rclcpp_action::create_client()`.
|
||||
*
|
||||
* Internally, this class is responsible for:
|
||||
* - coverting between the C++ action type and generic types for `rclcpp_action::ClientBase`, and
|
||||
@@ -260,11 +260,23 @@ public:
|
||||
using Goal = typename ActionT::Goal;
|
||||
using Feedback = typename ActionT::Feedback;
|
||||
using GoalHandle = ClientGoalHandle<ActionT>;
|
||||
using Result = typename GoalHandle::Result;
|
||||
using WrappedResult = typename GoalHandle::WrappedResult;
|
||||
using FeedbackCallback = typename ClientGoalHandle<ActionT>::FeedbackCallback;
|
||||
using CancelRequest = typename ActionT::CancelGoalService::Request;
|
||||
using CancelResponse = typename ActionT::CancelGoalService::Response;
|
||||
using CancelRequest = typename ActionT::Impl::CancelGoalService::Request;
|
||||
using CancelResponse = typename ActionT::Impl::CancelGoalService::Response;
|
||||
|
||||
/// Construct an action client.
|
||||
/**
|
||||
* This constructs an action client, but it will not work until it has been added to a node.
|
||||
* Use `rclcpp_action::create_client()` to both construct and add to a node.
|
||||
*
|
||||
* \param[in] node_base A pointer to the base interface of a node.
|
||||
* \param[in] node_graph A pointer to an interface that allows getting graph information about
|
||||
* a node.
|
||||
* \param[in] node_logging A pointer to an interface that allows getting a node's logger.
|
||||
* \param[in] action_name The action name.
|
||||
* \param[in] client_options Options to pass to the underlying `rcl_action::rcl_action_client_t`.
|
||||
*/
|
||||
Client(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
|
||||
@@ -279,6 +291,20 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/// Send an action goal and asynchronously get the result.
|
||||
/**
|
||||
* If the goal is accepted by an action server, the returned future is set to a `ClientGoalHandle`.
|
||||
* If the goal is rejected by an action server, then the future is set to a `nullptr`.
|
||||
*
|
||||
* The goal handle is used to monitor the status of the goal and get the final result.
|
||||
*
|
||||
* \param[in] goal The goal request.
|
||||
* \param[in] callback Optional user callback for feedback associated with the goal.
|
||||
* \param[in] ignore_result If `true`, then the result for the goal will not be requested and
|
||||
* therefore inaccessible from the goal handle.
|
||||
* \return A future that completes when the goal has been accepted or rejected.
|
||||
* If the goal is rejected, then the result will be a `nullptr`.
|
||||
*/
|
||||
std::shared_future<typename GoalHandle::SharedPtr>
|
||||
async_send_goal(
|
||||
const Goal & goal, FeedbackCallback callback = nullptr, bool ignore_result = false)
|
||||
@@ -286,26 +312,23 @@ public:
|
||||
// Put promise in the heap to move it around.
|
||||
auto promise = std::make_shared<std::promise<typename GoalHandle::SharedPtr>>();
|
||||
std::shared_future<typename GoalHandle::SharedPtr> future(promise->get_future());
|
||||
using GoalRequest = typename ActionT::GoalRequestService::Request;
|
||||
// auto goal_request = std::make_shared<GoalRequest>();
|
||||
// goal_request->goal_id = this->generate_goal_id();
|
||||
// goal_request->goal = goal;
|
||||
auto goal_request = std::make_shared<GoalRequest>(goal);
|
||||
goal_request->action_goal_id.uuid = this->generate_goal_id();
|
||||
using GoalRequest = typename ActionT::Impl::SendGoalService::Request;
|
||||
auto goal_request = std::make_shared<GoalRequest>();
|
||||
goal_request->goal_id.uuid = this->generate_goal_id();
|
||||
goal_request->goal = goal;
|
||||
this->send_goal_request(
|
||||
std::static_pointer_cast<void>(goal_request),
|
||||
[this, goal_request, callback, ignore_result, promise](
|
||||
std::shared_ptr<void> response) mutable
|
||||
{
|
||||
using GoalResponse = typename ActionT::GoalRequestService::Response;
|
||||
using GoalResponse = typename ActionT::Impl::SendGoalService::Response;
|
||||
auto goal_response = std::static_pointer_cast<GoalResponse>(response);
|
||||
if (!goal_response->accepted) {
|
||||
promise->set_value(nullptr);
|
||||
return;
|
||||
}
|
||||
GoalInfo goal_info;
|
||||
// goal_info.goal_id = goal_request->goal_id;
|
||||
goal_info.goal_id.uuid = goal_request->action_goal_id.uuid;
|
||||
goal_info.goal_id.uuid = goal_request->goal_id.uuid;
|
||||
goal_info.stamp = goal_response->stamp;
|
||||
// Do not use std::make_shared as friendship cannot be forwarded.
|
||||
std::shared_ptr<GoalHandle> goal_handle(new GoalHandle(goal_info, callback));
|
||||
@@ -324,7 +347,14 @@ public:
|
||||
return future;
|
||||
}
|
||||
|
||||
std::shared_future<Result>
|
||||
/// Asynchronously get the result for an active goal.
|
||||
/**
|
||||
* \throws exceptions::UnknownGoalHandleError If the goal unknown or already reached a terminal
|
||||
* state.
|
||||
* \param[in] goal_handle The goal handle for which to get the result.
|
||||
* \return A future that is set to the goal result when the goal is finished.
|
||||
*/
|
||||
std::shared_future<WrappedResult>
|
||||
async_get_result(typename GoalHandle::SharedPtr goal_handle)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(goal_handles_mutex_);
|
||||
@@ -338,6 +368,13 @@ public:
|
||||
return goal_handle->async_result();
|
||||
}
|
||||
|
||||
/// Asynchronously request a goal be canceled.
|
||||
/**
|
||||
* \throws exceptions::UnknownGoalHandleError If the goal is unknown or already reached a
|
||||
* terminal state.
|
||||
* \param[in] goal_handle The goal handle requesting to be canceled.
|
||||
* \return A future whose result indicates whether or not the cancel request was accepted.
|
||||
*/
|
||||
std::shared_future<bool>
|
||||
async_cancel_goal(typename GoalHandle::SharedPtr goal_handle)
|
||||
{
|
||||
@@ -367,6 +404,14 @@ public:
|
||||
return future;
|
||||
}
|
||||
|
||||
/// Asynchronously request for all goals to be canceled.
|
||||
/**
|
||||
* \return A future to a CancelResponse message that is set when the request has been
|
||||
* acknowledged by an action server.
|
||||
* See
|
||||
* <a href="https://github.com/ros2/rcl_interfaces/blob/master/action_msgs/srv/CancelGoal.srv">
|
||||
* action_msgs/CancelGoal.srv</a>.
|
||||
*/
|
||||
std::shared_future<typename CancelResponse::SharedPtr>
|
||||
async_cancel_all_goals()
|
||||
{
|
||||
@@ -378,6 +423,15 @@ public:
|
||||
return async_cancel(cancel_request);
|
||||
}
|
||||
|
||||
/// Asynchronously request all goals at or before a specified time be canceled.
|
||||
/**
|
||||
* \param[in] stamp The timestamp for the cancel goal request.
|
||||
* \return A future to a CancelResponse message that is set when the request has been
|
||||
* acknowledged by an action server.
|
||||
* See
|
||||
* <a href="https://github.com/ros2/rcl_interfaces/blob/master/action_msgs/srv/CancelGoal.srv">
|
||||
* action_msgs/CancelGoal.srv</a>.
|
||||
*/
|
||||
std::shared_future<typename CancelResponse::SharedPtr>
|
||||
async_cancel_goals_before(const rclcpp::Time & stamp)
|
||||
{
|
||||
@@ -406,7 +460,7 @@ private:
|
||||
std::shared_ptr<void>
|
||||
create_goal_response() const override
|
||||
{
|
||||
using GoalResponse = typename ActionT::GoalRequestService::Response;
|
||||
using GoalResponse = typename ActionT::Impl::SendGoalService::Response;
|
||||
return std::shared_ptr<void>(new GoalResponse());
|
||||
}
|
||||
|
||||
@@ -414,7 +468,7 @@ private:
|
||||
std::shared_ptr<void>
|
||||
create_result_response() const override
|
||||
{
|
||||
using GoalResultResponse = typename ActionT::GoalResultService::Response;
|
||||
using GoalResultResponse = typename ActionT::Impl::GetResultService::Response;
|
||||
return std::shared_ptr<void>(new GoalResultResponse());
|
||||
}
|
||||
|
||||
@@ -429,9 +483,8 @@ private:
|
||||
std::shared_ptr<void>
|
||||
create_feedback_message() const override
|
||||
{
|
||||
// using FeedbackMessage = typename ActionT::FeedbackMessage;
|
||||
// return std::shared_ptr<void>(new FeedbackMessage());
|
||||
return std::shared_ptr<void>(new Feedback());
|
||||
using FeedbackMessage = typename ActionT::Impl::FeedbackMessage;
|
||||
return std::shared_ptr<void>(new FeedbackMessage());
|
||||
}
|
||||
|
||||
/// \internal
|
||||
@@ -439,13 +492,10 @@ private:
|
||||
handle_feedback_message(std::shared_ptr<void> message) override
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(goal_handles_mutex_);
|
||||
// using FeedbackMessage = typename ActionT::FeedbackMessage;
|
||||
// typename FeedbackMessage::SharedPtr feedback_message =
|
||||
// std::static_pointer_cast<FeedbackMessage>(message);
|
||||
typename Feedback::SharedPtr feedback_message =
|
||||
std::static_pointer_cast<Feedback>(message);
|
||||
// const GoalID & goal_id = feedback_message->goal_id;
|
||||
const GoalID & goal_id = feedback_message->action_goal_id.uuid;
|
||||
using FeedbackMessage = typename ActionT::Impl::FeedbackMessage;
|
||||
typename FeedbackMessage::SharedPtr feedback_message =
|
||||
std::static_pointer_cast<FeedbackMessage>(message);
|
||||
const GoalUUID & goal_id = feedback_message->goal_id.uuid;
|
||||
if (goal_handles_.count(goal_id) == 0) {
|
||||
RCLCPP_DEBUG(
|
||||
this->get_logger(),
|
||||
@@ -453,15 +503,16 @@ private:
|
||||
return;
|
||||
}
|
||||
typename GoalHandle::SharedPtr goal_handle = goal_handles_[goal_id];
|
||||
// goal_handle->call_feedback_callback(goal_handle, feedback_message->feedback);
|
||||
goal_handle->call_feedback_callback(goal_handle, feedback_message);
|
||||
auto feedback = std::make_shared<Feedback>();
|
||||
*feedback = feedback_message->feedback;
|
||||
goal_handle->call_feedback_callback(goal_handle, feedback);
|
||||
}
|
||||
|
||||
/// \internal
|
||||
std::shared_ptr<void>
|
||||
create_status_message() const override
|
||||
{
|
||||
using GoalStatusMessage = typename ActionT::GoalStatusMessage;
|
||||
using GoalStatusMessage = typename ActionT::Impl::GoalStatusMessage;
|
||||
return std::shared_ptr<void>(new GoalStatusMessage());
|
||||
}
|
||||
|
||||
@@ -470,11 +521,10 @@ private:
|
||||
handle_status_message(std::shared_ptr<void> message) override
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(goal_handles_mutex_);
|
||||
using GoalStatusMessage = typename ActionT::GoalStatusMessage;
|
||||
using GoalStatusMessage = typename ActionT::Impl::GoalStatusMessage;
|
||||
auto status_message = std::static_pointer_cast<GoalStatusMessage>(message);
|
||||
for (const GoalStatus & status : status_message->status_list) {
|
||||
// const GoalID & goal_id = status.goal_info.goal_id;
|
||||
const GoalID & goal_id = status.goal_info.goal_id.uuid;
|
||||
const GoalUUID & goal_id = status.goal_info.goal_id.uuid;
|
||||
if (goal_handles_.count(goal_id) == 0) {
|
||||
RCLCPP_DEBUG(
|
||||
this->get_logger(),
|
||||
@@ -498,21 +548,22 @@ private:
|
||||
void
|
||||
make_result_aware(typename GoalHandle::SharedPtr goal_handle)
|
||||
{
|
||||
using GoalResultRequest = typename ActionT::GoalResultService::Request;
|
||||
using GoalResultRequest = typename ActionT::Impl::GetResultService::Request;
|
||||
auto goal_result_request = std::make_shared<GoalResultRequest>();
|
||||
// goal_result_request.goal_id = goal_handle->get_goal_id();
|
||||
goal_result_request->action_goal_id.uuid = goal_handle->get_goal_id();
|
||||
goal_result_request->goal_id.uuid = goal_handle->get_goal_id();
|
||||
this->send_result_request(
|
||||
std::static_pointer_cast<void>(goal_result_request),
|
||||
[goal_handle, this](std::shared_ptr<void> response) mutable
|
||||
{
|
||||
// Wrap the response in a struct with the fields a user cares about
|
||||
Result result;
|
||||
using GoalResultResponse = typename ActionT::GoalResultService::Response;
|
||||
result.response = std::static_pointer_cast<GoalResultResponse>(response);
|
||||
result.goal_id = goal_handle->get_goal_id();
|
||||
result.code = static_cast<ResultCode>(result.response->action_status);
|
||||
goal_handle->set_result(result);
|
||||
WrappedResult wrapped_result;
|
||||
using GoalResultResponse = typename ActionT::Impl::GetResultService::Response;
|
||||
auto result_response = std::static_pointer_cast<GoalResultResponse>(response);
|
||||
wrapped_result.result = std::make_shared<typename ActionT::Result>();
|
||||
*wrapped_result.result = result_response->result;
|
||||
wrapped_result.goal_id = goal_handle->get_goal_id();
|
||||
wrapped_result.code = static_cast<ResultCode>(result_response->status);
|
||||
goal_handle->set_result(wrapped_result);
|
||||
std::lock_guard<std::mutex> lock(goal_handles_mutex_);
|
||||
goal_handles_.erase(goal_handle->get_goal_id());
|
||||
});
|
||||
@@ -536,7 +587,7 @@ private:
|
||||
return future;
|
||||
}
|
||||
|
||||
std::map<GoalID, typename GoalHandle::SharedPtr> goal_handles_;
|
||||
std::map<GoalUUID, typename GoalHandle::SharedPtr> goal_handles_;
|
||||
std::mutex goal_handles_mutex_;
|
||||
};
|
||||
} // namespace rclcpp_action
|
||||
|
||||
@@ -45,6 +45,15 @@ enum class ResultCode : int8_t
|
||||
template<typename ActionT>
|
||||
class Client;
|
||||
|
||||
/// Class for interacting with goals sent from action clients.
|
||||
/**
|
||||
* Use this class to check the status of a goal as well as get the result.
|
||||
*
|
||||
* This class is not meant to be created by a user, instead it is created when a goal has been
|
||||
* accepted.
|
||||
* A `Client` will create an instance and return it to the user (via a future) after calling
|
||||
* `Client::async_send_goal`.
|
||||
*/
|
||||
template<typename ActionT>
|
||||
class ClientGoalHandle
|
||||
{
|
||||
@@ -52,38 +61,54 @@ public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientGoalHandle)
|
||||
|
||||
// A wrapper that defines the result of an action
|
||||
typedef struct Result
|
||||
typedef struct WrappedResult
|
||||
{
|
||||
/// The unique identifier of the goal
|
||||
GoalID goal_id;
|
||||
GoalUUID goal_id;
|
||||
/// A status to indicate if the goal was canceled, aborted, or suceeded
|
||||
ResultCode code;
|
||||
/// User defined fields sent back with an action
|
||||
typename ActionT::Result::SharedPtr response;
|
||||
} Result;
|
||||
typename ActionT::Result::SharedPtr result;
|
||||
} WrappedResult;
|
||||
|
||||
using Feedback = typename ActionT::Feedback;
|
||||
using FeedbackCallback =
|
||||
std::function<void (
|
||||
typename ClientGoalHandle<ActionT>::SharedPtr, const std::shared_ptr<const Feedback>)>;
|
||||
typename ClientGoalHandle<ActionT>::SharedPtr,
|
||||
const std::shared_ptr<const Feedback>)>;
|
||||
|
||||
virtual ~ClientGoalHandle();
|
||||
|
||||
const GoalID &
|
||||
/// Get the unique ID for the goal.
|
||||
const GoalUUID &
|
||||
get_goal_id() const;
|
||||
|
||||
/// Get the time when the goal was accepted.
|
||||
rclcpp::Time
|
||||
get_goal_stamp() const;
|
||||
|
||||
std::shared_future<Result>
|
||||
/// Get a future to the goal result.
|
||||
/**
|
||||
* This method should not be called if the `ignore_result` flag was set when
|
||||
* sending the original goal request (see Client::async_send_goal).
|
||||
*
|
||||
* `is_result_aware()` can be used to check if it is safe to call this method.
|
||||
*
|
||||
* \throws exceptions::UnawareGoalHandleError If the the goal handle is unaware of the result.
|
||||
* \return A future to the result.
|
||||
*/
|
||||
std::shared_future<WrappedResult>
|
||||
async_result();
|
||||
|
||||
/// Get the goal status code.
|
||||
int8_t
|
||||
get_status();
|
||||
|
||||
/// Check if an action client has subscribed to feedback for the goal.
|
||||
bool
|
||||
is_feedback_aware();
|
||||
|
||||
/// Check if an action client has requested the result for the goal.
|
||||
bool
|
||||
is_result_aware();
|
||||
|
||||
@@ -108,7 +133,7 @@ private:
|
||||
set_status(int8_t status);
|
||||
|
||||
void
|
||||
set_result(const Result & result);
|
||||
set_result(const WrappedResult & wrapped_result);
|
||||
|
||||
void
|
||||
invalidate();
|
||||
@@ -116,8 +141,8 @@ private:
|
||||
GoalInfo info_;
|
||||
|
||||
bool is_result_aware_{false};
|
||||
std::promise<Result> result_promise_;
|
||||
std::shared_future<Result> result_future_;
|
||||
std::promise<WrappedResult> result_promise_;
|
||||
std::shared_future<WrappedResult> result_future_;
|
||||
|
||||
FeedbackCallback feedback_callback_{nullptr};
|
||||
int8_t status_{GoalStatus::STATUS_ACCEPTED};
|
||||
|
||||
@@ -39,7 +39,7 @@ ClientGoalHandle<ActionT>::~ClientGoalHandle()
|
||||
}
|
||||
|
||||
template<typename ActionT>
|
||||
const GoalID &
|
||||
const GoalUUID &
|
||||
ClientGoalHandle<ActionT>::get_goal_id() const
|
||||
{
|
||||
// return info_.goal_id;
|
||||
@@ -54,7 +54,7 @@ ClientGoalHandle<ActionT>::get_goal_stamp() const
|
||||
}
|
||||
|
||||
template<typename ActionT>
|
||||
std::shared_future<typename ClientGoalHandle<ActionT>::Result>
|
||||
std::shared_future<typename ClientGoalHandle<ActionT>::WrappedResult>
|
||||
ClientGoalHandle<ActionT>::async_result()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
@@ -66,11 +66,11 @@ ClientGoalHandle<ActionT>::async_result()
|
||||
|
||||
template<typename ActionT>
|
||||
void
|
||||
ClientGoalHandle<ActionT>::set_result(const Result & result)
|
||||
ClientGoalHandle<ActionT>::set_result(const WrappedResult & wrapped_result)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
status_ = static_cast<int8_t>(result.code);
|
||||
result_promise_.set_value(result);
|
||||
status_ = static_cast<int8_t>(wrapped_result.code);
|
||||
result_promise_.set_value(wrapped_result);
|
||||
}
|
||||
|
||||
template<typename ActionT>
|
||||
|
||||
@@ -25,6 +25,13 @@
|
||||
|
||||
namespace rclcpp_action
|
||||
{
|
||||
/// Create an action client.
|
||||
/**
|
||||
* \param[in] node The action client will be added to this node.
|
||||
* \param[in] name The action name.
|
||||
* \param[in] group The action client will be added to this callback group.
|
||||
* If `nullptr`, then the action client is added to the default callback group.
|
||||
*/
|
||||
template<typename ActionT>
|
||||
typename Client<ActionT>::SharedPtr
|
||||
create_client(
|
||||
|
||||
@@ -31,6 +31,23 @@
|
||||
|
||||
namespace rclcpp_action
|
||||
{
|
||||
/// Create an action server.
|
||||
/**
|
||||
* All provided callback functions must be non-blocking.
|
||||
*
|
||||
* \sa Server::Server() for more information.
|
||||
*
|
||||
* \param node[in] The action server will be added to this node.
|
||||
* \param name[in] The action name.
|
||||
* \param[in] handle_goal A callback that decides if a goal should be accepted or rejected.
|
||||
* \param[in] handle_cancel A callback that decides if a goal should be attempted to be canceled.
|
||||
* The return from this callback only indicates if the server will try to cancel a goal.
|
||||
* It does not indicate if the goal was actually canceled.
|
||||
* \param[in] handle_accepted A callback that is called to give the user a handle to the goal.
|
||||
* \param[in] options options to pass to the underlying `rcl_action_server_t`.
|
||||
* \param group[in] The action server will be added to this callback group.
|
||||
* If `nullptr`, then the action server is added to the default callback group.
|
||||
*/
|
||||
template<typename ActionT>
|
||||
typename Server<ActionT>::SharedPtr
|
||||
create_server(
|
||||
|
||||
@@ -17,9 +17,14 @@
|
||||
* `rclcpp_action` provides the canonical C++ API for interacting with ROS Actions.
|
||||
* It consists of these main components:
|
||||
*
|
||||
* - TODO(jacobperron): Finish docs
|
||||
* - Action Client
|
||||
* - rclcpp_action/client.hpp
|
||||
* - rclcpp_action/create_client.hpp
|
||||
* - rclcpp_action/client_goal_handle.hpp
|
||||
* - Action Server
|
||||
* - rclcpp_action/server.hpp
|
||||
* - rclcpp_action/create_server.hpp
|
||||
* - rclcpp_action/server_goal_handle.hpp
|
||||
*/
|
||||
|
||||
#ifndef RCLCPP_ACTION__RCLCPP_ACTION_HPP_
|
||||
|
||||
@@ -63,7 +63,7 @@ enum class CancelResponse : int8_t
|
||||
/// \internal
|
||||
/**
|
||||
* This class should not be used directly by users writing an action server.
|
||||
* Instead users should use `rclcpp_action::Server<>`.
|
||||
* Instead users should use `rclcpp_action::Server`.
|
||||
*
|
||||
* Internally, this class is responsible for interfacing with the `rcl_action` API.
|
||||
*/
|
||||
@@ -146,7 +146,7 @@ protected:
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
virtual
|
||||
std::pair<GoalResponse, std::shared_ptr<void>>
|
||||
call_handle_goal_callback(GoalID &, std::shared_ptr<void> request) = 0;
|
||||
call_handle_goal_callback(GoalUUID &, std::shared_ptr<void> request) = 0;
|
||||
|
||||
// ServerBase will determine which goal ids are being cancelled, and then call this function for
|
||||
// each goal id.
|
||||
@@ -155,13 +155,13 @@ protected:
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
virtual
|
||||
CancelResponse
|
||||
call_handle_cancel_callback(const GoalID & uuid) = 0;
|
||||
call_handle_cancel_callback(const GoalUUID & uuid) = 0;
|
||||
|
||||
/// Given a goal request message, return the UUID contained within.
|
||||
/// \internal
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
virtual
|
||||
GoalID
|
||||
GoalUUID
|
||||
get_goal_id_from_goal_request(void * message) = 0;
|
||||
|
||||
/// Create an empty goal request message so it can be taken from a lower layer.
|
||||
@@ -178,13 +178,13 @@ protected:
|
||||
void
|
||||
call_goal_accepted_callback(
|
||||
std::shared_ptr<rcl_action_goal_handle_t> rcl_goal_handle,
|
||||
GoalID uuid, std::shared_ptr<void> goal_request_message) = 0;
|
||||
GoalUUID uuid, std::shared_ptr<void> goal_request_message) = 0;
|
||||
|
||||
/// Given a result request message, return the UUID contained within.
|
||||
/// \internal
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
virtual
|
||||
GoalID
|
||||
GoalUUID
|
||||
get_goal_id_from_result_request(void * message) = 0;
|
||||
|
||||
/// Create an empty goal request message so it can be taken from a lower layer.
|
||||
@@ -214,7 +214,7 @@ protected:
|
||||
/// \internal
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
void
|
||||
publish_result(const GoalID & uuid, std::shared_ptr<void> result_msg);
|
||||
publish_result(const GoalUUID & uuid, std::shared_ptr<void> result_msg);
|
||||
|
||||
/// \internal
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
@@ -272,7 +272,7 @@ public:
|
||||
|
||||
/// Signature of a callback that accepts or rejects goal requests.
|
||||
using GoalCallback = std::function<GoalResponse(
|
||||
const GoalID &, std::shared_ptr<const typename ActionT::Goal>)>;
|
||||
const GoalUUID &, std::shared_ptr<const typename ActionT::Goal>)>;
|
||||
/// Signature of a callback that accepts or rejects requests to cancel a goal.
|
||||
using CancelCallback = std::function<CancelResponse(std::shared_ptr<ServerGoalHandle<ActionT>>)>;
|
||||
/// Signature of a callback that is used to notify when the goal has been accepted.
|
||||
@@ -288,7 +288,7 @@ public:
|
||||
* - one to accept or reject requests to cancel a goal,
|
||||
* - one to receive a goal handle after a goal has been accepted.
|
||||
* All callbacks must be non-blocking.
|
||||
* The result of a goal should be set using methods on `rclcpp_action::ServerGoalHandle<>`.
|
||||
* The result of a goal should be set using methods on `rclcpp_action::ServerGoalHandle`.
|
||||
*
|
||||
* \param[in] node_base a pointer to the base interface of a node.
|
||||
* \param[in] node_clock a pointer to an interface that allows getting a node's clock.
|
||||
@@ -335,16 +335,14 @@ protected:
|
||||
|
||||
/// \internal
|
||||
std::pair<GoalResponse, std::shared_ptr<void>>
|
||||
call_handle_goal_callback(GoalID & uuid, std::shared_ptr<void> message) override
|
||||
call_handle_goal_callback(GoalUUID & uuid, std::shared_ptr<void> message) override
|
||||
{
|
||||
// TODO(sloretz) update and remove assert when IDL pipeline allows nesting user's type
|
||||
static_assert(
|
||||
std::is_same<typename ActionT::Goal, typename ActionT::GoalRequestService::Request>::value,
|
||||
"Assuming user fields were merged with goal request fields");
|
||||
GoalResponse user_response = handle_goal_(
|
||||
uuid, std::static_pointer_cast<typename ActionT::Goal>(message));
|
||||
auto request = std::static_pointer_cast<
|
||||
typename ActionT::Impl::SendGoalService::Request>(message);
|
||||
auto goal = std::shared_ptr<typename ActionT::Goal>(request, &request->goal);
|
||||
GoalResponse user_response = handle_goal_(uuid, goal);
|
||||
|
||||
auto ros_response = std::make_shared<typename ActionT::GoalRequestService::Response>();
|
||||
auto ros_response = std::make_shared<typename ActionT::Impl::SendGoalService::Response>();
|
||||
ros_response->accepted = GoalResponse::ACCEPT_AND_EXECUTE == user_response ||
|
||||
GoalResponse::ACCEPT_AND_DEFER == user_response;
|
||||
return std::make_pair(user_response, ros_response);
|
||||
@@ -352,7 +350,7 @@ protected:
|
||||
|
||||
/// \internal
|
||||
CancelResponse
|
||||
call_handle_cancel_callback(const GoalID & uuid) override
|
||||
call_handle_cancel_callback(const GoalUUID & uuid) override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(goal_handles_mutex_);
|
||||
CancelResponse resp = CancelResponse::REJECT;
|
||||
@@ -373,13 +371,13 @@ protected:
|
||||
void
|
||||
call_goal_accepted_callback(
|
||||
std::shared_ptr<rcl_action_goal_handle_t> rcl_goal_handle,
|
||||
GoalID uuid, std::shared_ptr<void> goal_request_message) override
|
||||
GoalUUID uuid, std::shared_ptr<void> goal_request_message) override
|
||||
{
|
||||
std::shared_ptr<ServerGoalHandle<ActionT>> goal_handle;
|
||||
std::weak_ptr<Server<ActionT>> weak_this = this->shared_from_this();
|
||||
|
||||
std::function<void(const GoalID &, std::shared_ptr<void>)> on_terminal_state =
|
||||
[weak_this](const GoalID & uuid, std::shared_ptr<void> result_message)
|
||||
std::function<void(const GoalUUID &, std::shared_ptr<void>)> on_terminal_state =
|
||||
[weak_this](const GoalUUID & uuid, std::shared_ptr<void> result_message)
|
||||
{
|
||||
std::shared_ptr<Server<ActionT>> shared_this = weak_this.lock();
|
||||
if (!shared_this) {
|
||||
@@ -396,8 +394,8 @@ protected:
|
||||
shared_this->goal_handles_.erase(uuid);
|
||||
};
|
||||
|
||||
std::function<void(const GoalID &)> on_executing =
|
||||
[weak_this](const GoalID & uuid)
|
||||
std::function<void(const GoalUUID &)> on_executing =
|
||||
[weak_this](const GoalUUID & uuid)
|
||||
{
|
||||
std::shared_ptr<Server<ActionT>> shared_this = weak_this.lock();
|
||||
if (!shared_this) {
|
||||
@@ -408,8 +406,8 @@ protected:
|
||||
shared_this->publish_status();
|
||||
};
|
||||
|
||||
std::function<void(std::shared_ptr<typename ActionT::Feedback>)> publish_feedback =
|
||||
[weak_this](std::shared_ptr<typename ActionT::Feedback> feedback_msg)
|
||||
std::function<void(std::shared_ptr<typename ActionT::Impl::FeedbackMessage>)> publish_feedback =
|
||||
[weak_this](std::shared_ptr<typename ActionT::Impl::FeedbackMessage> feedback_msg)
|
||||
{
|
||||
std::shared_ptr<Server<ActionT>> shared_this = weak_this.lock();
|
||||
if (!shared_this) {
|
||||
@@ -418,11 +416,12 @@ protected:
|
||||
shared_this->publish_feedback(std::static_pointer_cast<void>(feedback_msg));
|
||||
};
|
||||
|
||||
auto request = std::static_pointer_cast<
|
||||
const typename ActionT::Impl::SendGoalService::Request>(goal_request_message);
|
||||
auto goal = std::shared_ptr<const typename ActionT::Goal>(request, &request->goal);
|
||||
goal_handle.reset(
|
||||
new ServerGoalHandle<ActionT>(
|
||||
rcl_goal_handle, uuid,
|
||||
std::static_pointer_cast<const typename ActionT::Goal>(goal_request_message),
|
||||
on_terminal_state, on_executing, publish_feedback));
|
||||
rcl_goal_handle, uuid, goal, on_terminal_state, on_executing, publish_feedback));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(goal_handles_mutex_);
|
||||
goal_handles_[uuid] = goal_handle;
|
||||
@@ -431,41 +430,41 @@ protected:
|
||||
}
|
||||
|
||||
/// \internal
|
||||
GoalID
|
||||
GoalUUID
|
||||
get_goal_id_from_goal_request(void * message) override
|
||||
{
|
||||
return
|
||||
static_cast<typename ActionT::GoalRequestService::Request *>(message)->action_goal_id.uuid;
|
||||
static_cast<typename ActionT::Impl::SendGoalService::Request *>(message)->goal_id.uuid;
|
||||
}
|
||||
|
||||
/// \internal
|
||||
std::shared_ptr<void>
|
||||
create_goal_request() override
|
||||
{
|
||||
return std::shared_ptr<void>(new typename ActionT::GoalRequestService::Request());
|
||||
return std::shared_ptr<void>(new typename ActionT::Impl::SendGoalService::Request());
|
||||
}
|
||||
|
||||
/// \internal
|
||||
GoalID
|
||||
GoalUUID
|
||||
get_goal_id_from_result_request(void * message) override
|
||||
{
|
||||
return
|
||||
static_cast<typename ActionT::GoalResultService::Request *>(message)->action_goal_id.uuid;
|
||||
static_cast<typename ActionT::Impl::GetResultService::Request *>(message)->goal_id.uuid;
|
||||
}
|
||||
|
||||
/// \internal
|
||||
std::shared_ptr<void>
|
||||
create_result_request() override
|
||||
{
|
||||
return std::shared_ptr<void>(new typename ActionT::GoalResultService::Request());
|
||||
return std::shared_ptr<void>(new typename ActionT::Impl::GetResultService::Request());
|
||||
}
|
||||
|
||||
/// \internal
|
||||
std::shared_ptr<void>
|
||||
create_result_response(decltype(action_msgs::msg::GoalStatus::status) status) override
|
||||
{
|
||||
auto result = std::make_shared<typename ActionT::GoalResultService::Response>();
|
||||
result->action_status = status;
|
||||
auto result = std::make_shared<typename ActionT::Impl::GetResultService::Response>();
|
||||
result->status = status;
|
||||
return std::static_pointer_cast<void>(result);
|
||||
}
|
||||
|
||||
@@ -480,7 +479,7 @@ private:
|
||||
using GoalHandleWeakPtr = std::weak_ptr<ServerGoalHandle<ActionT>>;
|
||||
/// A map of goal id to goal handle weak pointers.
|
||||
/// This is used to provide a goal handle to handle_cancel.
|
||||
std::unordered_map<GoalID, GoalHandleWeakPtr> goal_handles_;
|
||||
std::unordered_map<GoalUUID, GoalHandleWeakPtr> goal_handles_;
|
||||
std::mutex goal_handles_mutex_;
|
||||
};
|
||||
} // namespace rclcpp_action
|
||||
|
||||
@@ -123,10 +123,10 @@ class Server;
|
||||
/// Class to interact with goals on a server.
|
||||
/**
|
||||
* Use this class to check the status of a goal as well as set the result.
|
||||
*
|
||||
* This class is not meant to be created by a user, instead it is created when a goal has been
|
||||
* accepted.
|
||||
* The class `rclcpp_action::Server<>` will create an instance and give it to the user in their
|
||||
* `handle_accepted` callback.
|
||||
* A `Server` will create an instance and give it to the user in their `handle_accepted` callback.
|
||||
*
|
||||
* Internally, this class is responsible for coverting between the C++ action type and generic
|
||||
* types for `rclcpp_action::ServerGoalHandleBase`.
|
||||
@@ -138,26 +138,29 @@ public:
|
||||
/// Send an update about the progress of a goal.
|
||||
/**
|
||||
* This must only be called when the goal is executing.
|
||||
* If execution of a goal is deferred then `ServerGoalHandle<>::set_executing()` must be called
|
||||
* If execution of a goal is deferred then `ServerGoalHandle::set_executing()` must be called
|
||||
* first.
|
||||
* `std::runtime_error` is raised if the goal is in any state besides executing.
|
||||
*
|
||||
* \throws std::runtime_error If the goal is in any state besides executing.
|
||||
*
|
||||
* \param[in] feedback_msg the message to publish to clients.
|
||||
*/
|
||||
void
|
||||
publish_feedback(std::shared_ptr<typename ActionT::Feedback> feedback_msg)
|
||||
{
|
||||
feedback_msg->action_goal_id.uuid = uuid_;
|
||||
publish_feedback_(feedback_msg);
|
||||
auto feedback_message = std::make_shared<typename ActionT::Impl::FeedbackMessage>();
|
||||
feedback_message->goal_id.uuid = uuid_;
|
||||
feedback_message->feedback = *feedback_msg;
|
||||
publish_feedback_(feedback_message);
|
||||
}
|
||||
|
||||
// TODO(sloretz) which exception is raised?
|
||||
/// Indicate that a goal could not be reached and has been aborted.
|
||||
/**
|
||||
* Only call this if the goal was executing but cannot be completed.
|
||||
* This is a terminal state, no more methods should be called on a goal handle after this is
|
||||
* called.
|
||||
* An exception is raised if the goal is in any state besides executing.
|
||||
*
|
||||
* \throws rclcpp::exceptions::RCLError If the goal is in any state besides executing.
|
||||
*
|
||||
* \param[in] result_msg the final result to send to clients.
|
||||
*/
|
||||
@@ -165,16 +168,19 @@ public:
|
||||
set_aborted(typename ActionT::Result::SharedPtr result_msg)
|
||||
{
|
||||
_set_aborted();
|
||||
result_msg->action_status = action_msgs::msg::GoalStatus::STATUS_ABORTED;
|
||||
on_terminal_state_(uuid_, result_msg);
|
||||
auto response = std::make_shared<typename ActionT::Impl::GetResultService::Response>();
|
||||
response->status = action_msgs::msg::GoalStatus::STATUS_ABORTED;
|
||||
response->result = *result_msg;
|
||||
on_terminal_state_(uuid_, response);
|
||||
}
|
||||
|
||||
/// Indicate that a goal has been reached.
|
||||
/// Indicate that a goal has succeeded.
|
||||
/**
|
||||
* Only call this if the goal is executing and has reached the desired final state.
|
||||
* This is a terminal state, no more methods should be called on a goal handle after this is
|
||||
* called.
|
||||
* An exception is raised if the goal is in any state besides executing.
|
||||
*
|
||||
* \throws rclcpp::exceptions::RCLError If the goal is in any state besides executing.
|
||||
*
|
||||
* \param[in] result_msg the final result to send to clients.
|
||||
*/
|
||||
@@ -182,8 +188,10 @@ public:
|
||||
set_succeeded(typename ActionT::Result::SharedPtr result_msg)
|
||||
{
|
||||
_set_succeeded();
|
||||
result_msg->action_status = action_msgs::msg::GoalStatus::STATUS_SUCCEEDED;
|
||||
on_terminal_state_(uuid_, result_msg);
|
||||
auto response = std::make_shared<typename ActionT::Impl::GetResultService::Response>();
|
||||
response->status = action_msgs::msg::GoalStatus::STATUS_SUCCEEDED;
|
||||
response->result = *result_msg;
|
||||
on_terminal_state_(uuid_, response);
|
||||
}
|
||||
|
||||
/// Indicate that a goal has been canceled.
|
||||
@@ -191,7 +199,8 @@ public:
|
||||
* Only call this if the goal is executing or pending, but has been canceled.
|
||||
* This is a terminal state, no more methods should be called on a goal handle after this is
|
||||
* called.
|
||||
* An exception is raised if the goal is in any state besides executing or pending.
|
||||
*
|
||||
* \throws rclcpp::exceptions::RCLError If the goal is in any state besides executing.
|
||||
*
|
||||
* \param[in] result_msg the final result to send to clients.
|
||||
*/
|
||||
@@ -199,14 +208,17 @@ public:
|
||||
set_canceled(typename ActionT::Result::SharedPtr result_msg)
|
||||
{
|
||||
_set_canceled();
|
||||
result_msg->action_status = action_msgs::msg::GoalStatus::STATUS_CANCELED;
|
||||
on_terminal_state_(uuid_, result_msg);
|
||||
auto response = std::make_shared<typename ActionT::Impl::GetResultService::Response>();
|
||||
response->status = action_msgs::msg::GoalStatus::STATUS_CANCELED;
|
||||
response->result = *result_msg;
|
||||
on_terminal_state_(uuid_, response);
|
||||
}
|
||||
|
||||
/// Indicate that the server is starting to execute a goal.
|
||||
/**
|
||||
* Only call this if the goal is pending.
|
||||
* An exception is raised if the goal is in any state besides pending.
|
||||
*
|
||||
* \throws rclcpp::exceptions::RCLError If the goal is in any state besides executing.
|
||||
*/
|
||||
void
|
||||
set_executing()
|
||||
@@ -215,7 +227,7 @@ public:
|
||||
on_executing_(uuid_);
|
||||
}
|
||||
|
||||
/// Get the original request message describing the goal.
|
||||
/// Get the user provided message describing the goal.
|
||||
const std::shared_ptr<const typename ActionT::Goal>
|
||||
get_goal() const
|
||||
{
|
||||
@@ -223,7 +235,7 @@ public:
|
||||
}
|
||||
|
||||
/// Get the unique identifier of the goal
|
||||
const GoalID &
|
||||
const GoalUUID &
|
||||
get_goal_id() const
|
||||
{
|
||||
return uuid_;
|
||||
@@ -233,8 +245,8 @@ public:
|
||||
{
|
||||
// Cancel goal if handle was allowed to destruct without reaching a terminal state
|
||||
if (try_canceling()) {
|
||||
auto null_result = std::make_shared<typename ActionT::Result>();
|
||||
null_result->action_status = action_msgs::msg::GoalStatus::STATUS_CANCELED;
|
||||
auto null_result = std::make_shared<typename ActionT::Impl::GetResultService::Response>();
|
||||
null_result->status = action_msgs::msg::GoalStatus::STATUS_CANCELED;
|
||||
on_terminal_state_(uuid_, null_result);
|
||||
}
|
||||
}
|
||||
@@ -243,11 +255,11 @@ protected:
|
||||
/// \internal
|
||||
ServerGoalHandle(
|
||||
std::shared_ptr<rcl_action_goal_handle_t> rcl_handle,
|
||||
GoalID uuid,
|
||||
GoalUUID uuid,
|
||||
std::shared_ptr<const typename ActionT::Goal> goal,
|
||||
std::function<void(const GoalID &, std::shared_ptr<void>)> on_terminal_state,
|
||||
std::function<void(const GoalID &)> on_executing,
|
||||
std::function<void(std::shared_ptr<typename ActionT::Feedback>)> publish_feedback
|
||||
std::function<void(const GoalUUID &, std::shared_ptr<void>)> on_terminal_state,
|
||||
std::function<void(const GoalUUID &)> on_executing,
|
||||
std::function<void(std::shared_ptr<typename ActionT::Impl::FeedbackMessage>)> publish_feedback
|
||||
)
|
||||
: ServerGoalHandleBase(rcl_handle), goal_(goal), uuid_(uuid),
|
||||
on_terminal_state_(on_terminal_state), on_executing_(on_executing),
|
||||
@@ -255,17 +267,17 @@ protected:
|
||||
{
|
||||
}
|
||||
|
||||
/// The original request message describing the goal.
|
||||
/// The user provided message describing the goal.
|
||||
const std::shared_ptr<const typename ActionT::Goal> goal_;
|
||||
|
||||
/// A unique id for the goal request.
|
||||
const GoalID uuid_;
|
||||
const GoalUUID uuid_;
|
||||
|
||||
friend Server<ActionT>;
|
||||
|
||||
std::function<void(const GoalID &, std::shared_ptr<void>)> on_terminal_state_;
|
||||
std::function<void(const GoalID &)> on_executing_;
|
||||
std::function<void(std::shared_ptr<typename ActionT::Feedback>)> publish_feedback_;
|
||||
std::function<void(const GoalUUID &, std::shared_ptr<void>)> on_terminal_state_;
|
||||
std::function<void(const GoalUUID &)> on_executing_;
|
||||
std::function<void(std::shared_ptr<typename ActionT::Impl::FeedbackMessage>)> publish_feedback_;
|
||||
};
|
||||
} // namespace rclcpp_action
|
||||
|
||||
|
||||
@@ -29,34 +29,34 @@
|
||||
namespace rclcpp_action
|
||||
{
|
||||
|
||||
using GoalID = std::array<uint8_t, UUID_SIZE>;
|
||||
using GoalUUID = std::array<uint8_t, UUID_SIZE>;
|
||||
using GoalStatus = action_msgs::msg::GoalStatus;
|
||||
using GoalInfo = action_msgs::msg::GoalInfo;
|
||||
|
||||
/// Convert a goal id to a human readable string.
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
std::string
|
||||
to_string(const GoalID & goal_id);
|
||||
to_string(const GoalUUID & goal_id);
|
||||
|
||||
// Convert C++ GoalID to rcl_action_goal_info_t
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
void
|
||||
convert(const GoalID & goal_id, rcl_action_goal_info_t * info);
|
||||
convert(const GoalUUID & goal_id, rcl_action_goal_info_t * info);
|
||||
|
||||
// Convert rcl_action_goal_info_t to C++ GoalID
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
void
|
||||
convert(const rcl_action_goal_info_t & info, GoalID * goal_id);
|
||||
convert(const rcl_action_goal_info_t & info, GoalUUID * goal_id);
|
||||
} // namespace rclcpp_action
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct less<rclcpp_action::GoalID>
|
||||
struct less<rclcpp_action::GoalUUID>
|
||||
{
|
||||
bool operator()(
|
||||
const rclcpp_action::GoalID & lhs,
|
||||
const rclcpp_action::GoalID & rhs) const
|
||||
const rclcpp_action::GoalUUID & lhs,
|
||||
const rclcpp_action::GoalUUID & rhs) const
|
||||
{
|
||||
return lhs < rhs;
|
||||
}
|
||||
@@ -64,9 +64,9 @@ struct less<rclcpp_action::GoalID>
|
||||
|
||||
/// Hash a goal id so it can be used as a key in std::unordered_map
|
||||
template<>
|
||||
struct hash<rclcpp_action::GoalID>
|
||||
struct hash<rclcpp_action::GoalUUID>
|
||||
{
|
||||
size_t operator()(const rclcpp_action::GoalID & uuid) const noexcept
|
||||
size_t operator()(const rclcpp_action::GoalUUID & uuid) const noexcept
|
||||
{
|
||||
// TODO(sloretz) Use someone else's hash function and cite it
|
||||
size_t result = 0;
|
||||
|
||||
@@ -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_action</name>
|
||||
<version>0.6.1</version>
|
||||
<version>0.7.0</version>
|
||||
<description>Adds action APIs for C++.</description>
|
||||
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
|
||||
<license>Apache License 2.0</license>
|
||||
|
||||
@@ -366,10 +366,10 @@ ClientBase::send_cancel_request(std::shared_ptr<void> request, ResponseCallback
|
||||
pimpl_->pending_cancel_responses[sequence_number] = callback;
|
||||
}
|
||||
|
||||
GoalID
|
||||
GoalUUID
|
||||
ClientBase::generate_goal_id()
|
||||
{
|
||||
GoalID goal_id;
|
||||
GoalUUID goal_id;
|
||||
// TODO(hidmic): Do something better than this for UUID generation.
|
||||
// std::generate(
|
||||
// goal_id.uuid.begin(), goal_id.uuid.end(),
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <vector>
|
||||
|
||||
using rclcpp_action::ServerBase;
|
||||
using rclcpp_action::GoalID;
|
||||
using rclcpp_action::GoalUUID;
|
||||
|
||||
namespace rclcpp_action
|
||||
{
|
||||
@@ -62,11 +62,11 @@ public:
|
||||
bool goal_expired_ = false;
|
||||
|
||||
// Results to be kept until the goal expires after reaching a terminal state
|
||||
std::unordered_map<GoalID, std::shared_ptr<void>> goal_results_;
|
||||
std::unordered_map<GoalUUID, std::shared_ptr<void>> goal_results_;
|
||||
// Requests for results are kept until a result becomes available
|
||||
std::unordered_map<GoalID, std::vector<rmw_request_id_t>> result_requests_;
|
||||
std::unordered_map<GoalUUID, std::vector<rmw_request_id_t>> result_requests_;
|
||||
// rcl goal handles are kept so api to send result doesn't try to access freed memory
|
||||
std::unordered_map<GoalID, std::shared_ptr<rcl_action_goal_handle_t>> goal_handles_;
|
||||
std::unordered_map<GoalUUID, std::shared_ptr<rcl_action_goal_handle_t>> goal_handles_;
|
||||
|
||||
rclcpp::Logger logger_;
|
||||
};
|
||||
@@ -228,7 +228,7 @@ ServerBase::execute_goal_request_received()
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
|
||||
GoalID uuid = get_goal_id_from_goal_request(message.get());
|
||||
GoalUUID uuid = get_goal_id_from_goal_request(message.get());
|
||||
convert(uuid, &goal_info);
|
||||
|
||||
// Call user's callback, getting the user's response and a ros message to send back
|
||||
@@ -339,7 +339,7 @@ ServerBase::execute_cancel_request_received()
|
||||
// For each canceled goal, call cancel callback
|
||||
for (size_t i = 0; i < goals.size; ++i) {
|
||||
const rcl_action_goal_info_t & goal_info = goals.data[i];
|
||||
GoalID uuid;
|
||||
GoalUUID uuid;
|
||||
convert(goal_info, &uuid);
|
||||
auto response_code = call_handle_cancel_callback(uuid);
|
||||
if (CancelResponse::ACCEPT == response_code) {
|
||||
@@ -388,7 +388,7 @@ ServerBase::execute_result_request_received()
|
||||
std::shared_ptr<void> result_response;
|
||||
|
||||
// check if the goal exists
|
||||
GoalID uuid = get_goal_id_from_result_request(result_request.get());
|
||||
GoalUUID uuid = get_goal_id_from_result_request(result_request.get());
|
||||
rcl_action_goal_info_t goal_info;
|
||||
convert(uuid, &goal_info);
|
||||
bool goal_exists;
|
||||
@@ -433,7 +433,7 @@ ServerBase::execute_check_expired_goals()
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
} else if (num_expired) {
|
||||
// A goal expired!
|
||||
GoalID uuid;
|
||||
GoalUUID uuid;
|
||||
convert(expired_goals[0], &uuid);
|
||||
RCLCPP_DEBUG(pimpl_->logger_, "Expired goal %s", to_string(uuid).c_str());
|
||||
pimpl_->goal_results_.erase(uuid);
|
||||
@@ -497,7 +497,7 @@ ServerBase::publish_status()
|
||||
}
|
||||
|
||||
void
|
||||
ServerBase::publish_result(const GoalID & uuid, std::shared_ptr<void> result_msg)
|
||||
ServerBase::publish_result(const GoalUUID & uuid, std::shared_ptr<void> result_msg)
|
||||
{
|
||||
// Check that the goal exists
|
||||
rcl_action_goal_info_t goal_info;
|
||||
|
||||
@@ -111,30 +111,19 @@ bool
|
||||
ServerGoalHandleBase::try_canceling() noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(rcl_handle_mutex_);
|
||||
// Check if the goal reached a terminal state already
|
||||
const bool active = rcl_action_goal_handle_is_active(rcl_handle_.get());
|
||||
if (!active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rcl_ret_t ret;
|
||||
|
||||
// Get the current state
|
||||
rcl_action_goal_state_t state = GOAL_STATE_UNKNOWN;
|
||||
ret = rcl_action_goal_handle_get_status(rcl_handle_.get(), &state);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If it's not already canceling then transition to that state
|
||||
if (GOAL_STATE_CANCELING != state) {
|
||||
// Check if the goal is cancelable
|
||||
const bool is_cancelable = rcl_action_goal_handle_is_cancelable(rcl_handle_.get());
|
||||
if (is_cancelable) {
|
||||
// Transition to CANCELING
|
||||
ret = rcl_action_update_goal_state(rcl_handle_.get(), GOAL_EVENT_CANCEL);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the state again
|
||||
rcl_action_goal_state_t state = GOAL_STATE_UNKNOWN;
|
||||
// Get the current state
|
||||
ret = rcl_action_goal_handle_get_status(rcl_handle_.get(), &state);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return false;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
namespace rclcpp_action
|
||||
{
|
||||
std::string
|
||||
to_string(const GoalID & goal_id)
|
||||
to_string(const GoalUUID & goal_id)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << std::hex;
|
||||
@@ -31,7 +31,7 @@ to_string(const GoalID & goal_id)
|
||||
}
|
||||
|
||||
void
|
||||
convert(const GoalID & goal_id, rcl_action_goal_info_t * info)
|
||||
convert(const GoalUUID & goal_id, rcl_action_goal_info_t * info)
|
||||
{
|
||||
for (size_t i = 0; i < 16; ++i) {
|
||||
info->goal_id.uuid[i] = goal_id[i];
|
||||
@@ -39,7 +39,7 @@ convert(const GoalID & goal_id, rcl_action_goal_info_t * info)
|
||||
}
|
||||
|
||||
void
|
||||
convert(const rcl_action_goal_info_t & info, GoalID * goal_id)
|
||||
convert(const rcl_action_goal_info_t & info, GoalUUID * goal_id)
|
||||
{
|
||||
for (size_t i = 0; i < 16; ++i) {
|
||||
(*goal_id)[i] = info.goal_id.uuid[i];
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <rclcpp/clock.hpp>
|
||||
#include <rclcpp/exceptions.hpp>
|
||||
#include <rclcpp/executors.hpp>
|
||||
#include <rclcpp/executors/multi_threaded_executor.hpp>
|
||||
#include <rclcpp/node.hpp>
|
||||
#include <rclcpp/publisher.hpp>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
@@ -47,23 +46,25 @@
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
const auto WAIT_FOR_SERVER_TIMEOUT = 10s;
|
||||
|
||||
class TestClient : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
using ActionType = test_msgs::action::Fibonacci;
|
||||
using ActionGoal = ActionType::Goal;
|
||||
using ActionGoalHandle = rclcpp_action::ClientGoalHandle<ActionType>;
|
||||
using ActionGoalRequestService = ActionType::GoalRequestService;
|
||||
using ActionGoalRequestService = ActionType::Impl::SendGoalService;
|
||||
using ActionGoalRequest = ActionGoalRequestService::Request;
|
||||
using ActionGoalResponse = ActionGoalRequestService::Response;
|
||||
using ActionGoalResultService = ActionType::GoalResultService;
|
||||
using ActionGoalResultService = ActionType::Impl::GetResultService;
|
||||
using ActionGoalResultRequest = ActionGoalResultService::Request;
|
||||
using ActionGoalResultResponse = ActionGoalResultService::Response;
|
||||
using ActionCancelGoalService = ActionType::CancelGoalService;
|
||||
using ActionCancelGoalRequest = ActionType::CancelGoalService::Request;
|
||||
using ActionCancelGoalResponse = ActionType::CancelGoalService::Response;
|
||||
using ActionStatusMessage = ActionType::GoalStatusMessage;
|
||||
using ActionFeedbackMessage = ActionType::Feedback;
|
||||
using ActionCancelGoalService = ActionType::Impl::CancelGoalService;
|
||||
using ActionCancelGoalRequest = ActionType::Impl::CancelGoalService::Request;
|
||||
using ActionCancelGoalResponse = ActionType::Impl::CancelGoalService::Response;
|
||||
using ActionStatusMessage = ActionType::Impl::GoalStatusMessage;
|
||||
using ActionFeedbackMessage = ActionType::Impl::FeedbackMessage;
|
||||
using ActionFeedback = ActionType::Feedback;
|
||||
|
||||
static void SetUpTestCase()
|
||||
@@ -88,9 +89,9 @@ protected:
|
||||
ActionGoalResponse::SharedPtr response)
|
||||
{
|
||||
response->stamp = clock.now();
|
||||
response->accepted = (request->order >= 0);
|
||||
response->accepted = (request->goal.order >= 0);
|
||||
if (response->accepted) {
|
||||
goals[request->action_goal_id.uuid] = {request, response};
|
||||
goals[request->goal_id.uuid] = {request, response};
|
||||
}
|
||||
});
|
||||
ASSERT_TRUE(goal_service != nullptr);
|
||||
@@ -106,29 +107,30 @@ protected:
|
||||
const ActionGoalResultRequest::SharedPtr request,
|
||||
ActionGoalResultResponse::SharedPtr response)
|
||||
{
|
||||
if (goals.count(request->action_goal_id.uuid) == 1) {
|
||||
auto goal_request = goals[request->action_goal_id.uuid].first;
|
||||
auto goal_response = goals[request->action_goal_id.uuid].second;
|
||||
if (goals.count(request->goal_id.uuid) == 1) {
|
||||
auto goal_request = goals[request->goal_id.uuid].first;
|
||||
auto goal_response = goals[request->goal_id.uuid].second;
|
||||
ActionStatusMessage status_message;
|
||||
rclcpp_action::GoalStatus goal_status;
|
||||
goal_status.goal_info.goal_id.uuid = goal_request->action_goal_id.uuid;
|
||||
goal_status.goal_info.goal_id.uuid = goal_request->goal_id.uuid;
|
||||
goal_status.goal_info.stamp = goal_response->stamp;
|
||||
goal_status.status = rclcpp_action::GoalStatus::STATUS_EXECUTING;
|
||||
status_message.status_list.push_back(goal_status);
|
||||
status_publisher->publish(status_message);
|
||||
client_executor.spin_once();
|
||||
ActionFeedbackMessage feedback_message;
|
||||
feedback_message.action_goal_id.uuid = goal_request->action_goal_id.uuid;
|
||||
feedback_message.sequence.push_back(0);
|
||||
feedback_message.goal_id.uuid = goal_request->goal_id.uuid;
|
||||
feedback_message.feedback.sequence.push_back(0);
|
||||
feedback_publisher->publish(feedback_message);
|
||||
client_executor.spin_once();
|
||||
if (goal_request->order > 0) {
|
||||
feedback_message.sequence.push_back(1);
|
||||
if (goal_request->goal.order > 0) {
|
||||
feedback_message.feedback.sequence.push_back(1);
|
||||
feedback_publisher->publish(feedback_message);
|
||||
client_executor.spin_once();
|
||||
for (int i = 1; i < goal_request->order; ++i) {
|
||||
feedback_message.sequence.push_back(
|
||||
feedback_message.sequence[i] + feedback_message.sequence[i - 1]);
|
||||
for (int i = 1; i < goal_request->goal.order; ++i) {
|
||||
feedback_message.feedback.sequence.push_back(
|
||||
feedback_message.feedback.sequence[i] +
|
||||
feedback_message.feedback.sequence[i - 1]);
|
||||
feedback_publisher->publish(feedback_message);
|
||||
client_executor.spin_once();
|
||||
}
|
||||
@@ -137,11 +139,11 @@ protected:
|
||||
status_message.status_list[0] = goal_status;
|
||||
status_publisher->publish(status_message);
|
||||
client_executor.spin_once();
|
||||
response->sequence = feedback_message.sequence;
|
||||
response->action_status = rclcpp_action::GoalStatus::STATUS_SUCCEEDED;
|
||||
goals.erase(request->action_goal_id.uuid);
|
||||
response->result.sequence = feedback_message.feedback.sequence;
|
||||
response->status = rclcpp_action::GoalStatus::STATUS_SUCCEEDED;
|
||||
goals.erase(request->goal_id.uuid);
|
||||
} else {
|
||||
response->action_status = rclcpp_action::GoalStatus::STATUS_UNKNOWN;
|
||||
response->status = rclcpp_action::GoalStatus::STATUS_UNKNOWN;
|
||||
}
|
||||
});
|
||||
ASSERT_TRUE(result_service != nullptr);
|
||||
@@ -157,7 +159,7 @@ protected:
|
||||
const ActionCancelGoalRequest::SharedPtr request,
|
||||
ActionCancelGoalResponse::SharedPtr response)
|
||||
{
|
||||
rclcpp_action::GoalID zero_uuid;
|
||||
rclcpp_action::GoalUUID zero_uuid;
|
||||
std::fill(zero_uuid.begin(), zero_uuid.end(), 0u);
|
||||
const rclcpp::Time cancel_stamp = request->goal_info.stamp;
|
||||
bool cancel_all = (
|
||||
@@ -170,11 +172,11 @@ protected:
|
||||
auto goal_response = it->second.second;
|
||||
const rclcpp::Time goal_stamp = goal_response->stamp;
|
||||
bool cancel_this = (
|
||||
request->goal_info.goal_id.uuid == goal_request->action_goal_id.uuid ||
|
||||
request->goal_info.goal_id.uuid == goal_request->goal_id.uuid ||
|
||||
cancel_stamp > goal_stamp);
|
||||
if (cancel_all || cancel_this) {
|
||||
rclcpp_action::GoalStatus goal_status;
|
||||
goal_status.goal_info.goal_id.uuid = goal_request->action_goal_id.uuid;
|
||||
goal_status.goal_info.goal_id.uuid = goal_request->goal_id.uuid;
|
||||
goal_status.goal_info.stamp = goal_response->stamp;
|
||||
goal_status.status = rclcpp_action::GoalStatus::STATUS_CANCELED;
|
||||
status_message.status_list.push_back(goal_status);
|
||||
@@ -250,7 +252,7 @@ protected:
|
||||
const char * const action_name{"fibonacci_test"};
|
||||
|
||||
std::map<
|
||||
rclcpp_action::GoalID,
|
||||
rclcpp_action::GoalUUID,
|
||||
std::pair<
|
||||
typename ActionGoalRequest::SharedPtr,
|
||||
typename ActionGoalResponse::SharedPtr>> goals;
|
||||
@@ -269,6 +271,7 @@ TEST_F(TestClient, construction_and_destruction)
|
||||
TEST_F(TestClient, async_send_goal_but_ignore_feedback_and_result)
|
||||
{
|
||||
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
|
||||
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
|
||||
|
||||
ActionGoal bad_goal;
|
||||
bad_goal.order = -5;
|
||||
@@ -290,6 +293,7 @@ TEST_F(TestClient, async_send_goal_but_ignore_feedback_and_result)
|
||||
TEST_F(TestClient, async_send_goal_and_ignore_feedback_but_wait_for_result)
|
||||
{
|
||||
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
|
||||
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
|
||||
|
||||
ActionGoal goal;
|
||||
goal.order = 5;
|
||||
@@ -301,16 +305,17 @@ TEST_F(TestClient, async_send_goal_and_ignore_feedback_but_wait_for_result)
|
||||
EXPECT_TRUE(goal_handle->is_result_aware());
|
||||
auto future_result = goal_handle->async_result();
|
||||
dual_spin_until_future_complete(future_result);
|
||||
auto result = future_result.get();
|
||||
ASSERT_EQ(6ul, result.response->sequence.size());
|
||||
EXPECT_EQ(0, result.response->sequence[0]);
|
||||
EXPECT_EQ(1, result.response->sequence[1]);
|
||||
EXPECT_EQ(5, result.response->sequence[5]);
|
||||
auto wrapped_result = future_result.get();
|
||||
ASSERT_EQ(6ul, wrapped_result.result->sequence.size());
|
||||
EXPECT_EQ(0, wrapped_result.result->sequence[0]);
|
||||
EXPECT_EQ(1, wrapped_result.result->sequence[1]);
|
||||
EXPECT_EQ(5, wrapped_result.result->sequence[5]);
|
||||
}
|
||||
|
||||
TEST_F(TestClient, async_send_goal_with_feedback_and_result)
|
||||
{
|
||||
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
|
||||
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
|
||||
|
||||
ActionGoal goal;
|
||||
goal.order = 4;
|
||||
@@ -332,16 +337,17 @@ TEST_F(TestClient, async_send_goal_with_feedback_and_result)
|
||||
EXPECT_TRUE(goal_handle->is_result_aware());
|
||||
auto future_result = goal_handle->async_result();
|
||||
dual_spin_until_future_complete(future_result);
|
||||
auto result = future_result.get();
|
||||
auto wrapped_result = future_result.get();
|
||||
|
||||
ASSERT_EQ(5u, result.response->sequence.size());
|
||||
EXPECT_EQ(3, result.response->sequence.back());
|
||||
ASSERT_EQ(5u, wrapped_result.result->sequence.size());
|
||||
EXPECT_EQ(3, wrapped_result.result->sequence.back());
|
||||
EXPECT_EQ(5, feedback_count);
|
||||
}
|
||||
|
||||
TEST_F(TestClient, async_cancel_one_goal)
|
||||
{
|
||||
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
|
||||
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
|
||||
|
||||
ActionGoal goal;
|
||||
goal.order = 5;
|
||||
@@ -359,6 +365,7 @@ TEST_F(TestClient, async_cancel_one_goal)
|
||||
TEST_F(TestClient, async_cancel_all_goals)
|
||||
{
|
||||
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
|
||||
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
|
||||
|
||||
ActionGoal goal;
|
||||
goal.order = 6;
|
||||
@@ -393,6 +400,7 @@ TEST_F(TestClient, async_cancel_all_goals)
|
||||
TEST_F(TestClient, async_cancel_some_goals)
|
||||
{
|
||||
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
|
||||
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
|
||||
|
||||
ActionGoal goal;
|
||||
goal.order = 6;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "rclcpp_action/server.hpp"
|
||||
|
||||
using Fibonacci = test_msgs::action::Fibonacci;
|
||||
using GoalID = rclcpp_action::GoalID;
|
||||
using GoalUUID = rclcpp_action::GoalUUID;
|
||||
|
||||
class TestServer : public ::testing::Test
|
||||
{
|
||||
@@ -36,16 +36,16 @@ protected:
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<Fibonacci::GoalRequestService::Request>
|
||||
send_goal_request(rclcpp::Node::SharedPtr node, GoalID uuid)
|
||||
std::shared_ptr<Fibonacci::Impl::SendGoalService::Request>
|
||||
send_goal_request(rclcpp::Node::SharedPtr node, GoalUUID uuid)
|
||||
{
|
||||
auto client = node->create_client<Fibonacci::GoalRequestService>(
|
||||
auto client = node->create_client<Fibonacci::Impl::SendGoalService>(
|
||||
"fibonacci/_action/send_goal");
|
||||
if (!client->wait_for_service(std::chrono::seconds(20))) {
|
||||
throw std::runtime_error("send goal service didn't become available");
|
||||
}
|
||||
auto request = std::make_shared<Fibonacci::GoalRequestService::Request>();
|
||||
request->action_goal_id.uuid = uuid;
|
||||
auto request = std::make_shared<Fibonacci::Impl::SendGoalService::Request>();
|
||||
request->goal_id.uuid = uuid;
|
||||
auto future = client->async_send_request(request);
|
||||
if (rclcpp::executor::FutureReturnCode::SUCCESS !=
|
||||
rclcpp::spin_until_future_complete(node, future))
|
||||
@@ -56,14 +56,14 @@ protected:
|
||||
}
|
||||
|
||||
void
|
||||
send_cancel_request(rclcpp::Node::SharedPtr node, GoalID uuid)
|
||||
send_cancel_request(rclcpp::Node::SharedPtr node, GoalUUID uuid)
|
||||
{
|
||||
auto cancel_client = node->create_client<Fibonacci::CancelGoalService>(
|
||||
auto cancel_client = node->create_client<Fibonacci::Impl::CancelGoalService>(
|
||||
"fibonacci/_action/cancel_goal");
|
||||
if (!cancel_client->wait_for_service(std::chrono::seconds(20))) {
|
||||
throw std::runtime_error("cancel goal service didn't become available");
|
||||
}
|
||||
auto request = std::make_shared<Fibonacci::CancelGoalService::Request>();
|
||||
auto request = std::make_shared<Fibonacci::Impl::CancelGoalService::Request>();
|
||||
request->goal_info.goal_id.uuid = uuid;
|
||||
auto future = cancel_client->async_send_request(request);
|
||||
if (rclcpp::executor::FutureReturnCode::SUCCESS !=
|
||||
@@ -80,7 +80,7 @@ TEST_F(TestServer, construction_and_destruction)
|
||||
|
||||
using GoalHandle = rclcpp_action::ServerGoalHandle<Fibonacci>;
|
||||
auto as = rclcpp_action::create_server<Fibonacci>(node, "fibonacci",
|
||||
[](const GoalID &, std::shared_ptr<const Fibonacci::Goal>) {
|
||||
[](const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>) {
|
||||
return rclcpp_action::GoalResponse::REJECT;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {
|
||||
@@ -93,10 +93,10 @@ TEST_F(TestServer, construction_and_destruction)
|
||||
TEST_F(TestServer, handle_goal_called)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("handle_goal_node", "/rclcpp_action/handle_goal");
|
||||
GoalID received_uuid;
|
||||
GoalUUID received_uuid;
|
||||
|
||||
auto handle_goal = [&received_uuid](
|
||||
const GoalID & uuid, std::shared_ptr<const Fibonacci::Goal>)
|
||||
const GoalUUID & uuid, std::shared_ptr<const Fibonacci::Goal>)
|
||||
{
|
||||
received_uuid = uuid;
|
||||
return rclcpp_action::GoalResponse::REJECT;
|
||||
@@ -114,15 +114,15 @@ TEST_F(TestServer, handle_goal_called)
|
||||
// Create a client that calls the goal request service
|
||||
// Make sure the UUID received is the same as the one sent
|
||||
|
||||
auto client = node->create_client<Fibonacci::GoalRequestService>(
|
||||
auto client = node->create_client<Fibonacci::Impl::SendGoalService>(
|
||||
"fibonacci/_action/send_goal");
|
||||
|
||||
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(20)));
|
||||
|
||||
auto request = std::make_shared<Fibonacci::GoalRequestService::Request>();
|
||||
auto request = std::make_shared<Fibonacci::Impl::SendGoalService::Request>();
|
||||
|
||||
const GoalID uuid{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
|
||||
request->action_goal_id.uuid = uuid;
|
||||
const GoalUUID uuid{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
|
||||
request->goal_id.uuid = uuid;
|
||||
|
||||
auto future = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
@@ -135,10 +135,10 @@ TEST_F(TestServer, handle_goal_called)
|
||||
TEST_F(TestServer, handle_accepted_called)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("handle_exec_node", "/rclcpp_action/handle_accepted");
|
||||
const GoalID uuid{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
|
||||
const GoalUUID uuid{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
|
||||
|
||||
auto handle_goal = [](
|
||||
const GoalID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
{
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||
};
|
||||
@@ -164,16 +164,16 @@ TEST_F(TestServer, handle_accepted_called)
|
||||
ASSERT_TRUE(received_handle);
|
||||
ASSERT_TRUE(received_handle->is_active());
|
||||
EXPECT_EQ(uuid, received_handle->get_goal_id());
|
||||
EXPECT_EQ(*request, *(received_handle->get_goal()));
|
||||
EXPECT_EQ(request->goal, *(received_handle->get_goal()));
|
||||
}
|
||||
|
||||
TEST_F(TestServer, handle_cancel_called)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("handle_cancel_node", "/rclcpp_action/handle_cancel");
|
||||
const GoalID uuid{{10, 20, 30, 40, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
|
||||
const GoalUUID uuid{{10, 20, 30, 40, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
|
||||
|
||||
auto handle_goal = [](
|
||||
const GoalID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
{
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||
};
|
||||
@@ -210,10 +210,10 @@ TEST_F(TestServer, handle_cancel_called)
|
||||
TEST_F(TestServer, publish_status_accepted)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("status_accept_node", "/rclcpp_action/status_accept");
|
||||
const GoalID uuid{{1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 110, 120, 13, 14, 15, 16}};
|
||||
const GoalUUID uuid{{1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 110, 120, 13, 14, 15, 16}};
|
||||
|
||||
auto handle_goal = [](
|
||||
const GoalID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
{
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||
};
|
||||
@@ -271,10 +271,10 @@ TEST_F(TestServer, publish_status_accepted)
|
||||
TEST_F(TestServer, publish_status_canceling)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("status_cancel_node", "/rclcpp_action/status_cancel");
|
||||
const GoalID uuid{{1, 2, 3, 40, 5, 6, 7, 80, 9, 10, 11, 120, 13, 14, 15, 160}};
|
||||
const GoalUUID uuid{{1, 2, 3, 40, 5, 6, 7, 80, 9, 10, 11, 120, 13, 14, 15, 160}};
|
||||
|
||||
auto handle_goal = [](
|
||||
const GoalID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
{
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||
};
|
||||
@@ -326,10 +326,10 @@ TEST_F(TestServer, publish_status_canceling)
|
||||
TEST_F(TestServer, publish_status_canceled)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("status_canceled", "/rclcpp_action/status_canceled");
|
||||
const GoalID uuid{{1, 2, 3, 40, 5, 6, 70, 8, 9, 1, 11, 120, 13, 140, 15, 160}};
|
||||
const GoalUUID uuid{{1, 2, 3, 40, 5, 6, 70, 8, 9, 1, 11, 120, 13, 140, 15, 160}};
|
||||
|
||||
auto handle_goal = [](
|
||||
const GoalID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
{
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||
};
|
||||
@@ -383,10 +383,10 @@ TEST_F(TestServer, publish_status_canceled)
|
||||
TEST_F(TestServer, publish_status_succeeded)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("status_succeeded", "/rclcpp_action/status_succeeded");
|
||||
const GoalID uuid{{1, 2, 3, 40, 5, 6, 70, 8, 9, 1, 11, 120, 13, 140, 15, 160}};
|
||||
const GoalUUID uuid{{1, 2, 3, 40, 5, 6, 70, 8, 9, 1, 11, 120, 13, 140, 15, 160}};
|
||||
|
||||
auto handle_goal = [](
|
||||
const GoalID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
{
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||
};
|
||||
@@ -438,10 +438,10 @@ TEST_F(TestServer, publish_status_succeeded)
|
||||
TEST_F(TestServer, publish_status_aborted)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("status_aborted", "/rclcpp_action/status_aborted");
|
||||
const GoalID uuid{{1, 2, 3, 40, 5, 6, 70, 8, 9, 1, 11, 120, 13, 140, 15, 160}};
|
||||
const GoalUUID uuid{{1, 2, 3, 40, 5, 6, 70, 8, 9, 1, 11, 120, 13, 140, 15, 160}};
|
||||
|
||||
auto handle_goal = [](
|
||||
const GoalID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
{
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||
};
|
||||
@@ -493,10 +493,10 @@ TEST_F(TestServer, publish_status_aborted)
|
||||
TEST_F(TestServer, publish_feedback)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("pub_feedback", "/rclcpp_action/pub_feedback");
|
||||
const GoalID uuid{{1, 20, 30, 4, 5, 6, 70, 8, 9, 1, 11, 120, 13, 14, 15, 160}};
|
||||
const GoalUUID uuid{{1, 20, 30, 4, 5, 6, 70, 8, 9, 1, 11, 120, 13, 14, 15, 160}};
|
||||
|
||||
auto handle_goal = [](
|
||||
const GoalID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
{
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||
};
|
||||
@@ -521,7 +521,7 @@ TEST_F(TestServer, publish_feedback)
|
||||
(void)as;
|
||||
|
||||
// Subscribe to feedback messages
|
||||
using FeedbackT = Fibonacci::Feedback;
|
||||
using FeedbackT = Fibonacci::Impl::FeedbackMessage;
|
||||
std::vector<FeedbackT::SharedPtr> received_msgs;
|
||||
auto subscriber = node->create_subscription<FeedbackT>(
|
||||
"fibonacci/_action/feedback", [&received_msgs](FeedbackT::SharedPtr msg)
|
||||
@@ -531,7 +531,7 @@ TEST_F(TestServer, publish_feedback)
|
||||
|
||||
send_goal_request(node, uuid);
|
||||
|
||||
auto sent_message = std::make_shared<FeedbackT>();
|
||||
auto sent_message = std::make_shared<Fibonacci::Feedback>();
|
||||
sent_message->sequence = {1, 1, 2, 3, 5};
|
||||
received_handle->publish_feedback(sent_message);
|
||||
|
||||
@@ -544,17 +544,16 @@ TEST_F(TestServer, publish_feedback)
|
||||
|
||||
ASSERT_EQ(1u, received_msgs.size());
|
||||
auto & msg = received_msgs.back();
|
||||
ASSERT_EQ(sent_message->sequence, msg->sequence);
|
||||
ASSERT_EQ(uuid, msg->action_goal_id.uuid);
|
||||
ASSERT_EQ(sent_message->sequence, msg->feedback.sequence);
|
||||
}
|
||||
|
||||
TEST_F(TestServer, get_result)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("get_result", "/rclcpp_action/get_result");
|
||||
const GoalID uuid{{1, 2, 3, 4, 5, 6, 7, 80, 90, 10, 11, 12, 13, 14, 15, 160}};
|
||||
const GoalUUID uuid{{1, 2, 3, 4, 5, 6, 7, 80, 90, 10, 11, 12, 13, 14, 15, 160}};
|
||||
|
||||
auto handle_goal = [](
|
||||
const GoalID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
{
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||
};
|
||||
@@ -581,13 +580,13 @@ TEST_F(TestServer, get_result)
|
||||
send_goal_request(node, uuid);
|
||||
|
||||
// Send result request
|
||||
auto result_client = node->create_client<Fibonacci::GoalResultService>(
|
||||
auto result_client = node->create_client<Fibonacci::Impl::GetResultService>(
|
||||
"fibonacci/_action/get_result");
|
||||
if (!result_client->wait_for_service(std::chrono::seconds(20))) {
|
||||
throw std::runtime_error("get result service didn't become available");
|
||||
}
|
||||
auto request = std::make_shared<Fibonacci::GoalResultService::Request>();
|
||||
request->action_goal_id.uuid = uuid;
|
||||
auto request = std::make_shared<Fibonacci::Impl::GetResultService::Request>();
|
||||
request->goal_id.uuid = uuid;
|
||||
auto future = result_client->async_send_request(request);
|
||||
|
||||
// Send a result
|
||||
@@ -600,17 +599,17 @@ TEST_F(TestServer, get_result)
|
||||
rclcpp::spin_until_future_complete(node, future));
|
||||
|
||||
auto response = future.get();
|
||||
EXPECT_EQ(action_msgs::msg::GoalStatus::STATUS_SUCCEEDED, response->action_status);
|
||||
EXPECT_EQ(result->sequence, response->sequence);
|
||||
EXPECT_EQ(action_msgs::msg::GoalStatus::STATUS_SUCCEEDED, response->status);
|
||||
EXPECT_EQ(result->sequence, response->result.sequence);
|
||||
}
|
||||
|
||||
TEST_F(TestServer, deferred_execution)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("defer_exec", "/rclcpp_action/defer_exec");
|
||||
const GoalID uuid{{1, 2, 3, 40, 5, 6, 70, 8, 9, 1, 11, 120, 13, 140, 15, 160}};
|
||||
const GoalUUID uuid{{1, 2, 3, 40, 5, 6, 70, 8, 9, 1, 11, 120, 13, 140, 15, 160}};
|
||||
|
||||
auto handle_goal = [](
|
||||
const GoalID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>)
|
||||
{
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_DEFER;
|
||||
};
|
||||
|
||||
26
rclcpp_components/CHANGELOG.rst
Normal file
26
rclcpp_components/CHANGELOG.rst
Normal file
@@ -0,0 +1,26 @@
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Changelog for package rclcpp_components
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
0.7.0 (2019-04-14)
|
||||
------------------
|
||||
* Introduce rclcpp_components to implement composition (`#665 <https://github.com/ros2/rclcpp/issues/665>`_)
|
||||
* Contributors: Michael Carroll
|
||||
|
||||
0.6.2 (2018-12-12)
|
||||
------------------
|
||||
|
||||
0.6.1 (2018-12-06)
|
||||
------------------
|
||||
|
||||
0.6.0 (2018-11-19)
|
||||
------------------
|
||||
|
||||
0.5.1 (2018-06-28)
|
||||
------------------
|
||||
|
||||
0.5.0 (2018-06-25)
|
||||
------------------
|
||||
|
||||
0.4.0 (2017-12-08)
|
||||
------------------
|
||||
122
rclcpp_components/CMakeLists.txt
Normal file
122
rclcpp_components/CMakeLists.txt
Normal file
@@ -0,0 +1,122 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(rclcpp_components)
|
||||
|
||||
# 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()
|
||||
|
||||
find_package(ament_cmake_ros REQUIRED)
|
||||
find_package(ament_index_cpp REQUIRED)
|
||||
find_package(class_loader REQUIRED)
|
||||
find_package(composition_interfaces REQUIRED)
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(rcpputils REQUIRED)
|
||||
|
||||
include_directories(include)
|
||||
|
||||
add_library(
|
||||
component_manager
|
||||
STATIC
|
||||
src/component_manager.cpp
|
||||
)
|
||||
ament_target_dependencies(component_manager
|
||||
"ament_index_cpp"
|
||||
"class_loader"
|
||||
"composition_interfaces"
|
||||
"rclcpp"
|
||||
"rcpputils"
|
||||
)
|
||||
|
||||
add_executable(
|
||||
component_container
|
||||
src/component_container.cpp
|
||||
)
|
||||
target_link_libraries(component_container component_manager)
|
||||
ament_target_dependencies(component_container
|
||||
"rclcpp"
|
||||
)
|
||||
|
||||
add_executable(
|
||||
component_container_mt
|
||||
src/component_container_mt.cpp
|
||||
)
|
||||
target_link_libraries(component_container_mt component_manager)
|
||||
ament_target_dependencies(component_container_mt
|
||||
"rclcpp"
|
||||
)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_link_libraries(component_container "stdc++fs")
|
||||
target_link_libraries(component_container_mt "stdc++fs")
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
|
||||
set(components "")
|
||||
add_library(test_component SHARED test/components/test_component.cpp)
|
||||
ament_target_dependencies(test_component
|
||||
"class_loader"
|
||||
"rclcpp"
|
||||
"rclcpp_components")
|
||||
#rclcpp_components_register_nodes(test_component "test_rclcpp_components::TestComponent")
|
||||
set(components "${components}test_rclcpp_components::TestComponentFoo;$<TARGET_FILE:test_component>\n")
|
||||
set(components "${components}test_rclcpp_components::TestComponentBar;$<TARGET_FILE:test_component>\n")
|
||||
set(components "${components}test_rclcpp_components::TestComponentNoNode;$<TARGET_FILE:test_component>\n")
|
||||
|
||||
file(GENERATE
|
||||
OUTPUT
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/test_ament_index/$<CONFIG>/share/ament_index/resource_index/rclcpp_components/${PROJECT_NAME}"
|
||||
CONTENT "${components}")
|
||||
|
||||
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
if(WIN32)
|
||||
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_component_manager test/test_component_manager.cpp
|
||||
APPEND_ENV AMENT_PREFIX_PATH=${CMAKE_CURRENT_BINARY_DIR}/test_ament_index/$<CONFIG>
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_component_manager)
|
||||
target_link_libraries(test_component_manager component_manager)
|
||||
target_include_directories(test_component_manager PRIVATE src)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_component_manager_api test/test_component_manager_api.cpp
|
||||
APPEND_ENV AMENT_PREFIX_PATH=${CMAKE_CURRENT_BINARY_DIR}/test_ament_index/$<CONFIG>
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_component_manager_api)
|
||||
target_link_libraries(test_component_manager_api component_manager)
|
||||
target_include_directories(test_component_manager_api PRIVATE src)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Install executables
|
||||
install(
|
||||
TARGETS component_container component_container_mt
|
||||
RUNTIME DESTINATION lib/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
# Install include directories
|
||||
install(
|
||||
DIRECTORY include/
|
||||
DESTINATION include
|
||||
)
|
||||
|
||||
# Install cmake
|
||||
install(
|
||||
DIRECTORY cmake
|
||||
DESTINATION share/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
# specific order: dependents before dependencies
|
||||
ament_export_include_directories(include)
|
||||
ament_export_dependencies(class_loader)
|
||||
ament_export_dependencies(rclcpp)
|
||||
ament_package(CONFIG_EXTRAS rclcpp_components-extras.cmake)
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2016 Open Source Robotics Foundation, Inc.
|
||||
# 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.
|
||||
@@ -14,4 +14,5 @@
|
||||
|
||||
# register node plugins
|
||||
ament_index_register_resource(
|
||||
"node_plugin" CONTENT "${_RCLCPP__NODE_PLUGINS}")
|
||||
"rclcpp_components" CONTENT "${_RCLCPP_COMPONENTS__NODES}")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2016 Open Source Robotics Foundation, Inc.
|
||||
# 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.
|
||||
@@ -13,38 +13,38 @@
|
||||
# limitations under the License.
|
||||
|
||||
#
|
||||
# Register a node plugin with the ament resource index.
|
||||
# Register an rclcpp component with the ament resource index.
|
||||
#
|
||||
# The passed library can contain multiple plugins extending the node interface.
|
||||
# The passed library can contain multiple nodes each registered via macro.
|
||||
#
|
||||
# :param target: the shared library target
|
||||
# :type target: string
|
||||
# :param ARGN: the unique plugin names being exported using class_loader
|
||||
# :type ARGN: list of strings
|
||||
#
|
||||
macro(rclcpp_register_node_plugins target)
|
||||
macro(rclcpp_components_register_nodes target)
|
||||
if(NOT TARGET ${target})
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"rclcpp_register_node_plugins() first argument "
|
||||
"rclcpp_components_register_nodes() first argument "
|
||||
"'${target}' is not a target")
|
||||
endif()
|
||||
get_target_property(_target_type ${target} TYPE)
|
||||
if(NOT _target_type STREQUAL "SHARED_LIBRARY")
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"rclcpp_register_node_plugins() first argument "
|
||||
"rclcpp_components_register_nodes() first argument "
|
||||
"'${target}' is not a shared library target")
|
||||
endif()
|
||||
|
||||
if(${ARGC} GREATER 0)
|
||||
_rclcpp_register_package_hook()
|
||||
_rclcpp_components_register_package_hook()
|
||||
set(_unique_names)
|
||||
foreach(_arg ${ARGN})
|
||||
if(_arg IN_LIST _unique_names)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"rclcpp_register_node_plugins() the plugin names "
|
||||
"rclcpp_components_register_nodes() the plugin names "
|
||||
"must be unique (multiple '${_arg}')")
|
||||
endif()
|
||||
list(APPEND _unique_names "${_arg}")
|
||||
@@ -54,8 +54,9 @@ macro(rclcpp_register_node_plugins target)
|
||||
else()
|
||||
set(_path "lib")
|
||||
endif()
|
||||
set(_RCLCPP__NODE_PLUGINS
|
||||
"${_RCLCPP__NODE_PLUGINS}${_arg};${_path}/$<TARGET_FILE_NAME:${target}>\n")
|
||||
set(_RCLCPP_COMPONENTS__NODES
|
||||
"${_RCLCPP_COMPONENTS__NODES}${_arg};${_path}/$<TARGET_FILE_NAME:${target}>\n")
|
||||
endforeach()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
46
rclcpp_components/include/rclcpp_components/node_factory.hpp
Normal file
46
rclcpp_components/include/rclcpp_components/node_factory.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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_COMPONENTS__NODE_FACTORY_HPP__
|
||||
#define RCLCPP_COMPONENTS__NODE_FACTORY_HPP__
|
||||
|
||||
#include "rclcpp_components/node_instance_wrapper.hpp"
|
||||
|
||||
namespace rclcpp_components
|
||||
{
|
||||
|
||||
/// The NodeFactory interface is used by the class loader to instantiate components.
|
||||
/**
|
||||
* The NodeFactory interface serves two purposes:
|
||||
* * It allows for classes not derived from `rclcpp::Node` to be used as components.
|
||||
* * It allows derived constructors to be called when components are loaded.
|
||||
*/
|
||||
class NodeFactory
|
||||
{
|
||||
public:
|
||||
NodeFactory() = default;
|
||||
|
||||
virtual ~NodeFactory() = default;
|
||||
|
||||
/// Create an instance of a component
|
||||
/**
|
||||
* \param[in] options Additional options used in the construction of the component.
|
||||
*/
|
||||
virtual
|
||||
NodeInstanceWrapper
|
||||
create_node_instance(const rclcpp::NodeOptions & options) = 0;
|
||||
};
|
||||
} // namespace rclcpp_components
|
||||
|
||||
#endif // RCLCPP_COMPONENTS__NODE_FACTORY_HPP__
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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_COMPONENTS__NODE_FACTORY_TEMPLATE_HPP__
|
||||
#define RCLCPP_COMPONENTS__NODE_FACTORY_TEMPLATE_HPP__
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp_components/node_factory.hpp"
|
||||
|
||||
namespace rclcpp_components
|
||||
{
|
||||
|
||||
/// NodeFactoryTemplate is a convenience class for instantiating components.
|
||||
/**
|
||||
* The NodeFactoryTemplate class can be used to provide the NodeFactory interface for
|
||||
* components that implement a single-argument constructor and `get_node_base_interface`.
|
||||
*/
|
||||
template<typename NodeT>
|
||||
class NodeFactoryTemplate : public NodeFactory
|
||||
{
|
||||
public:
|
||||
NodeFactoryTemplate() = default;
|
||||
virtual ~NodeFactoryTemplate() = default;
|
||||
|
||||
/// Create an instance of a component
|
||||
/**
|
||||
* \param[in] options Additional options used in the construction of the component.
|
||||
*/
|
||||
NodeInstanceWrapper
|
||||
create_node_instance(const rclcpp::NodeOptions & options) override
|
||||
{
|
||||
auto node = std::make_shared<NodeT>(options);
|
||||
|
||||
return NodeInstanceWrapper(
|
||||
node, std::bind(&NodeT::get_node_base_interface, node));
|
||||
}
|
||||
};
|
||||
} // namespace rclcpp_components
|
||||
|
||||
#endif // RCLCPP_COMPONENTS__NODE_FACTORY_TEMPLATE_HPP__
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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_COMPONENTS__NODE_INSTANCE_WRAPPER_HPP__
|
||||
#define RCLCPP_COMPONENTS__NODE_INSTANCE_WRAPPER_HPP__
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
|
||||
namespace rclcpp_components
|
||||
{
|
||||
/// The NodeInstanceWrapper encapsulates the node instance.
|
||||
class NodeInstanceWrapper
|
||||
{
|
||||
public:
|
||||
using NodeBaseInterfaceGetter = std::function<
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr(const std::shared_ptr<void> &)>;
|
||||
|
||||
NodeInstanceWrapper()
|
||||
: node_instance_(nullptr)
|
||||
{}
|
||||
|
||||
NodeInstanceWrapper(
|
||||
std::shared_ptr<void> node_instance,
|
||||
NodeBaseInterfaceGetter node_base_interface_getter)
|
||||
: node_instance_(node_instance), node_base_interface_getter_(node_base_interface_getter)
|
||||
{}
|
||||
|
||||
/// Get a type-erased pointer to the original Node instance
|
||||
/**
|
||||
* This is only for debugging and special cases.
|
||||
* For most cases `get_node_base_interface` will be sufficient.
|
||||
*
|
||||
* \return Shared pointer to the encapsulated Node instance.
|
||||
*/
|
||||
const std::shared_ptr<void>
|
||||
get_node_instance() const
|
||||
{
|
||||
return node_instance_;
|
||||
}
|
||||
|
||||
/// Get NodeBaseInterface pointer for the encapsulated Node Instance.
|
||||
/**
|
||||
* \return Shared NodeBaseInterface pointer of the encapsulated Node instance.
|
||||
*/
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
||||
get_node_base_interface()
|
||||
{
|
||||
return node_base_interface_getter_(node_instance_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<void> node_instance_;
|
||||
NodeBaseInterfaceGetter node_base_interface_getter_;
|
||||
};
|
||||
} // namespace rclcpp_components
|
||||
|
||||
#endif // RCLCPP_COMPONENTS__NODE_INSTANCE_WRAPPER_HPP__
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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_COMPONENTS__REGISTER_NODE_MACRO_HPP__
|
||||
#define RCLCPP_COMPONENTS__REGISTER_NODE_MACRO_HPP__
|
||||
|
||||
#include "class_loader/class_loader.hpp"
|
||||
#include "rclcpp_components/node_factory_template.hpp"
|
||||
|
||||
/// Register a component that can be dynamically loaded at runtime.
|
||||
/**
|
||||
* The registration macro should appear once per component per library.
|
||||
* The macro should appear in a single translation unit.
|
||||
*
|
||||
* Valid arguments for NodeClass shall:
|
||||
* * Have a constructor that takes a single argument that is a `rclcpp::NodeOptions` instance.
|
||||
* * Have a method of of the signature:
|
||||
* `rclcpp::node_interfaces::NodeBaseInterface::SharedPtr get_node_base_interface`
|
||||
*
|
||||
* Note: NodeClass does not need to inherit from `rclcpp::Node`, but it is the easiest way.
|
||||
*/
|
||||
#define RCLCPP_COMPONENTS_REGISTER_NODE(NodeClass) \
|
||||
CLASS_LOADER_REGISTER_CLASS(rclcpp_components::NodeFactoryTemplate<NodeClass>, \
|
||||
rclcpp_components::NodeFactory)
|
||||
|
||||
#endif // RCLCPP_COMPONENTS__REGISTER_NODE_MACRO_HPP__
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user