Compare commits
31 Commits
check_para
...
0.7.0
| 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 |
@@ -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,35 @@
|
||||
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>`_)
|
||||
|
||||
@@ -47,6 +47,7 @@ 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
|
||||
@@ -142,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()
|
||||
@@ -215,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()
|
||||
@@ -239,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}
|
||||
@@ -261,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()
|
||||
@@ -326,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})
|
||||
@@ -404,24 +431,17 @@ if(BUILD_TESTING)
|
||||
|
||||
ament_add_gtest(test_local_parameters test/test_local_parameters.cpp)
|
||||
if(TARGET test_local_parameters)
|
||||
target_include_directories(test_local_parameters PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
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)
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -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,6 +41,7 @@
|
||||
#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"
|
||||
@@ -53,8 +54,10 @@
|
||||
#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"
|
||||
@@ -72,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();
|
||||
@@ -117,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
|
||||
@@ -138,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.
|
||||
@@ -148,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.
|
||||
/**
|
||||
@@ -167,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.
|
||||
/**
|
||||
@@ -200,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.
|
||||
/**
|
||||
@@ -223,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.
|
||||
/**
|
||||
@@ -241,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);
|
||||
|
||||
@@ -496,6 +560,122 @@ 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)
|
||||
|
||||
@@ -514,7 +694,9 @@ private:
|
||||
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,10 +314,13 @@ 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),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -250,8 +351,11 @@ 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>();
|
||||
}
|
||||
@@ -286,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;
|
||||
}
|
||||
@@ -300,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
|
||||
|
||||
@@ -56,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
|
||||
|
||||
@@ -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
|
||||
|
||||
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)
|
||||
@@ -281,10 +281,10 @@ 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);
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
@@ -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.2</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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -34,35 +34,72 @@
|
||||
#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())),
|
||||
@@ -80,9 +117,11 @@ 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_,
|
||||
@@ -94,10 +133,48 @@ Node::Node(
|
||||
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()
|
||||
{}
|
||||
|
||||
@@ -113,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
|
||||
{
|
||||
@@ -308,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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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_);
|
||||
|
||||
@@ -46,18 +46,38 @@ dispatch_semaphore_t SignalHandler::signal_handler_sem_;
|
||||
sem_t SignalHandler::signal_handler_sem_;
|
||||
#endif
|
||||
|
||||
SignalHandler &
|
||||
SignalHandler::get_global_signal_handler()
|
||||
{
|
||||
static SignalHandler signal_handler;
|
||||
return signal_handler;
|
||||
}
|
||||
// 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()
|
||||
{
|
||||
static rclcpp::Logger logger = rclcpp::get_logger("rclcpp");
|
||||
return 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -169,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();
|
||||
|
||||
@@ -236,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);
|
||||
|
||||
@@ -245,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();
|
||||
|
||||
@@ -266,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_);
|
||||
@@ -285,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_);
|
||||
|
||||
@@ -295,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_);
|
||||
}
|
||||
@@ -330,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);
|
||||
@@ -357,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_);
|
||||
@@ -371,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);
|
||||
@@ -380,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());
|
||||
}
|
||||
|
||||
@@ -425,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,15 @@
|
||||
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)
|
||||
------------------
|
||||
|
||||
|
||||
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.2</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;
|
||||
|
||||
@@ -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__
|
||||
33
rclcpp_components/package.xml
Normal file
33
rclcpp_components/package.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp_components</name>
|
||||
<version>0.7.0</version>
|
||||
<description>Package containing tools for dynamically loadable components</description>
|
||||
<maintainer email="michael@openrobotics.org">Michael Carroll</maintainer>
|
||||
<license>Apache License 2.0</license>
|
||||
|
||||
<buildtool_depend>ament_cmake_ros</buildtool_depend>
|
||||
|
||||
<build_depend>ament_index_cpp</build_depend>
|
||||
<build_depend>class_loader</build_depend>
|
||||
<build_depend>composition_interfaces</build_depend>
|
||||
<build_depend>rclcpp</build_depend>
|
||||
<build_depend>rcpputils</build_depend>
|
||||
|
||||
<exec_depend>ament_index_cpp</exec_depend>
|
||||
<exec_depend>class_loader</exec_depend>
|
||||
<exec_depend>composition_interfaces</exec_depend>
|
||||
<exec_depend>rclcpp</exec_depend>
|
||||
|
||||
<test_depend>ament_cmake_gtest</test_depend>
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
<test_depend>launch_testing</test_depend>
|
||||
<test_depend>std_msgs</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2015 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.
|
||||
@@ -12,18 +12,18 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# copied from rclcpp/rclcpp-extras.cmake
|
||||
# copied from rclcpp_components/rclcpp_components-extras.cmake
|
||||
|
||||
# register ament_package() hook for node plugins once
|
||||
macro(_rclcpp_register_package_hook)
|
||||
if(NOT DEFINED _RCLCPP_PACKAGE_HOOK_REGISTERED)
|
||||
set(_RCLCPP_PACKAGE_HOOK_REGISTERED TRUE)
|
||||
# register ament_package() hook for node plugins once.
|
||||
macro(_rclcpp_components_register_package_hook)
|
||||
if(NOT DEFINED _RCLCPP_COMPONENTS_PACKAGE_HOOK_REGISTERED)
|
||||
set(_RCLCPP_COMPONENTS_PACKAGE_HOOK_REGISTERED TRUE)
|
||||
|
||||
find_package(ament_cmake_core QUIET REQUIRED)
|
||||
ament_register_extension("ament_package" "rclcpp"
|
||||
"rclcpp_package_hook.cmake")
|
||||
ament_register_extension("ament_package" "rclcpp_components"
|
||||
"rclcpp_components_package_hook.cmake")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
include("${rclcpp_DIR}/rclcpp_create_node_main.cmake")
|
||||
include("${rclcpp_DIR}/rclcpp_register_node_plugins.cmake")
|
||||
include("${rclcpp_components_DIR}/rclcpp_components_register_nodes.cmake")
|
||||
|
||||
29
rclcpp_components/src/component_container.cpp
Normal file
29
rclcpp_components/src/component_container.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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 <memory>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "component_manager.hpp"
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
/// Component container with a single-threaded executor.
|
||||
rclcpp::init(argc, argv);
|
||||
auto exec = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
auto node = std::make_shared<rclcpp_components::ComponentManager>(exec);
|
||||
exec->add_node(node);
|
||||
exec->spin();
|
||||
}
|
||||
29
rclcpp_components/src/component_container_mt.cpp
Normal file
29
rclcpp_components/src/component_container_mt.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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 <memory>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "component_manager.hpp"
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
/// Component container with a multi-threaded executor.
|
||||
rclcpp::init(argc, argv);
|
||||
auto exec = std::make_shared<rclcpp::executors::MultiThreadedExecutor>();
|
||||
auto node = std::make_shared<rclcpp_components::ComponentManager>(exec);
|
||||
exec->add_node(node);
|
||||
exec->spin();
|
||||
}
|
||||
239
rclcpp_components/src/component_manager.cpp
Normal file
239
rclcpp_components/src/component_manager.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
// 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 "component_manager.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ament_index_cpp/get_resource.hpp"
|
||||
#include "rcpputils/filesystem_helper.hpp"
|
||||
#include "rcpputils/split.hpp"
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
namespace rclcpp_components
|
||||
{
|
||||
|
||||
ComponentManager::ComponentManager(
|
||||
std::weak_ptr<rclcpp::executor::Executor> executor)
|
||||
: Node("ComponentManager"),
|
||||
executor_(executor)
|
||||
{
|
||||
loadNode_srv_ = create_service<LoadNode>("~/_container/load_node",
|
||||
std::bind(&ComponentManager::OnLoadNode, this, _1, _2, _3));
|
||||
unloadNode_srv_ = create_service<UnloadNode>("~/_container/unload_node",
|
||||
std::bind(&ComponentManager::OnUnloadNode, this, _1, _2, _3));
|
||||
listNodes_srv_ = create_service<ListNodes>("~/_container/list_nodes",
|
||||
std::bind(&ComponentManager::OnListNodes, this, _1, _2, _3));
|
||||
}
|
||||
|
||||
ComponentManager::~ComponentManager()
|
||||
{
|
||||
if (node_wrappers_.size()) {
|
||||
RCLCPP_DEBUG(get_logger(), "Removing components from executor");
|
||||
if (auto exec = executor_.lock()) {
|
||||
for (auto & wrapper : node_wrappers_) {
|
||||
exec->remove_node(wrapper.second.get_node_base_interface());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ComponentManager::ComponentResource>
|
||||
ComponentManager::get_component_resources(const std::string & package_name) const
|
||||
{
|
||||
std::string content;
|
||||
std::string base_path;
|
||||
if (!ament_index_cpp::get_resource(
|
||||
"rclcpp_components", package_name, content, &base_path))
|
||||
{
|
||||
throw ComponentManagerException("Could not find requested resource in ament index");
|
||||
}
|
||||
|
||||
std::vector<ComponentResource> resources;
|
||||
std::vector<std::string> lines = rcpputils::split(content, '\n', true);
|
||||
for (const auto & line : lines) {
|
||||
std::vector<std::string> parts = rcpputils::split(line, ';');
|
||||
if (parts.size() != 2) {
|
||||
throw ComponentManagerException("Invalid resource entry");
|
||||
}
|
||||
|
||||
std::string library_path = parts[1];
|
||||
if (!rcpputils::fs::path(library_path).is_absolute()) {
|
||||
library_path = base_path + "/" + library_path;
|
||||
}
|
||||
resources.push_back({parts[0], library_path});
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp_components::NodeFactory>
|
||||
ComponentManager::create_component_factory(const ComponentResource & resource)
|
||||
{
|
||||
std::string library_path = resource.second;
|
||||
std::string class_name = resource.first;
|
||||
std::string fq_class_name = "rclcpp_components::NodeFactoryTemplate<" + class_name + ">";
|
||||
|
||||
class_loader::ClassLoader * loader;
|
||||
if (loaders_.find(library_path) == loaders_.end()) {
|
||||
RCLCPP_INFO(get_logger(), "Load Library: %s", library_path.c_str());
|
||||
try {
|
||||
loaders_[library_path] = std::make_unique<class_loader::ClassLoader>(library_path);
|
||||
} catch (const std::exception & ex) {
|
||||
throw ComponentManagerException("Failed to load library: " + std::string(ex.what()));
|
||||
} catch (...) {
|
||||
throw ComponentManagerException("Failed to load library");
|
||||
}
|
||||
}
|
||||
loader = loaders_[library_path].get();
|
||||
|
||||
auto classes = loader->getAvailableClasses<rclcpp_components::NodeFactory>();
|
||||
for (const auto & clazz : classes) {
|
||||
RCLCPP_INFO(get_logger(), "Found class: %s", clazz.c_str());
|
||||
if (clazz == class_name || clazz == fq_class_name) {
|
||||
RCLCPP_INFO(get_logger(), "Instantiate class: %s", clazz.c_str());
|
||||
return loader->createInstance<rclcpp_components::NodeFactory>(clazz);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void
|
||||
ComponentManager::OnLoadNode(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<LoadNode::Request> request,
|
||||
std::shared_ptr<LoadNode::Response> response)
|
||||
{
|
||||
(void) request_header;
|
||||
|
||||
try {
|
||||
auto resources = get_component_resources(request->package_name);
|
||||
|
||||
for (const auto & resource : resources) {
|
||||
if (resource.first != request->plugin_name) {
|
||||
continue;
|
||||
}
|
||||
auto factory = create_component_factory(resource);
|
||||
|
||||
if (factory == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Parameter> parameters;
|
||||
for (const auto & p : request->parameters) {
|
||||
parameters.push_back(rclcpp::Parameter::from_parameter_msg(p));
|
||||
}
|
||||
|
||||
std::vector<std::string> remap_rules {request->remap_rules};
|
||||
|
||||
if (!request->node_name.empty()) {
|
||||
remap_rules.push_back("__node:=" + request->node_name);
|
||||
}
|
||||
|
||||
if (!request->node_namespace.empty()) {
|
||||
remap_rules.push_back("__ns:=" + request->node_namespace);
|
||||
}
|
||||
|
||||
auto options = rclcpp::NodeOptions()
|
||||
.initial_parameters(parameters)
|
||||
.arguments(remap_rules);
|
||||
|
||||
auto node_id = unique_id++;
|
||||
|
||||
if (0 == node_id) {
|
||||
// This puts a technical limit on the number of times you can add a component.
|
||||
// But even if you could add (and remove) them at 1 kHz (very optimistic rate)
|
||||
// it would still be a very long time before you could exhaust the pool of id's:
|
||||
// 2^64 / 1000 times per sec / 60 sec / 60 min / 24 hours / 365 days = 584,942,417 years
|
||||
// So around 585 million years. Even at 1 GHz, it would take 585 years.
|
||||
// I think it's safe to avoid trying to handle overflow.
|
||||
// If we roll over then it's most likely a bug.
|
||||
throw std::overflow_error("exhausted the unique ids for components in this process");
|
||||
}
|
||||
|
||||
try {
|
||||
node_wrappers_[node_id] = factory->create_node_instance(options);
|
||||
} catch (...) {
|
||||
// In the case that the component constructor throws an exception,
|
||||
// rethrow into the following catch block.
|
||||
throw ComponentManagerException("Component constructor threw an exception");
|
||||
}
|
||||
|
||||
auto node = node_wrappers_[node_id].get_node_base_interface();
|
||||
if (auto exec = executor_.lock()) {
|
||||
exec->add_node(node, true);
|
||||
}
|
||||
response->full_node_name = node->get_fully_qualified_name();
|
||||
response->unique_id = node_id;
|
||||
response->success = true;
|
||||
return;
|
||||
}
|
||||
RCLCPP_ERROR(
|
||||
get_logger(), "Failed to find class with the requested plugin name '%s' in "
|
||||
"the loaded library",
|
||||
request->plugin_name.c_str());
|
||||
response->error_message = "Failed to find class with the requested plugin name.";
|
||||
response->success = false;
|
||||
} catch (const ComponentManagerException & ex) {
|
||||
RCLCPP_ERROR(get_logger(), ex.what());
|
||||
response->error_message = ex.what();
|
||||
response->success = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ComponentManager::OnUnloadNode(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<UnloadNode::Request> request,
|
||||
std::shared_ptr<UnloadNode::Response> response)
|
||||
{
|
||||
(void) request_header;
|
||||
|
||||
auto wrapper = node_wrappers_.find(request->unique_id);
|
||||
|
||||
if (wrapper == node_wrappers_.end()) {
|
||||
response->success = false;
|
||||
std::stringstream ss;
|
||||
ss << "No node found with unique_id: " << request->unique_id;
|
||||
response->error_message = ss.str();
|
||||
RCLCPP_WARN(get_logger(), ss.str());
|
||||
} else {
|
||||
if (auto exec = executor_.lock()) {
|
||||
exec->remove_node(wrapper->second.get_node_base_interface());
|
||||
}
|
||||
node_wrappers_.erase(wrapper);
|
||||
response->success = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ComponentManager::OnListNodes(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<ListNodes::Request> request,
|
||||
std::shared_ptr<ListNodes::Response> response)
|
||||
{
|
||||
(void) request_header;
|
||||
(void) request;
|
||||
|
||||
for (auto & wrapper : node_wrappers_) {
|
||||
response->unique_ids.push_back(wrapper.first);
|
||||
response->full_node_names.push_back(
|
||||
wrapper.second.get_node_base_interface()->get_fully_qualified_name());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rclcpp_components
|
||||
104
rclcpp_components/src/component_manager.hpp
Normal file
104
rclcpp_components/src/component_manager.hpp
Normal file
@@ -0,0 +1,104 @@
|
||||
// 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 COMPONENT_MANAGER_HPP__
|
||||
#define COMPONENT_MANAGER_HPP__
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "class_loader/class_loader.hpp"
|
||||
|
||||
#include "rclcpp/executor.hpp"
|
||||
#include "rclcpp/node_options.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "composition_interfaces/srv/load_node.hpp"
|
||||
#include "composition_interfaces/srv/unload_node.hpp"
|
||||
#include "composition_interfaces/srv/list_nodes.hpp"
|
||||
|
||||
#include "rclcpp_components/node_factory.hpp"
|
||||
|
||||
namespace rclcpp_components
|
||||
{
|
||||
|
||||
class ComponentManagerException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit ComponentManagerException(const std::string & error_desc)
|
||||
: std::runtime_error(error_desc) {}
|
||||
};
|
||||
|
||||
class ComponentManager : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
using LoadNode = composition_interfaces::srv::LoadNode;
|
||||
using UnloadNode = composition_interfaces::srv::UnloadNode;
|
||||
using ListNodes = composition_interfaces::srv::ListNodes;
|
||||
|
||||
/// Represents a component resource.
|
||||
/**
|
||||
* Is a pair of class name (for class loader) and library path (absolute)
|
||||
*/
|
||||
using ComponentResource = std::pair<std::string, std::string>;
|
||||
|
||||
ComponentManager(
|
||||
std::weak_ptr<rclcpp::executor::Executor> executor);
|
||||
|
||||
~ComponentManager();
|
||||
|
||||
/// Return a list of valid loadable components in a given package.
|
||||
std::vector<ComponentResource>
|
||||
get_component_resources(const std::string & package_name) const;
|
||||
|
||||
std::shared_ptr<rclcpp_components::NodeFactory>
|
||||
create_component_factory(const ComponentResource & resource);
|
||||
|
||||
private:
|
||||
void
|
||||
OnLoadNode(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<LoadNode::Request> request,
|
||||
std::shared_ptr<LoadNode::Response> response);
|
||||
|
||||
void
|
||||
OnUnloadNode(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<UnloadNode::Request> request,
|
||||
std::shared_ptr<UnloadNode::Response> response);
|
||||
|
||||
void
|
||||
OnListNodes(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<ListNodes::Request> request,
|
||||
std::shared_ptr<ListNodes::Response> response);
|
||||
|
||||
private:
|
||||
std::weak_ptr<rclcpp::executor::Executor> executor_;
|
||||
|
||||
uint64_t unique_id {1};
|
||||
std::map<std::string, std::unique_ptr<class_loader::ClassLoader>> loaders_;
|
||||
std::map<uint64_t, rclcpp_components::NodeInstanceWrapper> node_wrappers_;
|
||||
|
||||
rclcpp::Service<LoadNode>::SharedPtr loadNode_srv_;
|
||||
rclcpp::Service<UnloadNode>::SharedPtr unloadNode_srv_;
|
||||
rclcpp::Service<ListNodes>::SharedPtr listNodes_srv_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp_components
|
||||
|
||||
#endif // COMPONENT_MANAGER_HPP__
|
||||
68
rclcpp_components/test/components/test_component.cpp
Normal file
68
rclcpp_components/test/components/test_component.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
// 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/rclcpp.hpp"
|
||||
|
||||
namespace test_rclcpp_components
|
||||
{
|
||||
/// Simple test component
|
||||
class TestComponentFoo : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
explicit TestComponentFoo(rclcpp::NodeOptions options)
|
||||
: rclcpp::Node("test_component_foo", options)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// Simple test component
|
||||
class TestComponentBar : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
explicit TestComponentBar(rclcpp::NodeOptions options)
|
||||
: rclcpp::Node("test_component_bar", options)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// Simple test component that doesn't inherit from rclcpp::Node
|
||||
class TestComponentNoNode
|
||||
{
|
||||
public:
|
||||
explicit TestComponentNoNode(rclcpp::NodeOptions options)
|
||||
: node_("test_component_no_node", options)
|
||||
{
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
||||
get_node_base_interface()
|
||||
{
|
||||
return node_.get_node_base_interface();
|
||||
}
|
||||
|
||||
private:
|
||||
rclcpp::Node node_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace test_rclcpp_components
|
||||
|
||||
#include "rclcpp_components/register_node_macro.hpp"
|
||||
|
||||
// Register the component with class_loader.
|
||||
// This acts as a sort of entry point, allowing the component to be discoverable when its library
|
||||
// is being loaded into a running process.
|
||||
RCLCPP_COMPONENTS_REGISTER_NODE(test_rclcpp_components::TestComponentFoo)
|
||||
RCLCPP_COMPONENTS_REGISTER_NODE(test_rclcpp_components::TestComponentBar)
|
||||
RCLCPP_COMPONENTS_REGISTER_NODE(test_rclcpp_components::TestComponentNoNode)
|
||||
89
rclcpp_components/test/test_component_manager.cpp
Normal file
89
rclcpp_components/test/test_component_manager.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// 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 <memory>
|
||||
|
||||
#include "component_manager.hpp"
|
||||
|
||||
#include "rcpputils/filesystem_helper.hpp"
|
||||
|
||||
class TestComponentManager : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestComponentManager, get_component_resources_invalid)
|
||||
{
|
||||
auto exec = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
auto manager = std::make_shared<rclcpp_components::ComponentManager>(exec);
|
||||
|
||||
EXPECT_THROW(manager->get_component_resources("invalid_package"),
|
||||
rclcpp_components::ComponentManagerException);
|
||||
}
|
||||
|
||||
TEST_F(TestComponentManager, get_component_resources_valid)
|
||||
{
|
||||
auto exec = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
auto manager = std::make_shared<rclcpp_components::ComponentManager>(exec);
|
||||
|
||||
auto resources = manager->get_component_resources("rclcpp_components");
|
||||
EXPECT_EQ(3u, resources.size());
|
||||
|
||||
EXPECT_EQ("test_rclcpp_components::TestComponentFoo", resources[0].first);
|
||||
EXPECT_EQ("test_rclcpp_components::TestComponentBar", resources[1].first);
|
||||
EXPECT_EQ("test_rclcpp_components::TestComponentNoNode", resources[2].first);
|
||||
|
||||
EXPECT_TRUE(rcpputils::fs::path(resources[0].second).exists());
|
||||
EXPECT_TRUE(rcpputils::fs::path(resources[1].second).exists());
|
||||
EXPECT_TRUE(rcpputils::fs::path(resources[2].second).exists());
|
||||
}
|
||||
|
||||
TEST_F(TestComponentManager, create_component_factory_valid)
|
||||
{
|
||||
auto exec = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
auto manager = std::make_shared<rclcpp_components::ComponentManager>(exec);
|
||||
|
||||
auto resources = manager->get_component_resources("rclcpp_components");
|
||||
EXPECT_EQ(3u, resources.size());
|
||||
|
||||
// Repeated loading should reuse existing class loader and not throw.
|
||||
EXPECT_NO_THROW(auto factory = manager->create_component_factory(resources[0]););
|
||||
EXPECT_NO_THROW(auto factory = manager->create_component_factory(resources[0]););
|
||||
|
||||
for (const auto & resource : resources) {
|
||||
auto factory = manager->create_component_factory(resource);
|
||||
EXPECT_NE(nullptr, factory);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestComponentManager, create_component_factory_invalid)
|
||||
{
|
||||
auto exec = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
auto manager = std::make_shared<rclcpp_components::ComponentManager>(exec);
|
||||
|
||||
// Test invalid library
|
||||
EXPECT_THROW(manager->create_component_factory({"foo_class", "invalid_library.so"}),
|
||||
rclcpp_components::ComponentManagerException);
|
||||
|
||||
// Test valid library with invalid class
|
||||
auto resources = manager->get_component_resources("rclcpp_components");
|
||||
auto factory = manager->create_component_factory({"foo_class", resources[0].second});
|
||||
EXPECT_EQ(nullptr, factory);
|
||||
}
|
||||
298
rclcpp_components/test/test_component_manager_api.cpp
Normal file
298
rclcpp_components/test/test_component_manager_api.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
// 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "composition_interfaces/srv/load_node.hpp"
|
||||
#include "composition_interfaces/srv/unload_node.hpp"
|
||||
#include "composition_interfaces/srv/list_nodes.hpp"
|
||||
|
||||
#include "component_manager.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class TestComponentManager : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestComponentManager, load_components)
|
||||
{
|
||||
auto exec = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
auto node = rclcpp::Node::make_shared("test_component_manager");
|
||||
auto manager = std::make_shared<rclcpp_components::ComponentManager>(exec);
|
||||
|
||||
exec->add_node(manager);
|
||||
exec->add_node(node);
|
||||
|
||||
auto client = node->create_client<composition_interfaces::srv::LoadNode>(
|
||||
"/ComponentManager/_container/load_node");
|
||||
|
||||
if (!client->wait_for_service(20s)) {
|
||||
ASSERT_TRUE(false) << "service not available after waiting";
|
||||
}
|
||||
|
||||
{
|
||||
auto request = std::make_shared<composition_interfaces::srv::LoadNode::Request>();
|
||||
request->package_name = "rclcpp_components";
|
||||
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
|
||||
|
||||
auto result = client->async_send_request(request);
|
||||
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
|
||||
EXPECT_EQ(ret, rclcpp::executor::FutureReturnCode::SUCCESS);
|
||||
EXPECT_EQ(result.get()->success, true);
|
||||
EXPECT_EQ(result.get()->error_message, "");
|
||||
EXPECT_EQ(result.get()->full_node_name, "/test_component_foo");
|
||||
EXPECT_EQ(result.get()->unique_id, 1u);
|
||||
}
|
||||
|
||||
{
|
||||
auto request = std::make_shared<composition_interfaces::srv::LoadNode::Request>();
|
||||
request->package_name = "rclcpp_components";
|
||||
request->plugin_name = "test_rclcpp_components::TestComponentBar";
|
||||
|
||||
auto result = client->async_send_request(request);
|
||||
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
|
||||
EXPECT_EQ(ret, rclcpp::executor::FutureReturnCode::SUCCESS);
|
||||
EXPECT_EQ(result.get()->success, true);
|
||||
EXPECT_EQ(result.get()->error_message, "");
|
||||
EXPECT_EQ(result.get()->full_node_name, "/test_component_bar");
|
||||
EXPECT_EQ(result.get()->unique_id, 2u);
|
||||
}
|
||||
|
||||
// Test remapping the node name
|
||||
{
|
||||
auto request = std::make_shared<composition_interfaces::srv::LoadNode::Request>();
|
||||
request->package_name = "rclcpp_components";
|
||||
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
|
||||
request->node_name = "test_component_baz";
|
||||
|
||||
auto result = client->async_send_request(request);
|
||||
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
|
||||
EXPECT_EQ(ret, rclcpp::executor::FutureReturnCode::SUCCESS);
|
||||
EXPECT_EQ(result.get()->success, true);
|
||||
EXPECT_EQ(result.get()->error_message, "");
|
||||
EXPECT_EQ(result.get()->full_node_name, "/test_component_baz");
|
||||
EXPECT_EQ(result.get()->unique_id, 3u);
|
||||
}
|
||||
|
||||
// Test remapping the node namespace
|
||||
{
|
||||
auto request = std::make_shared<composition_interfaces::srv::LoadNode::Request>();
|
||||
request->package_name = "rclcpp_components";
|
||||
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
|
||||
request->node_namespace = "/ns";
|
||||
request->node_name = "test_component_bing";
|
||||
|
||||
auto result = client->async_send_request(request);
|
||||
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
|
||||
EXPECT_EQ(ret, rclcpp::executor::FutureReturnCode::SUCCESS);
|
||||
EXPECT_EQ(result.get()->success, true);
|
||||
EXPECT_EQ(result.get()->error_message, "");
|
||||
EXPECT_EQ(result.get()->full_node_name, "/ns/test_component_bing");
|
||||
EXPECT_EQ(result.get()->unique_id, 4u);
|
||||
}
|
||||
|
||||
auto node_names = node->get_node_names();
|
||||
|
||||
auto find_in_nodes = [node_names](std::string name) {
|
||||
return std::find(node_names.begin(), node_names.end(), name) != node_names.end();
|
||||
};
|
||||
|
||||
EXPECT_TRUE(find_in_nodes("test_component_foo"));
|
||||
EXPECT_TRUE(find_in_nodes("test_component_bar"));
|
||||
EXPECT_TRUE(find_in_nodes("test_component_baz"));
|
||||
EXPECT_TRUE(find_in_nodes("test_component_bing"));
|
||||
}
|
||||
|
||||
TEST_F(TestComponentManager, load_invalid_components)
|
||||
{
|
||||
auto exec = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
auto node = rclcpp::Node::make_shared("test_component_manager");
|
||||
auto manager = std::make_shared<rclcpp_components::ComponentManager>(exec);
|
||||
|
||||
exec->add_node(manager);
|
||||
exec->add_node(node);
|
||||
|
||||
auto client = node->create_client<composition_interfaces::srv::LoadNode>(
|
||||
"/ComponentManager/_container/load_node");
|
||||
|
||||
if (!client->wait_for_service(20s)) {
|
||||
ASSERT_TRUE(false) << "service not available after waiting";
|
||||
}
|
||||
|
||||
{
|
||||
// Valid package, but invalid class name.
|
||||
auto request = std::make_shared<composition_interfaces::srv::LoadNode::Request>();
|
||||
request->package_name = "rclcpp_components";
|
||||
request->plugin_name = "test_rclcpp_components::TestComponent";
|
||||
|
||||
auto result = client->async_send_request(request);
|
||||
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
|
||||
EXPECT_EQ(ret, rclcpp::executor::FutureReturnCode::SUCCESS);
|
||||
EXPECT_EQ(result.get()->success, false);
|
||||
EXPECT_EQ(result.get()->error_message, "Failed to find class with the requested plugin name.");
|
||||
EXPECT_EQ(result.get()->full_node_name, "");
|
||||
EXPECT_EQ(result.get()->unique_id, 0u);
|
||||
}
|
||||
|
||||
{
|
||||
// Invalid package, but valid class name.
|
||||
auto request = std::make_shared<composition_interfaces::srv::LoadNode::Request>();
|
||||
request->package_name = "rclcpp_components_foo";
|
||||
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
|
||||
|
||||
auto result = client->async_send_request(request);
|
||||
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
|
||||
EXPECT_EQ(ret, rclcpp::executor::FutureReturnCode::SUCCESS);
|
||||
EXPECT_EQ(result.get()->success, false);
|
||||
EXPECT_EQ(result.get()->error_message, "Could not find requested resource in ament index");
|
||||
EXPECT_EQ(result.get()->full_node_name, "");
|
||||
EXPECT_EQ(result.get()->unique_id, 0u);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TestComponentManager, list_components)
|
||||
{
|
||||
auto exec = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
auto node = rclcpp::Node::make_shared("test_component_manager");
|
||||
auto manager = std::make_shared<rclcpp_components::ComponentManager>(exec);
|
||||
|
||||
exec->add_node(manager);
|
||||
exec->add_node(node);
|
||||
|
||||
{
|
||||
auto client = node->create_client<composition_interfaces::srv::LoadNode>(
|
||||
"/ComponentManager/_container/load_node");
|
||||
|
||||
if (!client->wait_for_service(20s)) {
|
||||
ASSERT_TRUE(false) << "service not available after waiting";
|
||||
}
|
||||
|
||||
{
|
||||
auto request = std::make_shared<composition_interfaces::srv::LoadNode::Request>();
|
||||
request->package_name = "rclcpp_components";
|
||||
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
|
||||
|
||||
auto result = client->async_send_request(request);
|
||||
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
|
||||
EXPECT_EQ(ret, rclcpp::executor::FutureReturnCode::SUCCESS);
|
||||
EXPECT_EQ(result.get()->success, true);
|
||||
EXPECT_EQ(result.get()->error_message, "");
|
||||
EXPECT_EQ(result.get()->full_node_name, "/test_component_foo");
|
||||
EXPECT_EQ(result.get()->unique_id, 1u);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto client = node->create_client<composition_interfaces::srv::ListNodes>(
|
||||
"/ComponentManager/_container/list_nodes");
|
||||
|
||||
if (!client->wait_for_service(20s)) {
|
||||
ASSERT_TRUE(false) << "service not available after waiting";
|
||||
}
|
||||
|
||||
{
|
||||
auto request = std::make_shared<composition_interfaces::srv::ListNodes::Request>();
|
||||
auto result = client->async_send_request(request);
|
||||
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
|
||||
EXPECT_EQ(ret, rclcpp::executor::FutureReturnCode::SUCCESS);
|
||||
auto node_names = result.get()->full_node_names;
|
||||
auto unique_ids = result.get()->unique_ids;
|
||||
|
||||
EXPECT_EQ(node_names.size(), 1u);
|
||||
EXPECT_EQ(node_names[0], "/test_component_foo");
|
||||
EXPECT_EQ(unique_ids.size(), 1u);
|
||||
EXPECT_EQ(unique_ids[0], 1u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestComponentManager, unload_component)
|
||||
{
|
||||
auto exec = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
auto node = rclcpp::Node::make_shared("test_component_manager");
|
||||
auto manager = std::make_shared<rclcpp_components::ComponentManager>(exec);
|
||||
|
||||
exec->add_node(manager);
|
||||
exec->add_node(node);
|
||||
|
||||
{
|
||||
auto client = node->create_client<composition_interfaces::srv::LoadNode>(
|
||||
"/ComponentManager/_container/load_node");
|
||||
|
||||
if (!client->wait_for_service(20s)) {
|
||||
ASSERT_TRUE(false) << "service not available after waiting";
|
||||
}
|
||||
|
||||
{
|
||||
auto request = std::make_shared<composition_interfaces::srv::LoadNode::Request>();
|
||||
request->package_name = "rclcpp_components";
|
||||
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
|
||||
|
||||
auto result = client->async_send_request(request);
|
||||
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
|
||||
EXPECT_EQ(ret, rclcpp::executor::FutureReturnCode::SUCCESS);
|
||||
EXPECT_EQ(result.get()->success, true);
|
||||
EXPECT_EQ(result.get()->error_message, "");
|
||||
EXPECT_EQ(result.get()->full_node_name, "/test_component_foo");
|
||||
EXPECT_EQ(result.get()->unique_id, 1u);
|
||||
}
|
||||
}
|
||||
|
||||
auto node_names = node->get_node_names();
|
||||
auto find_in_nodes = [node_names](std::string name) {
|
||||
return std::find(node_names.begin(), node_names.end(), name) != node_names.end();
|
||||
};
|
||||
EXPECT_TRUE(find_in_nodes("test_component_foo"));
|
||||
|
||||
{
|
||||
auto client = node->create_client<composition_interfaces::srv::UnloadNode>(
|
||||
"/ComponentManager/_container/unload_node");
|
||||
|
||||
if (!client->wait_for_service(20s)) {
|
||||
ASSERT_TRUE(false) << "service not available after waiting";
|
||||
}
|
||||
|
||||
{
|
||||
auto request = std::make_shared<composition_interfaces::srv::UnloadNode::Request>();
|
||||
request->unique_id = 1;
|
||||
|
||||
auto result = client->async_send_request(request);
|
||||
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
|
||||
EXPECT_EQ(ret, rclcpp::executor::FutureReturnCode::SUCCESS);
|
||||
EXPECT_EQ(result.get()->success, true);
|
||||
EXPECT_EQ(result.get()->error_message, "");
|
||||
}
|
||||
|
||||
{
|
||||
auto request = std::make_shared<composition_interfaces::srv::UnloadNode::Request>();
|
||||
request->unique_id = 1;
|
||||
|
||||
auto result = client->async_send_request(request);
|
||||
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
|
||||
EXPECT_EQ(ret, rclcpp::executor::FutureReturnCode::SUCCESS);
|
||||
EXPECT_EQ(result.get()->success, false);
|
||||
EXPECT_EQ(result.get()->error_message, "No node found with unique_id: 1");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,19 @@
|
||||
Changelog for package rclcpp_lifecycle
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
0.7.0 (2019-04-14)
|
||||
------------------
|
||||
* Fixed linter errors in rclcpp_lifecycle. (`#672 <https://github.com/ros2/rclcpp/issues/672>`_)
|
||||
* Added parameter-related templates to LifecycleNode. (`#645 <https://github.com/ros2/rclcpp/issues/645>`_)
|
||||
* Fixed use_sim_time issue on LifeCycleNode. (`#651 <https://github.com/ros2/rclcpp/issues/651>`_)
|
||||
* Updated to use ament_target_dependencies where possible. (`#659 <https://github.com/ros2/rclcpp/issues/659>`_)
|
||||
* Fixed hard-coded duration type representation so int64_t isn't assumed. (`#648 <https://github.com/ros2/rclcpp/issues/648>`_)
|
||||
* Added a method to the LifecycleNode class to get the logging interface. (`#652 <https://github.com/ros2/rclcpp/issues/652>`_)
|
||||
* 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>`_)
|
||||
* Removed dependency on rclpy. (`#626 <https://github.com/ros2/rclcpp/issues/626>`_)
|
||||
* Contributors: Emerson Knapp, Karsten Knese, Michael Carroll, Michael Jeronimo, Vinnam Kim, William Woodall, ivanpauno, rarvolt
|
||||
|
||||
0.6.2 (2018-12-13)
|
||||
------------------
|
||||
|
||||
|
||||
@@ -49,49 +49,49 @@ if(BUILD_TESTING)
|
||||
|
||||
ament_add_gtest(test_lifecycle_node test/test_lifecycle_node.cpp)
|
||||
if(TARGET test_lifecycle_node)
|
||||
target_include_directories(test_lifecycle_node PUBLIC
|
||||
${rcl_lifecycle_INCLUDE_DIRS}
|
||||
${rclcpp_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_lifecycle_node
|
||||
"rcl_lifecycle"
|
||||
"rclcpp"
|
||||
)
|
||||
target_link_libraries(test_lifecycle_node ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_state_machine_info test/test_state_machine_info.cpp)
|
||||
if(TARGET test_state_machine_info)
|
||||
target_include_directories(test_state_machine_info PUBLIC
|
||||
${rcl_lifecycle_INCLUDE_DIRS}
|
||||
${rclcpp_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_state_machine_info
|
||||
"rcl_lifecycle"
|
||||
"rclcpp"
|
||||
)
|
||||
target_link_libraries(test_state_machine_info ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_register_custom_callbacks test/test_register_custom_callbacks.cpp)
|
||||
if(TARGET test_register_custom_callbacks)
|
||||
target_include_directories(test_register_custom_callbacks PUBLIC
|
||||
${rcl_lifecycle_INCLUDE_DIRS}
|
||||
${rclcpp_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_register_custom_callbacks
|
||||
"rcl_lifecycle"
|
||||
"rclcpp"
|
||||
)
|
||||
target_link_libraries(test_register_custom_callbacks ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_callback_exceptions test/test_callback_exceptions.cpp)
|
||||
if(TARGET test_callback_exceptions)
|
||||
target_include_directories(test_callback_exceptions PUBLIC
|
||||
${rcl_lifecycle_INCLUDE_DIRS}
|
||||
${rclcpp_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_callback_exceptions
|
||||
"rcl_lifecycle"
|
||||
"rclcpp"
|
||||
)
|
||||
target_link_libraries(test_callback_exceptions ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_state_wrapper test/test_state_wrapper.cpp)
|
||||
if(TARGET test_state_wrapper)
|
||||
target_include_directories(test_state_wrapper PUBLIC
|
||||
${rcl_lifecycle_INCLUDE_DIRS}
|
||||
${rclcpp_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_state_wrapper
|
||||
"rcl_lifecycle"
|
||||
"rclcpp"
|
||||
)
|
||||
target_link_libraries(test_state_wrapper ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_transition_wrapper test/test_transition_wrapper.cpp)
|
||||
if(TARGET test_transition_wrapper)
|
||||
target_include_directories(test_transition_wrapper PUBLIC
|
||||
${rcl_lifecycle_INCLUDE_DIRS}
|
||||
${rclcpp_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_transition_wrapper
|
||||
"rcl_lifecycle"
|
||||
"rclcpp"
|
||||
)
|
||||
target_link_libraries(test_transition_wrapper ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
@@ -36,12 +36,14 @@
|
||||
#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"
|
||||
@@ -75,39 +77,25 @@ public:
|
||||
/**
|
||||
* \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_LIFECYCLE_PUBLIC
|
||||
explicit LifecycleNode(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_ = "",
|
||||
bool use_intra_process_comms = false);
|
||||
const rclcpp::NodeOptions & options = rclcpp::NodeOptions());
|
||||
|
||||
/// Create a node based on the node name and a rclcpp::Context.
|
||||
/**
|
||||
* \param[in] node_name Name of the node.
|
||||
* \param[in] namespace_ Namespace of the node.
|
||||
* \param[in] context The context for the node (usually represents the state of a process).
|
||||
* \param[in] 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_LIFECYCLE_PUBLIC
|
||||
LifecycleNode(
|
||||
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 = true,
|
||||
bool use_intra_process_comms = false,
|
||||
bool start_parameter_services = true);
|
||||
const rclcpp::NodeOptions & options);
|
||||
|
||||
RCLCPP_LIFECYCLE_PUBLIC
|
||||
virtual ~LifecycleNode();
|
||||
@@ -235,10 +223,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);
|
||||
|
||||
@@ -267,6 +255,26 @@ public:
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
set_parameters_atomically(const std::vector<rclcpp::Parameter> & parameters);
|
||||
|
||||
template<typename ParameterT>
|
||||
void
|
||||
set_parameter_if_not_set(
|
||||
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_LIFECYCLE_PUBLIC
|
||||
std::vector<rclcpp::Parameter>
|
||||
get_parameters(const std::vector<std::string> & names) const;
|
||||
@@ -285,6 +293,49 @@ 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;
|
||||
|
||||
template<typename ParameterT>
|
||||
bool
|
||||
get_parameter_or(
|
||||
const std::string & name,
|
||||
ParameterT & value,
|
||||
const ParameterT & alternative_value) const;
|
||||
|
||||
/// Get the parameter value; if not set, set the "alternative value" and store it in the node.
|
||||
/**
|
||||
* If the parameter is set, then the "value" argument is assigned the value
|
||||
* in the parameter.
|
||||
* If the parameter is not set, then the "value" argument is assigned the "alternative_value",
|
||||
* and the parameter is set to the "alternative_value" on the node.
|
||||
*
|
||||
* \param[in] name The name of the parameter to get.
|
||||
* \param[out] value The output where the value of the parameter should be assigned.
|
||||
* \param[in] alternative_value Value to be stored in output and parameter if the parameter was not set.
|
||||
*/
|
||||
template<typename ParameterT>
|
||||
void
|
||||
get_parameter_or_set(
|
||||
const std::string & name,
|
||||
ParameterT & value,
|
||||
const ParameterT & alternative_value);
|
||||
|
||||
RCLCPP_LIFECYCLE_PUBLIC
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor>
|
||||
describe_parameters(const std::vector<std::string> & names) const;
|
||||
@@ -371,6 +422,11 @@ public:
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr
|
||||
get_node_graph_interface();
|
||||
|
||||
/// Return the Node's internal NodeLoggingInterface implementation.
|
||||
RCLCPP_LIFECYCLE_PUBLIC
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr
|
||||
get_node_logging_interface();
|
||||
|
||||
/// Return the Node's internal NodeTimersInterface implementation.
|
||||
RCLCPP_LIFECYCLE_PUBLIC
|
||||
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
|
||||
@@ -391,6 +447,11 @@ public:
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
|
||||
get_node_parameters_interface();
|
||||
|
||||
/// Return the Node's internal NodeParametersInterface implementation.
|
||||
RCLCPP_LIFECYCLE_PUBLIC
|
||||
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr
|
||||
get_node_time_source_interface();
|
||||
|
||||
/// Return the Node's internal NodeWaitablesInterface implementation.
|
||||
RCLCPP_LIFECYCLE_PUBLIC
|
||||
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr
|
||||
@@ -521,6 +582,7 @@ 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_;
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
#ifndef RCLCPP_LIFECYCLE__LIFECYCLE_NODE_IMPL_HPP_
|
||||
#define RCLCPP_LIFECYCLE__LIFECYCLE_NODE_IMPL_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/intra_process_manager.hpp"
|
||||
@@ -132,10 +134,10 @@ LifecycleNode::create_subscription(
|
||||
allocator);
|
||||
}
|
||||
|
||||
template<typename DurationT, typename CallbackT>
|
||||
template<typename DurationRepT, typename DurationT, typename CallbackT>
|
||||
typename rclcpp::WallTimer<CallbackT>::SharedPtr
|
||||
LifecycleNode::create_wall_timer(
|
||||
std::chrono::duration<int64_t, DurationT> period,
|
||||
std::chrono::duration<DurationRepT, DurationT> period,
|
||||
CallbackT callback,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
@@ -201,5 +203,91 @@ LifecycleNode::register_param_change_callback(CallbackT && callback)
|
||||
this->node_parameters_->register_param_change_callback(std::forward<CallbackT>(callback));
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
void
|
||||
LifecycleNode::set_parameter_if_not_set(
|
||||
const std::string & name,
|
||||
const ParameterT & value)
|
||||
{
|
||||
rclcpp::Parameter parameter;
|
||||
if (!this->get_parameter(name, parameter)) {
|
||||
this->set_parameters({
|
||||
rclcpp::Parameter(name, 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
|
||||
LifecycleNode::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);
|
||||
}
|
||||
|
||||
// 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
|
||||
LifecycleNode::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
|
||||
LifecycleNode::get_parameter_or(
|
||||
const std::string & name,
|
||||
ParameterT & value,
|
||||
const ParameterT & alternative_value) const
|
||||
{
|
||||
bool got_parameter = get_parameter(name, value);
|
||||
if (!got_parameter) {
|
||||
value = alternative_value;
|
||||
}
|
||||
return got_parameter;
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
void
|
||||
LifecycleNode::get_parameter_or_set(
|
||||
const std::string & name,
|
||||
ParameterT & value,
|
||||
const ParameterT & alternative_value)
|
||||
{
|
||||
bool got_parameter = get_parameter(name, value);
|
||||
if (!got_parameter) {
|
||||
this->set_parameters({
|
||||
rclcpp::Parameter(name, alternative_value),
|
||||
});
|
||||
value = alternative_value;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rclcpp_lifecycle
|
||||
#endif // RCLCPP_LIFECYCLE__LIFECYCLE_NODE_IMPL_HPP_
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp_lifecycle</name>
|
||||
<version>0.6.2</version>
|
||||
<version>0.7.0</version>
|
||||
<description>Package containing a prototype for lifecycle implementation</description>
|
||||
<maintainer email="karsten@osrfoundation.org">Karsten Knese</maintainer>
|
||||
<license>Apache License 2.0</license>
|
||||
@@ -18,7 +18,6 @@
|
||||
<exec_depend>lifecycle_msgs</exec_depend>
|
||||
<exec_depend>rclcpp</exec_depend>
|
||||
<exec_depend>rcl_lifecycle</exec_depend>
|
||||
<exec_depend>rclpy</exec_depend>
|
||||
<exec_depend>rmw_implementation</exec_depend>
|
||||
<exec_depend>rosidl_typesupport_cpp</exec_depend>
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#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"
|
||||
@@ -45,30 +46,19 @@ namespace rclcpp_lifecycle
|
||||
|
||||
LifecycleNode::LifecycleNode(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool use_intra_process_comms)
|
||||
const rclcpp::NodeOptions & options)
|
||||
: LifecycleNode(
|
||||
node_name,
|
||||
namespace_,
|
||||
rclcpp::contexts::default_context::get_global_default_context(),
|
||||
{},
|
||||
{},
|
||||
true,
|
||||
use_intra_process_comms,
|
||||
true)
|
||||
"",
|
||||
options)
|
||||
{}
|
||||
|
||||
LifecycleNode::LifecycleNode(
|
||||
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 rclcpp::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())),
|
||||
@@ -86,12 +76,23 @@ LifecycleNode::LifecycleNode(
|
||||
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),
|
||||
use_intra_process_comms_(options.use_intra_process_comms()),
|
||||
impl_(new LifecycleNodeInterfaceImpl(node_base_, node_services_))
|
||||
{
|
||||
impl_->init();
|
||||
@@ -277,6 +278,18 @@ LifecycleNode::get_node_graph_interface()
|
||||
return node_graph_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr
|
||||
LifecycleNode::get_node_logging_interface()
|
||||
{
|
||||
return node_logging_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr
|
||||
LifecycleNode::get_node_time_source_interface()
|
||||
{
|
||||
return node_time_source_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
|
||||
LifecycleNode::get_node_timers_interface()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user