Compare commits
58 Commits
check_para
...
node_inter
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6dd86d8d8 | ||
|
|
ef41059a75 | ||
|
|
cfeb6a6360 | ||
|
|
c769b1b030 | ||
|
|
385cccc2cc | ||
|
|
d399fef9c6 | ||
|
|
ecf35114b6 | ||
|
|
7ed130cf7a | ||
|
|
59d59b0c18 | ||
|
|
a8a0788f81 | ||
|
|
e9101b49cd | ||
|
|
078d5ff662 | ||
|
|
dc05a2e755 | ||
|
|
98f610c114 | ||
|
|
d34fa607a2 | ||
|
|
02050c3901 | ||
|
|
1a0f8e3f28 | ||
|
|
0da966b981 | ||
|
|
6b10841477 | ||
|
|
97ed34a042 | ||
|
|
ddf4d345b3 | ||
|
|
ddcc1ec553 | ||
|
|
60996d1e59 | ||
|
|
713dd0c184 | ||
|
|
68d0ac1e61 | ||
|
|
70f48d68b9 | ||
|
|
fcfe94e404 | ||
|
|
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,72 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
0.7.2 (2019-05-08)
|
||||
------------------
|
||||
* Added new way to specify QoS settings for publishers and subscriptions. (`#713 <https://github.com/ros2/rclcpp/issues/713>`_)
|
||||
* The new way requires that you specify a history depth when creating a publisher or subscription.
|
||||
* In the past it was possible to create one without specifying any history depth, but these signatures have been deprecated.
|
||||
* Deprecated ``shared_ptr`` and raw pointer versions of ``Publisher<T>::publish()``. (`#709 <https://github.com/ros2/rclcpp/issues/709>`_)
|
||||
* Implemented API to set callbacks for liveliness and deadline QoS events for publishers and subscriptions. (`#695 <https://github.com/ros2/rclcpp/issues/695>`_)
|
||||
* Fixed a segmentation fault when publishing a parameter event when they ought to be disabled. (`#714 <https://github.com/ros2/rclcpp/issues/714>`_)
|
||||
* Changes required for upcoming pre-allocation API. (`#711 <https://github.com/ros2/rclcpp/issues/711>`_)
|
||||
* Changed ``Node::get_node_names()`` to return the full node names rather than just the base name. (`#698 <https://github.com/ros2/rclcpp/issues/698>`_)
|
||||
* Remove logic made redundant by the `ros2/rcl#255 <https://github.com/ros2/rcl/issues/255>`_ pull request. (`#712 <https://github.com/ros2/rclcpp/issues/712>`_)
|
||||
* Various improvements for ``rclcpp::Clock``. (`#696 <https://github.com/ros2/rclcpp/issues/696>`_)
|
||||
* Fixed uninitialized bool in ``clock.cpp``.
|
||||
* Fixed up includes of ``clock.hpp/cpp``.
|
||||
* Added documentation for exceptions to ``clock.hpp``.
|
||||
* Adjusted function signature of getters of ``clock.hpp/cpp``.
|
||||
* Removed raw pointers to ``Clock::create_jump_callback``.
|
||||
* Removed unnecessary ``rclcpp`` namespace reference from ``clock.cpp``.
|
||||
* Changed exception to ``bad_alloc`` on ``JumpHandler`` allocation failure.
|
||||
* Fixed missing ``nullptr`` check in ``Clock::on_time_jump``.
|
||||
* Added ``JumpHandler::callback`` types.
|
||||
* Added warning for lifetime of Clock and JumpHandler
|
||||
* Fixed bug left over from the `pull request #495 <https://github.com/ros2/rclcpp/pull/495>`_. (`#708 <https://github.com/ros2/rclcpp/issues/708>`_)
|
||||
* Changed the ``IntraProcessManager`` to be capable of storing ``shared_ptr<const T>`` in addition to ``unique_ptr<T>``. (`#690 <https://github.com/ros2/rclcpp/issues/690>`_)
|
||||
* Contributors: Alberto Soragna, Dima Dorezyuk, M. M, Michael Carroll, Michael Jeronimo, Tully Foote, William Woodall, ivanpauno, jhdcs
|
||||
|
||||
0.7.1 (2019-04-26)
|
||||
------------------
|
||||
* Added read only parameters. (`#495 <https://github.com/ros2/rclcpp/issues/495>`_)
|
||||
* Fixed a concurrency problem in the multithreaded executor. (`#703 <https://github.com/ros2/rclcpp/issues/703>`_)
|
||||
* Fixup utilities. (`#692 <https://github.com/ros2/rclcpp/issues/692>`_)
|
||||
* Added method to read timer cancellation. (`#697 <https://github.com/ros2/rclcpp/issues/697>`_)
|
||||
* Added Exception Generator function for implementing "from_rcl_error". (`#678 <https://github.com/ros2/rclcpp/issues/678>`_)
|
||||
* Updated initialization of rmw_qos_profile_t struct instances. (`#691 <https://github.com/ros2/rclcpp/issues/691>`_)
|
||||
* Removed the const value from the logger before comparison. (`#680 <https://github.com/ros2/rclcpp/issues/680>`_)
|
||||
* Contributors: Devin Bonnie, Dima Dorezyuk, Guillaume Autran, M. M, Shane Loretz, Víctor Mayoral Vilches, William Woodall, jhdcs
|
||||
|
||||
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
|
||||
@@ -63,10 +64,12 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/parameter_events_filter.cpp
|
||||
src/rclcpp/parameter_map.cpp
|
||||
src/rclcpp/parameter_service.cpp
|
||||
src/rclcpp/publisher.cpp
|
||||
src/rclcpp/publisher_base.cpp
|
||||
src/rclcpp/qos.cpp
|
||||
src/rclcpp/qos_event.cpp
|
||||
src/rclcpp/service.cpp
|
||||
src/rclcpp/signal_handler.cpp
|
||||
src/rclcpp/subscription.cpp
|
||||
src/rclcpp/subscription_base.cpp
|
||||
src/rclcpp/time.cpp
|
||||
src/rclcpp/time_source.cpp
|
||||
src/rclcpp/timer.cpp
|
||||
@@ -82,19 +85,40 @@ configure_file(
|
||||
COPYONLY
|
||||
)
|
||||
# generate header with logging macros
|
||||
set(python_code
|
||||
set(python_code_logging
|
||||
"import em"
|
||||
"em.invoke(['-o', 'include/rclcpp/logging.hpp', '${CMAKE_CURRENT_SOURCE_DIR}/resource/logging.hpp.em'])")
|
||||
string(REPLACE ";" "$<SEMICOLON>" python_code "${python_code}")
|
||||
string(REPLACE ";" "$<SEMICOLON>" python_code_logging "${python_code_logging}")
|
||||
add_custom_command(OUTPUT include/rclcpp/logging.hpp
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "include/rclcpp"
|
||||
COMMAND ${PYTHON_EXECUTABLE} ARGS -c "${python_code}"
|
||||
COMMAND ${PYTHON_EXECUTABLE} ARGS -c "${python_code_logging}"
|
||||
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/logging.hpp.em.watch"
|
||||
COMMENT "Expanding logging.hpp.em"
|
||||
VERBATIM
|
||||
)
|
||||
list(APPEND ${PROJECT_NAME}_SRCS
|
||||
include/rclcpp/logging.hpp)
|
||||
|
||||
# "watch" template for changes
|
||||
configure_file(
|
||||
"resource/interface_traits.hpp.em"
|
||||
"interface_traits.hpp.em.watch"
|
||||
COPYONLY
|
||||
)
|
||||
set(python_code_interface_traits
|
||||
"import em"
|
||||
"em.invoke(['-o', 'include/rclcpp/node_interfaces/interface_traits.hpp', '${CMAKE_CURRENT_SOURCE_DIR}/resource/interface_traits.hpp.em'])")
|
||||
string(REPLACE ";" "$<SEMICOLON>" python_code_interface_traits "${python_code_interface_traits}")
|
||||
add_custom_command(OUTPUT include/rclcpp/node_interfaces/interface_traits.hpp
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "include/rclcpp/node_interfaces"
|
||||
COMMAND ${PYTHON_EXECUTABLE} ARGS -c "${python_code_interface_traits}"
|
||||
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/interface_traits.hpp.em.watch"
|
||||
COMMENT "Expanding interfae_traits.hpp.em"
|
||||
VERBATIM
|
||||
)
|
||||
list(APPEND ${PROJECT_NAME}_SRCS
|
||||
include/rclcpp/node_interfaces/interface_traits.hpp)
|
||||
|
||||
include_directories("${CMAKE_CURRENT_BINARY_DIR}/include")
|
||||
|
||||
add_library(${PROJECT_NAME}
|
||||
@@ -140,96 +164,122 @@ if(BUILD_TESTING)
|
||||
|
||||
find_package(rmw_implementation_cmake REQUIRED)
|
||||
|
||||
include(cmake/rclcpp_add_build_failure_test.cmake)
|
||||
|
||||
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_interfaces__get_node_interfaces
|
||||
test/node_interfaces/test_get_node_interfaces.cpp)
|
||||
if(TARGET test_node_interfaces__get_node_interfaces)
|
||||
target_link_libraries(test_node_interfaces__get_node_interfaces ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
# TODO(wjwwood): reenable these build failure tests when I can get Jenkins to ignore their output
|
||||
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ref_rclcpp_node
|
||||
# test/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ref_rclcpp_node.cpp)
|
||||
# target_link_libraries(build_failure__get_node_topics_interface_const_ref_rclcpp_node
|
||||
# ${PROJECT_NAME})
|
||||
|
||||
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
|
||||
# test/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ptr_rclcpp_node.cpp)
|
||||
# target_link_libraries(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
|
||||
# ${PROJECT_NAME})
|
||||
|
||||
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ref_wrapped_node
|
||||
# test/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ref_wrapped_node.cpp)
|
||||
# target_link_libraries(build_failure__get_node_topics_interface_const_ref_rclcpp_node
|
||||
# ${PROJECT_NAME})
|
||||
|
||||
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ptr_wrapped_node
|
||||
# test/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ptr_wrapped_node.cpp)
|
||||
# target_link_libraries(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
|
||||
# ${PROJECT_NAME})
|
||||
|
||||
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()
|
||||
ament_add_gtest(test_node_initial_parameters test/test_node_initial_parameters.cpp)
|
||||
if(TARGET test_node_initial_parameters)
|
||||
target_link_libraries(test_node_initial_parameters ${PROJECT_NAME})
|
||||
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 +289,31 @@ 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_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 +321,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 +393,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})
|
||||
@@ -370,6 +437,14 @@ if(BUILD_TESTING)
|
||||
target_link_libraries(test_time ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_timer test/test_timer.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_timer)
|
||||
ament_target_dependencies(test_timer
|
||||
"rcl")
|
||||
target_link_libraries(test_timer ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_time_source test/test_time_source.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_time_source)
|
||||
@@ -394,6 +469,14 @@ if(BUILD_TESTING)
|
||||
target_link_libraries(test_init ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_interface_traits test/test_interface_traits.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_interface_traits)
|
||||
ament_target_dependencies(test_interface_traits
|
||||
"rcl")
|
||||
target_link_libraries(test_interface_traits ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_multi_threaded_executor test/executors/test_multi_threaded_executor.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_multi_threaded_executor)
|
||||
@@ -404,24 +487,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/
|
||||
|
||||
56
rclcpp/cmake/rclcpp_add_build_failure_test.cmake
Normal file
56
rclcpp/cmake/rclcpp_add_build_failure_test.cmake
Normal file
@@ -0,0 +1,56 @@
|
||||
# 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.
|
||||
|
||||
#
|
||||
# Register a test which tries to compile a file and expects it to fail to build.
|
||||
#
|
||||
# This will create two targets, one by the given target name and a test target
|
||||
# which has the same name prefixed with `test_`.
|
||||
# For example, if target is `should_not_compile__use_const_argument` then there
|
||||
# will be an executable target called `should_not_compile__use_const_argument`
|
||||
# and a test target called `test_should_not_compile__use_const_argument`.
|
||||
#
|
||||
# :param target: the name of the target to be created
|
||||
# :type target: string
|
||||
# :param ARGN: the list of source files to be used to create the test executable
|
||||
# :type ARGN: list of strings
|
||||
#
|
||||
macro(rclcpp_add_build_failure_test target)
|
||||
if(${ARGC} EQUAL 0)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"rclcpp_add_build_failure_test() requires a target name and "
|
||||
"at least one source file")
|
||||
endif()
|
||||
|
||||
add_executable(${target} ${ARGN})
|
||||
set_target_properties(${target}
|
||||
PROPERTIES
|
||||
EXCLUDE_FROM_ALL TRUE
|
||||
EXCLUDE_FROM_DEFAULT_BUILD TRUE)
|
||||
|
||||
add_test(
|
||||
NAME test_${target}
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
--build .
|
||||
--target ${target}
|
||||
--config $<CONFIGURATION>
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_tests_properties(test_${target}
|
||||
PROPERTIES
|
||||
WILL_FAIL TRUE
|
||||
LABELS "build_failure"
|
||||
)
|
||||
endmacro()
|
||||
@@ -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()
|
||||
@@ -65,7 +65,8 @@ void * retyped_reallocate(void * untyped_pointer, size_t size, void * untyped_al
|
||||
|
||||
// Convert a std::allocator_traits-formatted Allocator into an rcl allocator
|
||||
template<
|
||||
typename T, typename Alloc,
|
||||
typename T,
|
||||
typename Alloc,
|
||||
typename std::enable_if<!std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
|
||||
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
|
||||
{
|
||||
@@ -83,7 +84,8 @@ rcl_allocator_t get_rcl_allocator(Alloc & allocator)
|
||||
|
||||
// TODO(jacquelinekay) Workaround for an incomplete implementation of std::allocator<void>
|
||||
template<
|
||||
typename T, typename Alloc,
|
||||
typename T,
|
||||
typename Alloc,
|
||||
typename std::enable_if<std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
|
||||
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
|
||||
{
|
||||
|
||||
@@ -36,6 +36,7 @@ class AnySubscriptionCallback
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAlloc = typename MessageAllocTraits::allocator_type;
|
||||
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
|
||||
|
||||
using SharedPtrCallback = std::function<void (const std::shared_ptr<MessageT>)>;
|
||||
@@ -154,7 +155,6 @@ public:
|
||||
void dispatch(
|
||||
std::shared_ptr<MessageT> message, const rmw_message_info_t & message_info)
|
||||
{
|
||||
(void)message_info;
|
||||
if (shared_ptr_callback_) {
|
||||
shared_ptr_callback_(message);
|
||||
} else if (shared_ptr_with_info_callback_) {
|
||||
@@ -177,30 +177,50 @@ public:
|
||||
}
|
||||
|
||||
void dispatch_intra_process(
|
||||
MessageUniquePtr & message, const rmw_message_info_t & message_info)
|
||||
ConstMessageSharedPtr message, const rmw_message_info_t & message_info)
|
||||
{
|
||||
if (const_shared_ptr_callback_) {
|
||||
const_shared_ptr_callback_(message);
|
||||
} else if (const_shared_ptr_with_info_callback_) {
|
||||
const_shared_ptr_with_info_callback_(message, message_info);
|
||||
} else {
|
||||
if (unique_ptr_callback_ || unique_ptr_with_info_callback_ ||
|
||||
shared_ptr_callback_ || shared_ptr_with_info_callback_)
|
||||
{
|
||||
throw std::runtime_error("unexpected dispatch_intra_process const shared "
|
||||
"message call with no const shared_ptr callback");
|
||||
} else {
|
||||
throw std::runtime_error("unexpected message without any callback set");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dispatch_intra_process(
|
||||
MessageUniquePtr message, const rmw_message_info_t & message_info)
|
||||
{
|
||||
(void)message_info;
|
||||
if (shared_ptr_callback_) {
|
||||
typename std::shared_ptr<MessageT> shared_message = std::move(message);
|
||||
shared_ptr_callback_(shared_message);
|
||||
} else if (shared_ptr_with_info_callback_) {
|
||||
typename std::shared_ptr<MessageT> shared_message = std::move(message);
|
||||
shared_ptr_with_info_callback_(shared_message, message_info);
|
||||
} else if (const_shared_ptr_callback_) {
|
||||
typename std::shared_ptr<MessageT const> const_shared_message = std::move(message);
|
||||
const_shared_ptr_callback_(const_shared_message);
|
||||
} else if (const_shared_ptr_with_info_callback_) {
|
||||
typename std::shared_ptr<MessageT const> const_shared_message = std::move(message);
|
||||
const_shared_ptr_with_info_callback_(const_shared_message, message_info);
|
||||
} else if (unique_ptr_callback_) {
|
||||
unique_ptr_callback_(std::move(message));
|
||||
} else if (unique_ptr_with_info_callback_) {
|
||||
unique_ptr_with_info_callback_(std::move(message), message_info);
|
||||
} else if (const_shared_ptr_callback_ || const_shared_ptr_with_info_callback_) {
|
||||
throw std::runtime_error("unexpected dispatch_intra_process unique message call"
|
||||
" with const shared_ptr callback");
|
||||
} else {
|
||||
throw std::runtime_error("unexpected message without any callback set");
|
||||
}
|
||||
}
|
||||
|
||||
bool use_take_shared_method()
|
||||
{
|
||||
return const_shared_ptr_callback_ || const_shared_ptr_with_info_callback_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<MessageAlloc> message_allocator_;
|
||||
MessageDeleter message_deleter_;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/publisher.hpp"
|
||||
#include "rclcpp/service.hpp"
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/timer.hpp"
|
||||
@@ -92,6 +93,10 @@ public:
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(CallbackGroup)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_publisher(const rclcpp::PublisherBase::SharedPtr publisher_ptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_subscription(const rclcpp::SubscriptionBase::SharedPtr subscription_ptr);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
#define RCLCPP__CLOCK_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/time.hpp"
|
||||
@@ -35,13 +32,17 @@ class JumpHandler
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(JumpHandler)
|
||||
|
||||
using pre_callback_t = std::function<void ()>;
|
||||
using post_callback_t = std::function<void (const rcl_time_jump_t &)>;
|
||||
|
||||
JumpHandler(
|
||||
std::function<void()> pre_callback,
|
||||
std::function<void(const rcl_time_jump_t &)> post_callback,
|
||||
pre_callback_t pre_callback,
|
||||
post_callback_t post_callback,
|
||||
const rcl_jump_threshold_t & threshold);
|
||||
|
||||
std::function<void()> pre_callback;
|
||||
std::function<void(const rcl_time_jump_t &)> post_callback;
|
||||
pre_callback_t pre_callback;
|
||||
post_callback_t post_callback;
|
||||
rcl_jump_threshold_t notice_threshold;
|
||||
};
|
||||
|
||||
@@ -50,38 +51,74 @@ class Clock
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Clock)
|
||||
|
||||
/// Default c'tor
|
||||
/**
|
||||
* Initializes the clock instance with the given clock_type.
|
||||
*
|
||||
* \param clock_type type of the clock.
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
explicit Clock(rcl_clock_type_t clock_type = RCL_SYSTEM_TIME);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
~Clock();
|
||||
|
||||
/**
|
||||
* Returns current time from the time source specified by clock_type.
|
||||
*
|
||||
* \return current time.
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
Time
|
||||
now();
|
||||
|
||||
/**
|
||||
* Returns the clock of the type `RCL_ROS_TIME` is active.
|
||||
*
|
||||
* \return true if the clock is active
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw if
|
||||
* the current clock does not have the clock_type `RCL_ROS_TIME`.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
ros_time_is_active();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rcl_clock_t *
|
||||
get_clock_handle();
|
||||
get_clock_handle() noexcept;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rcl_clock_type_t
|
||||
get_clock_type();
|
||||
get_clock_type() const noexcept;
|
||||
|
||||
// Add a callback to invoke if the jump threshold is exceeded.
|
||||
/**
|
||||
* These callback functions must remain valid as long as the
|
||||
* returned shared pointer is valid.
|
||||
*
|
||||
* Function will register callbacks to the callback queue. On time jump all
|
||||
* callbacks will be executed whose threshold is greater then the time jump;
|
||||
* The logic will first call selected pre_callbacks and then all selected
|
||||
* post_callbacks.
|
||||
*
|
||||
* Function is only applicable if the clock_type is `RCL_ROS_TIME`
|
||||
*
|
||||
* \param pre_callback. Must be non-throwing
|
||||
* \param post_callback. Must be non-throwing.
|
||||
* \param threshold. Callbacks will be triggered if the time jump is greater
|
||||
* then the threshold.
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
* \throws std::bad_alloc if the allocation of the JumpHandler fails.
|
||||
* \warning the instance of the clock must remain valid as long as any created
|
||||
* JumpHandler.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
JumpHandler::SharedPtr
|
||||
create_jump_callback(
|
||||
std::function<void()> pre_callback,
|
||||
std::function<void(const rcl_time_jump_t &)> post_callback,
|
||||
JumpHandler::pre_callback_t pre_callback,
|
||||
JumpHandler::post_callback_t post_callback,
|
||||
const rcl_jump_threshold_t & threshold);
|
||||
|
||||
private:
|
||||
|
||||
@@ -18,19 +18,30 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/node_options.hpp"
|
||||
#include "rclcpp/publisher_factory.hpp"
|
||||
#include "rclcpp/publisher_options.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rmw/qos_profiles.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
template<typename MessageT, typename AllocatorT, typename PublisherT>
|
||||
template<
|
||||
typename MessageT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename PublisherT = ::rclcpp::Publisher<MessageT, AllocatorT>>
|
||||
// cppcheck-suppress syntaxError // bug in cppcheck 1.82 for [[deprecated]] on templated function
|
||||
[[deprecated("use alternative rclcpp::create_publisher() signatures")]]
|
||||
std::shared_ptr<PublisherT>
|
||||
create_publisher(
|
||||
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
|
||||
const std::string & topic_name,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
const PublisherEventCallbacks & event_callbacks,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group,
|
||||
bool use_intra_process_comms,
|
||||
std::shared_ptr<AllocatorT> allocator)
|
||||
{
|
||||
@@ -39,10 +50,69 @@ create_publisher(
|
||||
|
||||
auto pub = node_topics->create_publisher(
|
||||
topic_name,
|
||||
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(allocator),
|
||||
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(event_callbacks, allocator),
|
||||
publisher_options,
|
||||
use_intra_process_comms);
|
||||
node_topics->add_publisher(pub);
|
||||
|
||||
node_topics->add_publisher(pub, group);
|
||||
|
||||
return std::dynamic_pointer_cast<PublisherT>(pub);
|
||||
}
|
||||
|
||||
/// Create and return a publisher of the given MessageT type.
|
||||
/**
|
||||
* The NodeT type only needs to have a method called get_node_topics_interface()
|
||||
* which returns a shared_ptr to a NodeTopicsInterface.
|
||||
*/
|
||||
template<
|
||||
typename MessageT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename PublisherT = ::rclcpp::Publisher<MessageT, AllocatorT>,
|
||||
typename NodeT>
|
||||
std::shared_ptr<PublisherT>
|
||||
create_publisher(
|
||||
NodeT & node,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
|
||||
))
|
||||
{
|
||||
using rclcpp::node_interfaces::get_node_topics_interface;
|
||||
auto node_topics = get_node_topics_interface(node);
|
||||
|
||||
std::shared_ptr<AllocatorT> allocator = options.allocator;
|
||||
if (!allocator) {
|
||||
allocator = std::make_shared<AllocatorT>();
|
||||
}
|
||||
|
||||
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 = node_topics->get_node_base_interface()->get_use_intra_process_default();
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unrecognized IntraProcessSetting value");
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO(wjwwood): convert all of the interfaces to use QoS and PublisherOptionsBase
|
||||
auto pub = node_topics->create_publisher(
|
||||
topic_name,
|
||||
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(
|
||||
options.event_callbacks,
|
||||
allocator
|
||||
),
|
||||
options.template to_rcl_publisher_options<MessageT>(qos),
|
||||
use_intra_process
|
||||
);
|
||||
node_topics->add_publisher(pub, options.callback_group);
|
||||
return std::dynamic_pointer_cast<PublisherT>(pub);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,11 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/subscription_factory.hpp"
|
||||
#include "rclcpp/subscription_options.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rmw/qos_profiles.h"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -32,12 +35,15 @@ template<
|
||||
typename AllocatorT,
|
||||
typename CallbackMessageT,
|
||||
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>>
|
||||
// cppcheck-suppress syntaxError // bug in cppcheck 1.82 for [[deprecated]] on templated function
|
||||
[[deprecated("use alternative rclcpp::create_subscription() signatures")]]
|
||||
typename std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
|
||||
const std::string & topic_name,
|
||||
CallbackT && callback,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
const SubscriptionEventCallbacks & event_callbacks,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group,
|
||||
bool ignore_local_publications,
|
||||
bool use_intra_process_comms,
|
||||
@@ -52,7 +58,10 @@ create_subscription(
|
||||
|
||||
auto factory = rclcpp::create_subscription_factory
|
||||
<MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT>(
|
||||
std::forward<CallbackT>(callback), msg_mem_strat, allocator);
|
||||
std::forward<CallbackT>(callback),
|
||||
event_callbacks,
|
||||
msg_mem_strat,
|
||||
allocator);
|
||||
|
||||
auto sub = node_topics->create_subscription(
|
||||
topic_name,
|
||||
@@ -63,6 +72,75 @@ create_subscription(
|
||||
return std::dynamic_pointer_cast<SubscriptionT>(sub);
|
||||
}
|
||||
|
||||
/// Create and return a subscription of the given MessageT type.
|
||||
/**
|
||||
* The NodeT type only needs to have a method called get_node_topics_interface()
|
||||
* which returns a shared_ptr to a NodeTopicsInterface, or be a
|
||||
* NodeTopicsInterface pointer itself.
|
||||
*/
|
||||
template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
|
||||
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
|
||||
typename NodeT>
|
||||
typename std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
NodeT && node,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
CallbackT && callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
|
||||
),
|
||||
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT, AllocatorT>::SharedPtr
|
||||
msg_mem_strat = nullptr)
|
||||
{
|
||||
using rclcpp::node_interfaces::get_node_topics_interface;
|
||||
auto node_topics = get_node_topics_interface(std::forward<NodeT>(node));
|
||||
|
||||
if (!msg_mem_strat) {
|
||||
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
|
||||
msg_mem_strat = MessageMemoryStrategy<CallbackMessageT, AllocatorT>::create_default();
|
||||
}
|
||||
|
||||
std::shared_ptr<AllocatorT> allocator = options.allocator;
|
||||
if (!allocator) {
|
||||
allocator = std::make_shared<AllocatorT>();
|
||||
}
|
||||
auto factory = rclcpp::create_subscription_factory
|
||||
<MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT>(
|
||||
std::forward<CallbackT>(callback), options.event_callbacks, msg_mem_strat, allocator);
|
||||
|
||||
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 = node_topics->get_node_base_interface()->get_use_intra_process_default();
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unrecognized IntraProcessSetting value");
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO(wjwwood): convert all of the interfaces to use QoS and SubscriptionOptionsBase
|
||||
auto sub = node_topics->create_subscription(
|
||||
topic_name,
|
||||
factory,
|
||||
options.template to_rcl_subscription_options<MessageT>(qos),
|
||||
use_intra_process);
|
||||
node_topics->add_subscription(sub, options.callback_group);
|
||||
return std::dynamic_pointer_cast<SubscriptionT>(sub);
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CREATE_SUBSCRIPTION_HPP_
|
||||
|
||||
@@ -23,91 +23,87 @@
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
class Duration
|
||||
class RCLCPP_PUBLIC Duration
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
Duration(int32_t seconds, uint32_t nanoseconds);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit Duration(
|
||||
rcl_duration_value_t nanoseconds);
|
||||
explicit Duration(rcl_duration_value_t nanoseconds);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit Duration(
|
||||
std::chrono::nanoseconds nanoseconds);
|
||||
explicit Duration(std::chrono::nanoseconds nanoseconds);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
Duration(
|
||||
const builtin_interfaces::msg::Duration & duration_msg);
|
||||
// intentionally not using explicit to create a conversion constructor
|
||||
template<class Rep, class Period>
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
Duration(const std::chrono::duration<Rep, Period> & duration) // NOLINT(runtime/explicit)
|
||||
: Duration(std::chrono::duration_cast<std::chrono::nanoseconds>(duration))
|
||||
{}
|
||||
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
Duration(const builtin_interfaces::msg::Duration & duration_msg); // NOLINT(runtime/explicit)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit Duration(const rcl_duration_t & duration);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
Duration(const Duration & rhs);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~Duration();
|
||||
virtual ~Duration() = default;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
operator builtin_interfaces::msg::Duration() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
// cppcheck-suppress operatorEq // this is a false positive from cppcheck
|
||||
Duration &
|
||||
operator=(const Duration & rhs);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
Duration &
|
||||
operator=(const builtin_interfaces::msg::Duration & Duration_msg);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator==(const rclcpp::Duration & rhs) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator<(const rclcpp::Duration & rhs) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator<=(const rclcpp::Duration & rhs) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator>=(const rclcpp::Duration & rhs) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator>(const rclcpp::Duration & rhs) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
Duration
|
||||
operator+(const rclcpp::Duration & rhs) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
Duration
|
||||
operator-(const rclcpp::Duration & rhs) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
static Duration
|
||||
static
|
||||
Duration
|
||||
max();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
Duration
|
||||
operator*(double scale) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rcl_duration_value_t
|
||||
nanoseconds() const;
|
||||
|
||||
/// \return the duration in seconds as a floating point number.
|
||||
/// \warning Depending on sizeof(double) there could be significant precision loss.
|
||||
/// When an exact time is required use nanoseconds() instead.
|
||||
RCLCPP_PUBLIC
|
||||
double
|
||||
seconds() const;
|
||||
|
||||
template<class DurationT>
|
||||
DurationT
|
||||
to_chrono() const
|
||||
{
|
||||
return std::chrono::duration_cast<DurationT>(std::chrono::nanoseconds(this->nanoseconds()));
|
||||
}
|
||||
|
||||
rmw_time_t
|
||||
to_rmw_time() const;
|
||||
|
||||
private:
|
||||
rcl_duration_t rcl_duration_;
|
||||
};
|
||||
|
||||
@@ -110,13 +110,15 @@ public:
|
||||
* \throws std::runtime_error if the rcl_get_error_state returns 0
|
||||
* \throws RCLErrorBase some child class exception based on ret
|
||||
*/
|
||||
/* *INDENT-OFF* */ // Uncrustify cannot yet understand [[noreturn]] properly
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
throw_from_rcl_error(
|
||||
throw_from_rcl_error [[noreturn]] (
|
||||
rcl_ret_t ret,
|
||||
const std::string & prefix = "",
|
||||
const rcl_error_state_t * error_state = nullptr,
|
||||
void (* reset_error)() = rcl_reset_error);
|
||||
/* *INDENT-ON* */
|
||||
|
||||
class RCLErrorBase
|
||||
{
|
||||
@@ -185,14 +187,35 @@ public:
|
||||
class InvalidParametersException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
// Inherit constructors from runtime_error;
|
||||
// Inherit constructors from runtime_error.
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
/// Throwing if passed parameter value is invalid.
|
||||
/// Thrown if passed parameter value is invalid.
|
||||
class InvalidParameterValueException : public std::runtime_error
|
||||
{
|
||||
// Inherit constructors from runtime_error;
|
||||
// Inherit constructors from runtime_error.
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
/// Thrown if parameter is already declared.
|
||||
class ParameterAlreadyDeclaredException : public std::runtime_error
|
||||
{
|
||||
// Inherit constructors from runtime_error.
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
/// Thrown if parameter is not declared, e.g. either set or get was called without first declaring.
|
||||
class ParameterNotDeclaredException : public std::runtime_error
|
||||
{
|
||||
// Inherit constructors from runtime_error.
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
/// Thrown if parameter is immutable and therefore cannot be undeclared.
|
||||
class ParameterImmutableException : public std::runtime_error
|
||||
{
|
||||
// Inherit constructors from runtime_error.
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -25,14 +25,15 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <set>
|
||||
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/intra_process_manager_impl.hpp"
|
||||
#include "rclcpp/mapped_ring_buffer.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/publisher.hpp"
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
#include "rclcpp/subscription_base.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -184,21 +185,11 @@ public:
|
||||
* \param buffer_size if 0 (default) a size is calculated based on the QoS.
|
||||
* \return an unsigned 64-bit integer which is the publisher's unique id.
|
||||
*/
|
||||
template<typename MessageT, typename Alloc>
|
||||
RCLCPP_PUBLIC
|
||||
uint64_t
|
||||
add_publisher(
|
||||
typename Publisher<MessageT, Alloc>::SharedPtr publisher,
|
||||
size_t buffer_size = 0)
|
||||
{
|
||||
auto id = IntraProcessManager::get_next_unique_id();
|
||||
size_t size = buffer_size > 0 ? buffer_size : publisher->get_queue_size();
|
||||
auto mrb = mapped_ring_buffer::MappedRingBuffer<
|
||||
MessageT,
|
||||
typename Publisher<MessageT, Alloc>::MessageAlloc
|
||||
>::make_shared(size, publisher->get_allocator());
|
||||
impl_->add_publisher(id, publisher, mrb, size);
|
||||
return id;
|
||||
}
|
||||
rclcpp::PublisherBase::SharedPtr publisher,
|
||||
size_t buffer_size = 0);
|
||||
|
||||
/// Unregister a publisher using the publisher's unique id.
|
||||
/**
|
||||
@@ -242,12 +233,11 @@ public:
|
||||
* \return the message sequence number.
|
||||
*/
|
||||
template<
|
||||
typename MessageT, typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>>
|
||||
typename MessageT, typename Alloc = std::allocator<void>>
|
||||
uint64_t
|
||||
store_intra_process_message(
|
||||
uint64_t intra_process_publisher_id,
|
||||
std::unique_ptr<MessageT, Deleter> & message)
|
||||
std::shared_ptr<const MessageT> message)
|
||||
{
|
||||
using MRBMessageAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<MessageT>;
|
||||
using TypedMRB = typename mapped_ring_buffer::MappedRingBuffer<MessageT, MRBMessageAlloc>;
|
||||
@@ -270,6 +260,35 @@ public:
|
||||
return message_seq;
|
||||
}
|
||||
|
||||
template<
|
||||
typename MessageT, typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>>
|
||||
uint64_t
|
||||
store_intra_process_message(
|
||||
uint64_t intra_process_publisher_id,
|
||||
std::unique_ptr<MessageT, Deleter> message)
|
||||
{
|
||||
using MRBMessageAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<MessageT>;
|
||||
using TypedMRB = typename mapped_ring_buffer::MappedRingBuffer<MessageT, MRBMessageAlloc>;
|
||||
uint64_t message_seq = 0;
|
||||
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = impl_->get_publisher_info_for_id(
|
||||
intra_process_publisher_id, message_seq);
|
||||
typename TypedMRB::SharedPtr typed_buffer = std::static_pointer_cast<TypedMRB>(buffer);
|
||||
if (!typed_buffer) {
|
||||
throw std::runtime_error("Typecast failed due to incorrect message type");
|
||||
}
|
||||
|
||||
// Insert the message into the ring buffer using the message_seq to identify it.
|
||||
bool did_replace = typed_buffer->push_and_replace(message_seq, std::move(message));
|
||||
// TODO(wjwwood): do something when a message was displaced. log debug?
|
||||
(void)did_replace; // Avoid unused variable warning.
|
||||
|
||||
impl_->store_intra_process_message(intra_process_publisher_id, message_seq);
|
||||
|
||||
// Return the message sequence which is sent to the subscription.
|
||||
return message_seq;
|
||||
}
|
||||
|
||||
/// Take an intra process message.
|
||||
/**
|
||||
* The intra_process_publisher_id and message_sequence_number parameters
|
||||
@@ -334,10 +353,45 @@ public:
|
||||
// Return a copy or the unique_ptr (ownership) depending on how many subscriptions are left.
|
||||
if (target_subs_size) {
|
||||
// There are more subscriptions to serve, return a copy.
|
||||
typed_buffer->get_copy_at_key(message_sequence_number, message);
|
||||
typed_buffer->get(message_sequence_number, message);
|
||||
} else {
|
||||
// This is the last one to be returned, transfer ownership.
|
||||
typed_buffer->pop_at_key(message_sequence_number, message);
|
||||
typed_buffer->pop(message_sequence_number, message);
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
typename MessageT, typename Alloc = std::allocator<void>>
|
||||
void
|
||||
take_intra_process_message(
|
||||
uint64_t intra_process_publisher_id,
|
||||
uint64_t message_sequence_number,
|
||||
uint64_t requesting_subscriptions_intra_process_id,
|
||||
std::shared_ptr<const MessageT> & message)
|
||||
{
|
||||
using MRBMessageAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<MessageT>;
|
||||
using TypedMRB = mapped_ring_buffer::MappedRingBuffer<MessageT, MRBMessageAlloc>;
|
||||
message = nullptr;
|
||||
|
||||
size_t target_subs_size = 0;
|
||||
std::lock_guard<std::mutex> lock(take_mutex_);
|
||||
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = impl_->take_intra_process_message(
|
||||
intra_process_publisher_id,
|
||||
message_sequence_number,
|
||||
requesting_subscriptions_intra_process_id,
|
||||
target_subs_size
|
||||
);
|
||||
typename TypedMRB::SharedPtr typed_buffer = std::static_pointer_cast<TypedMRB>(buffer);
|
||||
if (!typed_buffer) {
|
||||
return;
|
||||
}
|
||||
// Return a copy or the unique_ptr (ownership) depending on how many subscriptions are left.
|
||||
if (target_subs_size) {
|
||||
// There are more subscriptions to serve, return a copy.
|
||||
typed_buffer->get(message_sequence_number, message);
|
||||
} else {
|
||||
// This is the last one to be returned, transfer ownership.
|
||||
typed_buffer->pop(message_sequence_number, message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,6 +400,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,14 +25,17 @@
|
||||
#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"
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
#include "rclcpp/subscription_base.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -80,6 +84,9 @@ public:
|
||||
virtual bool
|
||||
matches_any_publishers(const rmw_gid_t * id) const = 0;
|
||||
|
||||
virtual size_t
|
||||
get_subscription_count(uint64_t intra_process_publisher_id) const = 0;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(IntraProcessManagerImplBase)
|
||||
};
|
||||
@@ -95,9 +102,7 @@ public:
|
||||
add_subscription(uint64_t id, SubscriptionBase::SharedPtr subscription)
|
||||
{
|
||||
subscriptions_[id] = subscription;
|
||||
// subscription->get_topic_name() -> const char * can be used as the key,
|
||||
// since subscriptions_ shares the ownership of subscription
|
||||
subscription_ids_by_topic_[subscription->get_topic_name()].insert(id);
|
||||
subscription_ids_by_topic_[fixed_size_string(subscription->get_topic_name())].insert(id);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -172,7 +177,8 @@ public:
|
||||
}
|
||||
|
||||
// Figure out what subscriptions should receive the message.
|
||||
auto & destined_subscriptions = subscription_ids_by_topic_[publisher->get_topic_name()];
|
||||
auto & destined_subscriptions =
|
||||
subscription_ids_by_topic_[fixed_size_string(publisher->get_topic_name())];
|
||||
// Store the list for later comparison.
|
||||
if (info.target_subscriptions_by_message_sequence.count(message_seq) == 0) {
|
||||
info.target_subscriptions_by_message_sequence.emplace(
|
||||
@@ -248,9 +254,52 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t
|
||||
get_subscription_count(uint64_t intra_process_publisher_id) const
|
||||
{
|
||||
auto publisher_it = publishers_.find(intra_process_publisher_id);
|
||||
if (publisher_it == publishers_.end()) {
|
||||
// Publisher is either invalid or no longer exists.
|
||||
return 0;
|
||||
}
|
||||
auto publisher = publisher_it->second.publisher.lock();
|
||||
if (!publisher) {
|
||||
throw std::runtime_error("publisher has unexpectedly gone out of scope");
|
||||
}
|
||||
auto sub_map_it =
|
||||
subscription_ids_by_topic_.find(fixed_size_string(publisher->get_topic_name()));
|
||||
if (sub_map_it == subscription_ids_by_topic_.end()) {
|
||||
// No intraprocess subscribers
|
||||
return 0;
|
||||
}
|
||||
return sub_map_it->second.size();
|
||||
}
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(IntraProcessManagerImpl)
|
||||
|
||||
using FixedSizeString = std::array<char, RMW_TOPIC_MAX_NAME_LENGTH + 1>;
|
||||
|
||||
FixedSizeString
|
||||
fixed_size_string(const char * str) const
|
||||
{
|
||||
FixedSizeString ret;
|
||||
size_t size = std::strlen(str) + 1;
|
||||
if (size > ret.size()) {
|
||||
throw std::runtime_error("failed to copy topic name");
|
||||
}
|
||||
std::memcpy(ret.data(), str, size);
|
||||
return ret;
|
||||
}
|
||||
struct strcmp_wrapper
|
||||
{
|
||||
bool
|
||||
operator()(const FixedSizeString lhs, const FixedSizeString rhs) const
|
||||
{
|
||||
return std::strcmp(lhs.data(), rhs.data()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using RebindAlloc = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
|
||||
|
||||
@@ -262,19 +311,11 @@ private:
|
||||
std::hash<uint64_t>, std::equal_to<uint64_t>,
|
||||
RebindAlloc<std::pair<const uint64_t, SubscriptionBase::WeakPtr>>>;
|
||||
|
||||
struct strcmp_wrapper
|
||||
{
|
||||
bool
|
||||
operator()(const char * lhs, const char * rhs) const
|
||||
{
|
||||
return std::strcmp(lhs, rhs) < 0;
|
||||
}
|
||||
};
|
||||
using IDTopicMap = std::map<
|
||||
const char *,
|
||||
FixedSizeString,
|
||||
AllocSet,
|
||||
strcmp_wrapper,
|
||||
RebindAlloc<std::pair<const char * const, AllocSet>>>;
|
||||
RebindAlloc<std::pair<const FixedSizeString, AllocSet>>>;
|
||||
|
||||
SubscriptionMap subscriptions_;
|
||||
|
||||
|
||||
34
rclcpp/include/rclcpp/intra_process_setting.hpp
Normal file
34
rclcpp/include/rclcpp/intra_process_setting.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2019 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__INTRA_PROCESS_SETTING_HPP_
|
||||
#define RCLCPP__INTRA_PROCESS_SETTING_HPP_
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Used as argument in create_publisher and create_subscriber.
|
||||
enum class IntraProcessSetting
|
||||
{
|
||||
/// Explicitly enable intraprocess comm at publisher/subscription level.
|
||||
Enable,
|
||||
/// Explicitly disable intraprocess comm at publisher/subscription level.
|
||||
Disable,
|
||||
/// Take intraprocess configuration from the node.
|
||||
NodeDefault
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__INTRA_PROCESS_SETTING_HPP_
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -38,7 +39,7 @@ public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBufferBase)
|
||||
};
|
||||
|
||||
/// Ring buffer container of unique_ptr's of T, which can be accessed by a key.
|
||||
/// Ring buffer container of shared_ptr's or unique_ptr's of T, which can be accessed by a key.
|
||||
/**
|
||||
* T must be a CopyConstructable and CopyAssignable.
|
||||
* This class can be used in a container by using the base class MappedRingBufferBase.
|
||||
@@ -64,6 +65,7 @@ public:
|
||||
using ElemAlloc = typename ElemAllocTraits::allocator_type;
|
||||
using ElemDeleter = allocator::Deleter<ElemAlloc, T>;
|
||||
|
||||
using ConstElemSharedPtr = std::shared_ptr<const T>;
|
||||
using ElemUniquePtr = std::unique_ptr<T, ElemDeleter>;
|
||||
|
||||
/// Constructor.
|
||||
@@ -101,57 +103,33 @@ public:
|
||||
* \param value if the key is found, the value is stored in this parameter
|
||||
*/
|
||||
void
|
||||
get_copy_at_key(uint64_t key, ElemUniquePtr & value)
|
||||
get(uint64_t key, ElemUniquePtr & value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||
auto it = get_iterator_of_key(key);
|
||||
value = nullptr;
|
||||
if (it != elements_.end() && it->in_use) {
|
||||
auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
|
||||
ElemAllocTraits::construct(*allocator_.get(), ptr, *it->value);
|
||||
value = ElemUniquePtr(ptr);
|
||||
if (it->unique_value) {
|
||||
ElemDeleter deleter = it->unique_value.get_deleter();
|
||||
auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
|
||||
ElemAllocTraits::construct(*allocator_.get(), ptr, *it->unique_value);
|
||||
value = ElemUniquePtr(ptr, deleter);
|
||||
} else if (it->shared_value) {
|
||||
ElemDeleter * deleter = std::get_deleter<ElemDeleter, const T>(it->shared_value);
|
||||
auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
|
||||
ElemAllocTraits::construct(*allocator_.get(), ptr, *it->shared_value);
|
||||
if (deleter) {
|
||||
value = ElemUniquePtr(ptr, *deleter);
|
||||
} else {
|
||||
value = ElemUniquePtr(ptr);
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Unexpected empty MappedRingBuffer element.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return ownership of the value stored in the ring buffer, leaving a copy.
|
||||
/**
|
||||
* The key is matched if an element in the ring bufer has a matching key.
|
||||
* This method will allocate in order to store a copy.
|
||||
*
|
||||
* The key is not guaranteed to be unique, see the class docs for more.
|
||||
*
|
||||
* The ownership of the currently stored object is returned, but a copy is
|
||||
* made and stored in its place.
|
||||
* This means that multiple calls to this function for a particular element
|
||||
* will result in returning the copied and stored object not the original.
|
||||
* This also means that later calls to pop_at_key will not return the
|
||||
* originally stored object, since it was returned by the first call to this
|
||||
* method.
|
||||
*
|
||||
* The contents of value before the method is called are discarded.
|
||||
*
|
||||
* \param key the key associated with the stored value
|
||||
* \param value if the key is found, the value is stored in this parameter
|
||||
*/
|
||||
void
|
||||
get_ownership_at_key(uint64_t key, ElemUniquePtr & value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||
auto it = get_iterator_of_key(key);
|
||||
value = nullptr;
|
||||
if (it != elements_.end() && it->in_use) {
|
||||
// Make a copy.
|
||||
auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
|
||||
ElemAllocTraits::construct(*allocator_.get(), ptr, *it->value);
|
||||
auto copy = ElemUniquePtr(ptr);
|
||||
// Return the original.
|
||||
value.swap(it->value);
|
||||
// Store the copy.
|
||||
it->value.swap(copy);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return ownership of the value stored in the ring buffer at the given key.
|
||||
/// Share ownership of the value stored in the ring buffer at the given key.
|
||||
/**
|
||||
* The key is matched if an element in the ring buffer has a matching key.
|
||||
*
|
||||
@@ -163,13 +141,90 @@ public:
|
||||
* \param value if the key is found, the value is stored in this parameter
|
||||
*/
|
||||
void
|
||||
pop_at_key(uint64_t key, ElemUniquePtr & value)
|
||||
get(uint64_t key, ConstElemSharedPtr & value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||
auto it = get_iterator_of_key(key);
|
||||
value.reset();
|
||||
if (it != elements_.end() && it->in_use) {
|
||||
if (!it->shared_value) {
|
||||
// The stored unique_ptr is upgraded to a shared_ptr here.
|
||||
// All the remaining get and pop calls done with unique_ptr
|
||||
// signature will receive a copy.
|
||||
if (!it->unique_value) {
|
||||
throw std::runtime_error("Unexpected empty MappedRingBuffer element.");
|
||||
}
|
||||
it->shared_value = std::move(it->unique_value);
|
||||
}
|
||||
value = it->shared_value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Give the ownership of the stored value to the caller if possible, or copy and release.
|
||||
/**
|
||||
* The key is matched if an element in the ring buffer has a matching key.
|
||||
* This method may allocate in order to return a copy.
|
||||
*
|
||||
* If the stored value is a shared_ptr, it is not possible to downgrade it to a unique_ptr.
|
||||
* In that case, a copy is returned and the stored value is released.
|
||||
*
|
||||
* The key is not guaranteed to be unique, see the class docs for more.
|
||||
*
|
||||
* The contents of value before the method is called are discarded.
|
||||
*
|
||||
* \param key the key associated with the stored value
|
||||
* \param value if the key is found, the value is stored in this parameter
|
||||
*/
|
||||
void
|
||||
pop(uint64_t key, ElemUniquePtr & value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||
auto it = get_iterator_of_key(key);
|
||||
value = nullptr;
|
||||
if (it != elements_.end() && it->in_use) {
|
||||
value.swap(it->value);
|
||||
if (it->unique_value) {
|
||||
value = std::move(it->unique_value);
|
||||
} else if (it->shared_value) {
|
||||
auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
|
||||
ElemAllocTraits::construct(*allocator_.get(), ptr, *it->shared_value);
|
||||
auto deleter = std::get_deleter<ElemDeleter, const T>(it->shared_value);
|
||||
if (deleter) {
|
||||
value = ElemUniquePtr(ptr, *deleter);
|
||||
} else {
|
||||
value = ElemUniquePtr(ptr);
|
||||
}
|
||||
it->shared_value.reset();
|
||||
} else {
|
||||
throw std::runtime_error("Unexpected empty MappedRingBuffer element.");
|
||||
}
|
||||
it->in_use = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Give the ownership of the stored value to the caller, at the given key.
|
||||
/**
|
||||
* The key is matched if an element in the ring buffer has a matching key.
|
||||
*
|
||||
* The key is not guaranteed to be unique, see the class docs for more.
|
||||
*
|
||||
* The contents of value before the method is called are discarded.
|
||||
*
|
||||
* \param key the key associated with the stored value
|
||||
* \param value if the key is found, the value is stored in this parameter
|
||||
*/
|
||||
void
|
||||
pop(uint64_t key, ConstElemSharedPtr & value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||
auto it = get_iterator_of_key(key);
|
||||
if (it != elements_.end() && it->in_use) {
|
||||
if (it->shared_value) {
|
||||
value = std::move(it->shared_value);
|
||||
} else if (it->unique_value) {
|
||||
value = std::move(it->unique_value);
|
||||
} else {
|
||||
throw std::runtime_error("Unexpected empty MappedRingBuffer element.");
|
||||
}
|
||||
it->in_use = false;
|
||||
}
|
||||
}
|
||||
@@ -180,29 +235,44 @@ public:
|
||||
* It is up to the user to ensure the key is unique.
|
||||
* This method should not allocate memory.
|
||||
*
|
||||
* After insertion, if a pair was replaced, then value will contain ownership
|
||||
* of that displaced value. Otherwise it will be a nullptr.
|
||||
* After insertion the value will be a nullptr.
|
||||
* If a pair were replaced, its smart pointer is reset.
|
||||
*
|
||||
* \param key the key associated with the value to be stored
|
||||
* \param value the value to store, and optionally the value displaced
|
||||
*/
|
||||
bool
|
||||
push_and_replace(uint64_t key, ElemUniquePtr & value)
|
||||
push_and_replace(uint64_t key, ConstElemSharedPtr value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||
bool did_replace = elements_[head_].in_use;
|
||||
elements_[head_].key = key;
|
||||
elements_[head_].value.swap(value);
|
||||
elements_[head_].in_use = true;
|
||||
Element & element = elements_[head_];
|
||||
element.key = key;
|
||||
element.unique_value.reset();
|
||||
element.shared_value.reset();
|
||||
element.shared_value = value;
|
||||
element.in_use = true;
|
||||
head_ = (head_ + 1) % elements_.size();
|
||||
return did_replace;
|
||||
}
|
||||
|
||||
/// Insert a key-value pair, displacing an existing pair if necessary.
|
||||
/**
|
||||
* See `bool push_and_replace(uint64_t key, const ConstElemSharedPtr & value)`.
|
||||
*/
|
||||
bool
|
||||
push_and_replace(uint64_t key, ElemUniquePtr && value)
|
||||
push_and_replace(uint64_t key, ElemUniquePtr value)
|
||||
{
|
||||
ElemUniquePtr temp = std::move(value);
|
||||
return push_and_replace(key, temp);
|
||||
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||
bool did_replace = elements_[head_].in_use;
|
||||
Element & element = elements_[head_];
|
||||
element.key = key;
|
||||
element.unique_value.reset();
|
||||
element.shared_value.reset();
|
||||
element.unique_value = std::move(value);
|
||||
element.in_use = true;
|
||||
head_ = (head_ + 1) % elements_.size();
|
||||
return did_replace;
|
||||
}
|
||||
|
||||
/// Return true if the key is found in the ring buffer, otherwise false.
|
||||
@@ -216,27 +286,28 @@ public:
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(MappedRingBuffer<T, Alloc>)
|
||||
|
||||
struct element
|
||||
struct Element
|
||||
{
|
||||
uint64_t key;
|
||||
ElemUniquePtr value;
|
||||
ElemUniquePtr unique_value;
|
||||
ConstElemSharedPtr shared_value;
|
||||
bool in_use;
|
||||
};
|
||||
|
||||
using VectorAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<element>;
|
||||
using VectorAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<Element>;
|
||||
|
||||
typename std::vector<element, VectorAlloc>::iterator
|
||||
typename std::vector<Element, VectorAlloc>::iterator
|
||||
get_iterator_of_key(uint64_t key)
|
||||
{
|
||||
auto it = std::find_if(
|
||||
elements_.begin(), elements_.end(),
|
||||
[key](element & e) -> bool {
|
||||
[key](Element & e) -> bool {
|
||||
return e.key == key && e.in_use;
|
||||
});
|
||||
return it;
|
||||
}
|
||||
|
||||
std::vector<element, VectorAlloc> elements_;
|
||||
std::vector<Element, VectorAlloc> elements_;
|
||||
size_t head_;
|
||||
std::shared_ptr<ElemAlloc> allocator_;
|
||||
std::mutex data_mutex_;
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
virtual size_t number_of_ready_subscriptions() const = 0;
|
||||
virtual size_t number_of_ready_services() const = 0;
|
||||
virtual size_t number_of_ready_clients() const = 0;
|
||||
virtual size_t number_of_ready_events() const = 0;
|
||||
virtual size_t number_of_ready_timers() const = 0;
|
||||
virtual size_t number_of_guard_conditions() const = 0;
|
||||
virtual size_t number_of_waitables() const = 0;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,11 +36,12 @@
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/intra_process_manager.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/create_publisher.hpp"
|
||||
#include "rclcpp/create_service.hpp"
|
||||
#include "rclcpp/create_subscription.hpp"
|
||||
#include "rclcpp/intra_process_manager.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -51,35 +52,82 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
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)
|
||||
RCLCPP_LOCAL
|
||||
inline
|
||||
std::string
|
||||
extend_name_with_sub_namespace(const std::string & name, const std::string & sub_namespace)
|
||||
{
|
||||
if (!allocator) {
|
||||
allocator = std::make_shared<Alloc>();
|
||||
std::string name_with_sub_namespace(name);
|
||||
if (sub_namespace != "" && name.front() != '/' && name.front() != '~') {
|
||||
name_with_sub_namespace = sub_namespace + "/" + name;
|
||||
}
|
||||
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);
|
||||
return name_with_sub_namespace;
|
||||
}
|
||||
|
||||
template<typename MessageT, typename Alloc, typename PublisherT>
|
||||
template<typename MessageT, typename AllocatorT, 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)
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
const PublisherOptionsWithAllocator<AllocatorT> & options)
|
||||
{
|
||||
if (!allocator) {
|
||||
allocator = std::make_shared<Alloc>();
|
||||
}
|
||||
return rclcpp::create_publisher<MessageT, Alloc, PublisherT>(
|
||||
this->node_topics_.get(),
|
||||
topic_name,
|
||||
qos_profile,
|
||||
use_intra_process_comms_,
|
||||
allocator);
|
||||
return rclcpp::create_publisher<MessageT, AllocatorT, PublisherT>(
|
||||
*this,
|
||||
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
|
||||
qos,
|
||||
options);
|
||||
}
|
||||
|
||||
template<typename MessageT, typename AllocatorT, typename PublisherT>
|
||||
std::shared_ptr<PublisherT>
|
||||
Node::create_publisher(
|
||||
const std::string & topic_name,
|
||||
size_t qos_history_depth,
|
||||
std::shared_ptr<AllocatorT> allocator)
|
||||
{
|
||||
PublisherOptionsWithAllocator<AllocatorT> pub_options;
|
||||
pub_options.allocator = allocator;
|
||||
return this->create_publisher<MessageT, AllocatorT, PublisherT>(
|
||||
topic_name, rclcpp::QoS(rclcpp::KeepLast(qos_history_depth)), pub_options);
|
||||
}
|
||||
|
||||
template<typename MessageT, typename AllocatorT, typename PublisherT>
|
||||
std::shared_ptr<PublisherT>
|
||||
Node::create_publisher(
|
||||
const std::string & topic_name,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
std::shared_ptr<AllocatorT> allocator)
|
||||
{
|
||||
rclcpp::QoS qos(rclcpp::QoSInitialization::from_rmw(qos_profile));
|
||||
qos.get_rmw_qos_profile() = qos_profile;
|
||||
|
||||
PublisherOptionsWithAllocator<AllocatorT> pub_options;
|
||||
pub_options.allocator = allocator;
|
||||
return this->create_publisher<MessageT, AllocatorT, PublisherT>(topic_name, qos, pub_options);
|
||||
}
|
||||
|
||||
template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename SubscriptionT>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
Node::create_subscription(
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
CallbackT && callback,
|
||||
const SubscriptionOptionsWithAllocator<AllocatorT> & options,
|
||||
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, AllocatorT>::SharedPtr
|
||||
msg_mem_strat)
|
||||
{
|
||||
return rclcpp::create_subscription<MessageT>(
|
||||
*this,
|
||||
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
|
||||
qos,
|
||||
std::forward<CallbackT>(callback),
|
||||
options,
|
||||
msg_mem_strat);
|
||||
}
|
||||
|
||||
template<
|
||||
@@ -99,27 +147,16 @@ Node::create_subscription(
|
||||
msg_mem_strat,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
{
|
||||
using CallbackMessageT = typename rclcpp::subscription_traits::has_message_type<CallbackT>::type;
|
||||
rclcpp::QoS qos(rclcpp::QoSInitialization::from_rmw(qos_profile));
|
||||
qos.get_rmw_qos_profile() = qos_profile;
|
||||
|
||||
if (!allocator) {
|
||||
allocator = std::make_shared<Alloc>();
|
||||
}
|
||||
SubscriptionOptionsWithAllocator<Alloc> sub_options;
|
||||
sub_options.callback_group = group;
|
||||
sub_options.ignore_local_publications = ignore_local_publications;
|
||||
sub_options.allocator = allocator;
|
||||
|
||||
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, qos, std::forward<CallbackT>(callback), sub_options, msg_mem_strat);
|
||||
}
|
||||
|
||||
template<
|
||||
@@ -139,22 +176,23 @@ Node::create_subscription(
|
||||
msg_mem_strat,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
{
|
||||
rmw_qos_profile_t qos = rmw_qos_profile_default;
|
||||
qos.depth = qos_history_depth;
|
||||
return this->create_subscription<MessageT>(
|
||||
SubscriptionOptionsWithAllocator<Alloc> sub_options;
|
||||
sub_options.callback_group = group;
|
||||
sub_options.ignore_local_publications = ignore_local_publications;
|
||||
sub_options.allocator = allocator;
|
||||
|
||||
return this->create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
|
||||
topic_name,
|
||||
rclcpp::QoS(rclcpp::KeepLast(qos_history_depth)),
|
||||
std::forward<CallbackT>(callback),
|
||||
qos,
|
||||
group,
|
||||
ignore_local_publications,
|
||||
msg_mem_strat,
|
||||
allocator);
|
||||
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 +220,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,15 +237,68 @@ 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>
|
||||
void
|
||||
Node::register_param_change_callback(CallbackT && callback)
|
||||
template<typename ParameterT>
|
||||
auto
|
||||
Node::declare_parameter(
|
||||
const std::string & name,
|
||||
const ParameterT & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor)
|
||||
{
|
||||
this->node_parameters_->register_param_change_callback(std::forward<CallbackT>(callback));
|
||||
return this->declare_parameter(
|
||||
name,
|
||||
rclcpp::ParameterValue(default_value),
|
||||
parameter_descriptor
|
||||
).get<ParameterT>();
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
std::vector<ParameterT>
|
||||
Node::declare_parameters(
|
||||
const std::string & namespace_,
|
||||
const std::map<std::string, ParameterT> & parameters)
|
||||
{
|
||||
std::vector<ParameterT> result;
|
||||
std::string normalized_namespace = namespace_.empty() ? "" : (namespace_ + ".");
|
||||
std::transform(
|
||||
parameters.begin(), parameters.end(), std::back_inserter(result),
|
||||
[this, &normalized_namespace](auto element) {
|
||||
return this->declare_parameter(normalized_namespace + element.first, element.second);
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
std::vector<ParameterT>
|
||||
Node::declare_parameters(
|
||||
const std::string & namespace_,
|
||||
const std::map<
|
||||
std::string,
|
||||
std::pair<ParameterT, rcl_interfaces::msg::ParameterDescriptor>
|
||||
> & parameters)
|
||||
{
|
||||
std::vector<ParameterT> result;
|
||||
std::string normalized_namespace = namespace_.empty() ? "" : (namespace_ + ".");
|
||||
std::transform(
|
||||
parameters.begin(), parameters.end(), std::back_inserter(result),
|
||||
[this, &normalized_namespace](auto element) {
|
||||
return static_cast<ParameterT>(
|
||||
this->declare_parameter(
|
||||
normalized_namespace + element.first,
|
||||
element.second.first,
|
||||
element.second.second)
|
||||
);
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
@@ -216,30 +307,32 @@ Node::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),
|
||||
});
|
||||
if (
|
||||
!this->has_parameter(name) ||
|
||||
this->describe_parameter(name).type == PARAMETER_NOT_SET)
|
||||
{
|
||||
this->set_parameter(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>
|
||||
template<typename ParameterT>
|
||||
void
|
||||
Node::set_parameters_if_not_set(
|
||||
const std::string & name,
|
||||
const std::map<std::string, MapValueT> & values)
|
||||
const std::map<std::string, ParameterT> & 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));
|
||||
std::string parameter_name = name + "." + val.first;
|
||||
if (
|
||||
!this->has_parameter(parameter_name) ||
|
||||
this->describe_parameter(parameter_name).type == PARAMETER_NOT_SET)
|
||||
{
|
||||
params.push_back(rclcpp::Parameter(parameter_name, val.second));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,32 +341,15 @@ Node::set_parameters_if_not_set(
|
||||
|
||||
template<typename ParameterT>
|
||||
bool
|
||||
Node::get_parameter(const std::string & name, ParameterT & value) const
|
||||
Node::get_parameter(const std::string & name, ParameterT & parameter) const
|
||||
{
|
||||
rclcpp::Parameter parameter;
|
||||
bool result = get_parameter(name, parameter);
|
||||
if (result) {
|
||||
value = parameter.get_value<ParameterT>();
|
||||
}
|
||||
std::string sub_name = extend_name_with_sub_namespace(name, this->get_sub_namespace());
|
||||
|
||||
return result;
|
||||
}
|
||||
rclcpp::Parameter parameter_variant;
|
||||
|
||||
// this is a partially-specialized version of get_parameter above,
|
||||
// where our concrete type for ParameterT is std::map, but the to-be-determined
|
||||
// type is the value in the map.
|
||||
template<typename MapValueT>
|
||||
bool
|
||||
Node::get_parameters(
|
||||
const std::string & name,
|
||||
std::map<std::string, MapValueT> & values) const
|
||||
{
|
||||
std::map<std::string, rclcpp::Parameter> params;
|
||||
bool result = node_parameters_->get_parameters_by_prefix(name, params);
|
||||
bool result = get_parameter(sub_name, parameter_variant);
|
||||
if (result) {
|
||||
for (const auto & param : params) {
|
||||
values[param.first] = param.second.get_value<MapValueT>();
|
||||
}
|
||||
parameter = static_cast<ParameterT>(parameter_variant.get_value<ParameterT>());
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -283,16 +359,38 @@ template<typename ParameterT>
|
||||
bool
|
||||
Node::get_parameter_or(
|
||||
const std::string & name,
|
||||
ParameterT & value,
|
||||
ParameterT & parameter,
|
||||
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, parameter);
|
||||
if (!got_parameter) {
|
||||
value = alternative_value;
|
||||
parameter = alternative_value;
|
||||
}
|
||||
return got_parameter;
|
||||
}
|
||||
|
||||
// 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 ParameterT>
|
||||
bool
|
||||
Node::get_parameters(
|
||||
const std::string & prefix,
|
||||
std::map<std::string, ParameterT> & values) const
|
||||
{
|
||||
std::map<std::string, rclcpp::Parameter> params;
|
||||
bool result = node_parameters_->get_parameters_by_prefix(prefix, params);
|
||||
if (result) {
|
||||
for (const auto & param : params) {
|
||||
values[param.first] = static_cast<ParameterT>(param.second.get_value<ParameterT>());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
void
|
||||
Node::get_parameter_or_set(
|
||||
@@ -300,15 +398,24 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename CallbackT>
|
||||
void
|
||||
Node::register_param_change_callback(CallbackT && callback)
|
||||
{
|
||||
this->node_parameters_->register_param_change_callback(std::forward<CallbackT>(callback));
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NODE_IMPL_HPP_
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
// 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_INTERFACES__GET_NODE_TOPICS_INTERFACE_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__GET_NODE_TOPICS_INTERFACE_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
|
||||
/// This header provides the get_node_topics_interface() template function.
|
||||
/**
|
||||
* This function is useful for getting the NodeTopicsInterface pointer from
|
||||
* various kinds of Node-like classes.
|
||||
*
|
||||
* It's able to get the NodeTopicsInterface pointer so long as the class
|
||||
* has a method called ``get_node_topics_interface()`` which returns
|
||||
* either a pointer (const or not) to a NodeTopicsInterface or a
|
||||
* std::shared_ptr to a NodeTopicsInterface.
|
||||
*/
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// This is a meta-programming checker for if a given Node-like object has a
|
||||
// getter called get_node_topics_interface() which returns various types,
|
||||
// e.g. const pointer or a shared pointer.
|
||||
template<typename NodeType, typename ReturnType>
|
||||
struct has_get_node_topics_interface
|
||||
{
|
||||
private:
|
||||
template<typename T>
|
||||
static constexpr
|
||||
auto
|
||||
check(T *)->typename std::is_same<
|
||||
decltype(std::declval<T>().get_node_topics_interface()),
|
||||
ReturnType
|
||||
>::type;
|
||||
|
||||
template<typename>
|
||||
static constexpr
|
||||
std::false_type
|
||||
check(...);
|
||||
|
||||
public:
|
||||
using type = decltype(check<NodeType>(nullptr));
|
||||
static constexpr bool value = type::value;
|
||||
};
|
||||
|
||||
// If NodeType is a pointer to NodeTopicsInterface already (just normal function overload).
|
||||
inline
|
||||
rclcpp::node_interfaces::NodeTopicsInterface *
|
||||
get_node_topics_interface_from_pointer(rclcpp::node_interfaces::NodeTopicsInterface * pointer)
|
||||
{
|
||||
return pointer;
|
||||
}
|
||||
|
||||
// If NodeType has a method called get_node_topics_interface() which returns a shared pointer.
|
||||
template<
|
||||
typename NodeType,
|
||||
typename std::enable_if<has_get_node_topics_interface<
|
||||
typename std::remove_pointer<NodeType>::type,
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeTopicsInterface>
|
||||
>::value, int>::type = 0
|
||||
>
|
||||
rclcpp::node_interfaces::NodeTopicsInterface *
|
||||
get_node_topics_interface_from_pointer(NodeType node_pointer)
|
||||
{
|
||||
return node_pointer->get_node_topics_interface().get();
|
||||
}
|
||||
|
||||
// If NodeType has a method called get_node_topics_interface() which returns a pointer.
|
||||
template<
|
||||
typename NodeType,
|
||||
typename std::enable_if<has_get_node_topics_interface<
|
||||
typename std::remove_pointer<NodeType>::type,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface *
|
||||
>::value, int>::type = 0
|
||||
>
|
||||
rclcpp::node_interfaces::NodeTopicsInterface *
|
||||
get_node_topics_interface_from_pointer(NodeType node_pointer)
|
||||
{
|
||||
return node_pointer->get_node_topics_interface();
|
||||
}
|
||||
|
||||
// Forward shared_ptr's to const node pointer signatures.
|
||||
template<
|
||||
typename NodeType,
|
||||
typename std::enable_if<std::is_same<
|
||||
NodeType,
|
||||
typename std::shared_ptr<typename std::remove_pointer<NodeType>::type::element_type> *
|
||||
>::value, int>::type = 0
|
||||
>
|
||||
rclcpp::node_interfaces::NodeTopicsInterface *
|
||||
get_node_topics_interface_from_pointer(NodeType node_shared_pointer)
|
||||
{
|
||||
return get_node_topics_interface_from_pointer(node_shared_pointer->get());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// Get the NodeTopicsInterface as a pointer from a pointer to a "Node like" object.
|
||||
template<
|
||||
typename NodeType,
|
||||
typename std::enable_if<std::is_pointer<NodeType>::value, int>::type = 0
|
||||
>
|
||||
rclcpp::node_interfaces::NodeTopicsInterface *
|
||||
get_node_topics_interface(NodeType && node_pointer)
|
||||
{
|
||||
// Forward pointers to detail implmentation directly.
|
||||
return detail::get_node_topics_interface_from_pointer(std::forward<NodeType>(node_pointer));
|
||||
}
|
||||
|
||||
/// Get the NodeTopicsInterface as a pointer from a "Node like" object.
|
||||
template<
|
||||
typename NodeType,
|
||||
typename std::enable_if<!std::is_pointer<NodeType>::value, int>::type = 0
|
||||
>
|
||||
rclcpp::node_interfaces::NodeTopicsInterface *
|
||||
get_node_topics_interface(NodeType && node_reference)
|
||||
{
|
||||
// Forward references to detail implmentation as a pointer.
|
||||
return detail::get_node_topics_interface_from_pointer(&node_reference);
|
||||
}
|
||||
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__GET_NODE_TOPICS_INTERFACE_HPP_
|
||||
@@ -19,9 +19,11 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/node.h"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_options.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -33,15 +35,15 @@ namespace node_interfaces
|
||||
class NodeBase : public NodeBaseInterface
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBaseInterface)
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBase)
|
||||
|
||||
RCLCPP_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 rcl_node_options_t & rcl_node_options,
|
||||
bool use_intra_process_default);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -57,6 +59,11 @@ public:
|
||||
const char *
|
||||
get_namespace() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
const char *
|
||||
get_fully_qualified_name() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rclcpp::Context::SharedPtr
|
||||
@@ -82,6 +89,11 @@ public:
|
||||
std::shared_ptr<const rcl_node_t>
|
||||
get_shared_rcl_node_handle() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
assert_liveliness() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
@@ -117,10 +129,16 @@ public:
|
||||
std::unique_lock<std::recursive_mutex>
|
||||
acquire_notify_guard_condition_lock() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
get_use_intra_process_default() const;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeBase)
|
||||
|
||||
rclcpp::Context::SharedPtr context_;
|
||||
bool use_intra_process_default_;
|
||||
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -95,6 +102,12 @@ public:
|
||||
std::shared_ptr<const rcl_node_t>
|
||||
get_shared_rcl_node_handle() const = 0;
|
||||
|
||||
/// Manually assert that this Node is alive (for RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_NODE).
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
assert_liveliness() const = 0;
|
||||
|
||||
/// Create and return a callback group.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -142,6 +155,12 @@ public:
|
||||
virtual
|
||||
std::unique_lock<std::recursive_mutex>
|
||||
acquire_notify_guard_condition_lock() const = 0;
|
||||
|
||||
/// Return the default preference for using intra process communication.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
get_use_intra_process_default() const = 0;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -40,6 +40,16 @@ namespace rclcpp
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
// Internal struct for holding useful info about parameters
|
||||
struct ParameterInfo
|
||||
{
|
||||
/// Current value of the parameter.
|
||||
rclcpp::ParameterValue value;
|
||||
|
||||
/// A description of the parameter
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
};
|
||||
|
||||
/// Implementation of the NodeParameters part of the Node API.
|
||||
class NodeParameters : public NodeParametersInterface
|
||||
{
|
||||
@@ -49,81 +59,104 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
NodeParameters(
|
||||
const node_interfaces::NodeBaseInterface::SharedPtr node_base,
|
||||
const node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
|
||||
const node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
|
||||
const node_interfaces::NodeServicesInterface::SharedPtr node_services,
|
||||
const node_interfaces::NodeClockInterface::SharedPtr node_clock,
|
||||
const std::vector<Parameter> & initial_parameters,
|
||||
bool use_intra_process,
|
||||
bool start_parameter_services);
|
||||
bool start_parameter_services,
|
||||
bool start_parameter_event_publisher,
|
||||
const rclcpp::QoS & parameter_event_qos,
|
||||
const rclcpp::PublisherOptionsBase & parameter_event_publisher_options,
|
||||
bool allow_undeclared_parameters,
|
||||
bool automatically_declare_initial_parameters);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeParameters();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
undeclare_parameter(const std::string & name) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
has_parameter(const std::string & name) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
set_parameters(
|
||||
const std::vector<rclcpp::Parameter> & parameters);
|
||||
const std::vector<rclcpp::Parameter> & parameters) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
set_parameters_atomically(
|
||||
const std::vector<rclcpp::Parameter> & parameters);
|
||||
const std::vector<rclcpp::Parameter> & parameters) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<rclcpp::Parameter>
|
||||
get_parameters(const std::vector<std::string> & names) const;
|
||||
get_parameters(const std::vector<std::string> & names) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rclcpp::Parameter
|
||||
get_parameter(const std::string & name) const;
|
||||
get_parameter(const std::string & name) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
get_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::Parameter & parameter) const;
|
||||
rclcpp::Parameter & parameter) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
get_parameters_by_prefix(
|
||||
const std::string & prefix,
|
||||
std::map<std::string, rclcpp::Parameter> & parameters) const;
|
||||
std::map<std::string, rclcpp::Parameter> & parameters) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor>
|
||||
describe_parameters(const std::vector<std::string> & names) const;
|
||||
describe_parameters(const std::vector<std::string> & names) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<uint8_t>
|
||||
get_parameter_types(const std::vector<std::string> & names) const;
|
||||
get_parameter_types(const std::vector<std::string> & names) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const;
|
||||
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
OnParametersSetCallbackType
|
||||
set_on_parameters_set_callback(OnParametersSetCallbackType callback) override;
|
||||
|
||||
[[deprecated("use set_on_parameters_set_callback() instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
register_param_change_callback(ParametersCallbackFunction callback);
|
||||
register_param_change_callback(OnParametersSetCallbackType callback) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::map<std::string, rclcpp::ParameterValue> &
|
||||
get_initial_parameter_values() const override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeParameters)
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
ParametersCallbackFunction parameters_callback_ = nullptr;
|
||||
OnParametersSetCallbackType on_parameters_set_callback_ = nullptr;
|
||||
|
||||
std::map<std::string, rclcpp::Parameter> parameters_;
|
||||
std::map<std::string, ParameterInfo> parameters_;
|
||||
|
||||
std::map<std::string, rclcpp::ParameterValue> initial_parameter_values_;
|
||||
|
||||
bool allow_undeclared_ = false;
|
||||
|
||||
Publisher<rcl_interfaces::msg::ParameterEvent>::SharedPtr events_publisher_;
|
||||
|
||||
@@ -131,6 +164,7 @@ private:
|
||||
|
||||
std::string combined_name_;
|
||||
|
||||
node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
|
||||
node_interfaces::NodeClockInterface::SharedPtr node_clock_;
|
||||
};
|
||||
|
||||
|
||||
@@ -42,12 +42,49 @@ public:
|
||||
virtual
|
||||
~NodeParametersInterface() = default;
|
||||
|
||||
/// Declare and initialize a parameter.
|
||||
/**
|
||||
* \sa rclcpp::Node::declare_parameter
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor) = 0;
|
||||
|
||||
/// Undeclare a parameter.
|
||||
/**
|
||||
* \sa rclcpp::Node::undeclare_parameter
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
undeclare_parameter(const std::string & name) = 0;
|
||||
|
||||
/// Return true if the parameter has been declared, otherwise false.
|
||||
/**
|
||||
* \sa rclcpp::Node::has_parameter
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
has_parameter(const std::string & name) const = 0;
|
||||
|
||||
/// Set one or more parameters, one at a time.
|
||||
/**
|
||||
* \sa rclcpp::Node::set_parameters
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
set_parameters(
|
||||
const std::vector<rclcpp::Parameter> & parameters) = 0;
|
||||
set_parameters(const std::vector<rclcpp::Parameter> & parameters) = 0;
|
||||
|
||||
/// Set and initialize a parameter, all at once.
|
||||
/**
|
||||
* \sa rclcpp::Node::set_parameters_atomically
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
@@ -119,14 +156,34 @@ public:
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const = 0;
|
||||
|
||||
using ParametersCallbackFunction = std::function<
|
||||
rcl_interfaces::msg::SetParametersResult(
|
||||
const std::vector<rclcpp::Parameter> &)>;
|
||||
using OnParametersSetCallbackType =
|
||||
std::function<
|
||||
rcl_interfaces::msg::SetParametersResult(const std::vector<rclcpp::Parameter> &)
|
||||
>;
|
||||
|
||||
using ParametersCallbackFunction [[deprecated("use OnParametersSetCallbackType instead")]] =
|
||||
OnParametersSetCallbackType;
|
||||
|
||||
/// Register a callback for when parameters are being set, return an existing one.
|
||||
/**
|
||||
* \sa rclcpp::Node::set_on_parameters_set_callback
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
OnParametersSetCallbackType
|
||||
set_on_parameters_set_callback(OnParametersSetCallbackType callback) = 0;
|
||||
|
||||
[[deprecated("use set_on_parameters_set_callback() instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
register_param_change_callback(ParametersCallbackFunction callback) = 0;
|
||||
register_param_change_callback(OnParametersSetCallbackType callback) = 0;
|
||||
|
||||
/// Return the initial parameter values used by the NodeParameters to override default values.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
const std::map<std::string, rclcpp::ParameterValue> &
|
||||
get_initial_parameter_values() const = 0;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -42,44 +42,44 @@ public:
|
||||
explicit NodeTopics(rclcpp::node_interfaces::NodeBaseInterface * node_base);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeTopics();
|
||||
~NodeTopics() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rclcpp::PublisherBase::SharedPtr
|
||||
create_publisher(
|
||||
const std::string & topic_name,
|
||||
const rclcpp::PublisherFactory & publisher_factory,
|
||||
rcl_publisher_options_t & publisher_options,
|
||||
bool use_intra_process);
|
||||
const rcl_publisher_options_t & publisher_options,
|
||||
bool use_intra_process) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
add_publisher(
|
||||
rclcpp::PublisherBase::SharedPtr publisher);
|
||||
rclcpp::PublisherBase::SharedPtr publisher,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rclcpp::SubscriptionBase::SharedPtr
|
||||
create_subscription(
|
||||
const std::string & topic_name,
|
||||
const rclcpp::SubscriptionFactory & subscription_factory,
|
||||
rcl_subscription_options_t & subscription_options,
|
||||
bool use_intra_process);
|
||||
const rcl_subscription_options_t & subscription_options,
|
||||
bool use_intra_process) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
add_subscription(
|
||||
rclcpp::SubscriptionBase::SharedPtr subscription,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group);
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeBaseInterface *
|
||||
get_node_base_interface() const override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeTopics)
|
||||
|
||||
NodeBaseInterface * node_base_;
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -50,14 +50,15 @@ public:
|
||||
create_publisher(
|
||||
const std::string & topic_name,
|
||||
const rclcpp::PublisherFactory & publisher_factory,
|
||||
rcl_publisher_options_t & publisher_options,
|
||||
const rcl_publisher_options_t & publisher_options,
|
||||
bool use_intra_process) = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
add_publisher(
|
||||
rclcpp::PublisherBase::SharedPtr publisher) = 0;
|
||||
rclcpp::PublisherBase::SharedPtr publisher,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -65,7 +66,7 @@ public:
|
||||
create_subscription(
|
||||
const std::string & topic_name,
|
||||
const rclcpp::SubscriptionFactory & subscription_factory,
|
||||
rcl_subscription_options_t & subscription_options,
|
||||
const rcl_subscription_options_t & subscription_options,
|
||||
bool use_intra_process) = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
@@ -74,6 +75,11 @@ public:
|
||||
add_subscription(
|
||||
rclcpp::SubscriptionBase::SharedPtr subscription,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rclcpp::node_interfaces::NodeBaseInterface *
|
||||
get_node_base_interface() const = 0;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
330
rclcpp/include/rclcpp/node_options.hpp
Normal file
330
rclcpp/include/rclcpp/node_options.hpp
Normal file
@@ -0,0 +1,330 @@
|
||||
// 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/publisher_options.hpp"
|
||||
#include "rclcpp/qos.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 = rclcpp::ParameterEventQoS
|
||||
* - with history setting and depth from rmw_qos_profile_parameter_events
|
||||
* - parameter_event_publisher_options = rclcpp::PublisherOptionsBase
|
||||
* - allow_undeclared_parameters = false
|
||||
* - automatically_declare_initial_parameters = false
|
||||
* - 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 the use_global_arguments flag.
|
||||
RCLCPP_PUBLIC
|
||||
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(bool use_global_arguments);
|
||||
|
||||
/// Return the use_intra_process_comms flag.
|
||||
RCLCPP_PUBLIC
|
||||
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(bool use_intra_process_comms);
|
||||
|
||||
/// Return the start_parameter_services flag.
|
||||
RCLCPP_PUBLIC
|
||||
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(bool start_parameter_services);
|
||||
|
||||
/// Return the start_parameter_event_publisher flag.
|
||||
RCLCPP_PUBLIC
|
||||
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(bool start_parameter_event_publisher);
|
||||
|
||||
/// Return a reference to the parameter_event_qos QoS.
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::QoS &
|
||||
parameter_event_qos() const;
|
||||
|
||||
/// Set the parameter_event_qos 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(const rclcpp::QoS & parameter_event_qos);
|
||||
|
||||
/// Return a reference to the parameter_event_publisher_options.
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::PublisherOptionsBase &
|
||||
parameter_event_publisher_options() const;
|
||||
|
||||
/// Set the parameter_event_publisher_options, return this for parameter idiom.
|
||||
/**
|
||||
* The QoS settings to be used for the parameter event publisher, if enabled.
|
||||
*
|
||||
* \todo(wjwwood): make this take/store an instance of
|
||||
* rclcpp::PublisherOptionsWithAllocator<Allocator>, but to do that requires
|
||||
* NodeOptions to also be templated based on the Allocator type.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
parameter_event_publisher_options(
|
||||
const rclcpp::PublisherOptionsBase & parameter_event_publisher_options);
|
||||
|
||||
/// Return the allow_undeclared_parameters flag.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
allow_undeclared_parameters() const;
|
||||
|
||||
/// Set the allow_undeclared_parameters, return this for parameter idiom.
|
||||
/**
|
||||
* If true, allow any parameter name to be set on the node without first
|
||||
* being declared.
|
||||
* Otherwise, setting an undeclared parameter will raise an exception.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
allow_undeclared_parameters(bool allow_undeclared_parameters);
|
||||
|
||||
/// Return the automatically_declare_initial_parameters flag.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
automatically_declare_initial_parameters() const;
|
||||
|
||||
/// Set the automatically_declare_initial_parameters, return this.
|
||||
/**
|
||||
* If true, automatically iterate through the node's initial parameters and
|
||||
* implicitly declare any that have not already been declared.
|
||||
* Otherwise, parameters passed to the node's initial_parameters, and/or the
|
||||
* global initial parameter values, which are not explicitly declared will
|
||||
* not appear on the node at all.
|
||||
* Already declared parameters will not be re-declared, and parameters
|
||||
* declared in this way will use the default constructed ParameterDescriptor.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
automatically_declare_initial_parameters(bool automatically_declare_initial_parameters);
|
||||
|
||||
/// 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};
|
||||
|
||||
rclcpp::QoS parameter_event_qos_ = rclcpp::ParameterEventsQoS(
|
||||
rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)
|
||||
);
|
||||
|
||||
rclcpp::PublisherOptionsBase parameter_event_publisher_options_ = rclcpp::PublisherOptionsBase();
|
||||
|
||||
bool allow_undeclared_parameters_ {false};
|
||||
|
||||
bool automatically_declare_initial_parameters_ {false};
|
||||
|
||||
rcl_allocator_t allocator_ {rcl_get_default_allocator()};
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NODE_OPTIONS_HPP_
|
||||
@@ -28,21 +28,59 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
class Parameter;
|
||||
|
||||
namespace node_interfaces
|
||||
{
|
||||
struct ParameterInfo;
|
||||
} // namespace node_interfaces
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// This helper function is required because you cannot do specialization on a
|
||||
// class method, so instead we specialize this template function and call it
|
||||
// from the unspecialized, but dependent, class method.
|
||||
template<typename T>
|
||||
auto
|
||||
get_value_helper(const rclcpp::Parameter * parameter);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// Structure to store an arbitrary parameter with templated get/set methods.
|
||||
class Parameter
|
||||
{
|
||||
public:
|
||||
/// Construct with an empty name and a parameter value of type rclcpp::PARAMETER_NOT_SET.
|
||||
RCLCPP_PUBLIC
|
||||
Parameter();
|
||||
|
||||
/// Construct with given name and a parameter value of type rclcpp::PARAMETER_NOT_SET.
|
||||
RCLCPP_PUBLIC
|
||||
explicit Parameter(const std::string & name);
|
||||
|
||||
/// Construct with given name and given parameter value.
|
||||
RCLCPP_PUBLIC
|
||||
Parameter(const std::string & name, const ParameterValue & value);
|
||||
|
||||
/// Construct with given name and given parameter value.
|
||||
template<typename ValueTypeT>
|
||||
explicit Parameter(const std::string & name, ValueTypeT value)
|
||||
Parameter(const std::string & name, ValueTypeT value)
|
||||
: Parameter(name, ParameterValue(value))
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit Parameter(const rclcpp::node_interfaces::ParameterInfo & parameter_info);
|
||||
|
||||
/// Equal operator.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator==(const Parameter & rhs) const;
|
||||
|
||||
/// Not equal operator.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator!=(const Parameter & rhs) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
ParameterType
|
||||
@@ -60,6 +98,11 @@ public:
|
||||
rcl_interfaces::msg::ParameterValue
|
||||
get_value_message() const;
|
||||
|
||||
/// Get the internal storage for the parameter value.
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::ParameterValue &
|
||||
get_parameter_value() const;
|
||||
|
||||
/// Get value of parameter using rclcpp::ParameterType as template argument.
|
||||
template<ParameterType ParamT>
|
||||
decltype(auto)
|
||||
@@ -71,10 +114,7 @@ public:
|
||||
/// Get value of parameter using c++ types as template argument.
|
||||
template<typename T>
|
||||
decltype(auto)
|
||||
get_value() const
|
||||
{
|
||||
return value_.get<T>();
|
||||
}
|
||||
get_value() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
@@ -142,6 +182,49 @@ RCLCPP_PUBLIC
|
||||
std::ostream &
|
||||
operator<<(std::ostream & os, const std::vector<Parameter> & parameters);
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
auto
|
||||
get_value_helper(const rclcpp::Parameter * parameter)
|
||||
{
|
||||
return parameter->get_parameter_value().get<T>();
|
||||
}
|
||||
|
||||
// Specialization allowing Parameter::get() to return a const ref to the parameter value object.
|
||||
template<>
|
||||
inline
|
||||
auto
|
||||
get_value_helper<rclcpp::ParameterValue>(const rclcpp::Parameter * parameter)
|
||||
{
|
||||
return parameter->get_parameter_value();
|
||||
}
|
||||
|
||||
// Specialization allowing Parameter::get() to return a const ref to the parameter itself.
|
||||
template<>
|
||||
inline
|
||||
auto
|
||||
get_value_helper<rclcpp::Parameter>(const rclcpp::Parameter * parameter)
|
||||
{
|
||||
// Use this lambda to ensure it's a const reference being returned (and not a copy).
|
||||
auto type_enforcing_lambda =
|
||||
[¶meter]() -> const rclcpp::Parameter & {
|
||||
return *parameter;
|
||||
};
|
||||
return type_enforcing_lambda();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T>
|
||||
decltype(auto)
|
||||
Parameter::get_value() const
|
||||
{
|
||||
// use the helper to specialize for the ParameterValue and Parameter cases.
|
||||
return detail::get_value_helper<T>(this);
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
namespace std
|
||||
|
||||
@@ -110,38 +110,33 @@ public:
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename SubscriptionT =
|
||||
rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent, Alloc>>
|
||||
typename AllocatorT = std::allocator<void>>
|
||||
typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
|
||||
on_parameter_event(CallbackT && callback)
|
||||
on_parameter_event(
|
||||
CallbackT && callback,
|
||||
const rclcpp::QoS & qos = (
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_default))
|
||||
),
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
|
||||
))
|
||||
{
|
||||
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
|
||||
auto msg_mem_strat =
|
||||
MessageMemoryStrategy<rcl_interfaces::msg::ParameterEvent, Alloc>::create_default();
|
||||
|
||||
using rcl_interfaces::msg::ParameterEvent;
|
||||
return rclcpp::create_subscription<
|
||||
ParameterEvent, CallbackT, Alloc, ParameterEvent, SubscriptionT>(
|
||||
this->node_topics_interface_.get(),
|
||||
return rclcpp::create_subscription<rcl_interfaces::msg::ParameterEvent>(
|
||||
this->node_topics_interface_,
|
||||
"parameter_events",
|
||||
qos,
|
||||
std::forward<CallbackT>(callback),
|
||||
rmw_qos_profile_default,
|
||||
nullptr, // group,
|
||||
false, // ignore_local_publications,
|
||||
false, // use_intra_process_comms_,
|
||||
msg_mem_strat,
|
||||
std::make_shared<Alloc>());
|
||||
options);
|
||||
}
|
||||
|
||||
RCLCPP_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)
|
||||
@@ -154,7 +149,7 @@ protected:
|
||||
wait_for_service_nanoseconds(std::chrono::nanoseconds timeout);
|
||||
|
||||
private:
|
||||
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface_;
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface_;
|
||||
rclcpp::Client<rcl_interfaces::srv::GetParameters>::SharedPtr get_parameters_client_;
|
||||
rclcpp::Client<rcl_interfaces::srv::GetParameterTypes>::SharedPtr
|
||||
get_parameter_types_client_;
|
||||
@@ -281,10 +276,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);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
explicit ParameterService(
|
||||
const std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
|
||||
const std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
|
||||
node_interfaces::NodeParametersInterface * node_params,
|
||||
rclcpp::node_interfaces::NodeParametersInterface * node_params,
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
|
||||
private:
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
enum ParameterType
|
||||
|
||||
enum ParameterType : uint8_t
|
||||
{
|
||||
PARAMETER_NOT_SET = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET,
|
||||
PARAMETER_BOOL = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
|
||||
@@ -45,11 +46,11 @@ enum ParameterType
|
||||
/// Return the name of a parameter type
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
to_string(const ParameterType type);
|
||||
to_string(ParameterType type);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::ostream &
|
||||
operator<<(std::ostream & os, const ParameterType type);
|
||||
operator<<(std::ostream & os, ParameterType type);
|
||||
|
||||
/// Indicate the parameter type does not match the expected type.
|
||||
class ParameterTypeException : public std::runtime_error
|
||||
@@ -129,10 +130,21 @@ public:
|
||||
rcl_interfaces::msg::ParameterValue
|
||||
to_value_msg() const;
|
||||
|
||||
/// Equal operator.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator==(const ParameterValue & rhs) const;
|
||||
|
||||
/// Not equal operator.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator!=(const ParameterValue & rhs) const;
|
||||
|
||||
// The following get() variants require the use of ParameterType
|
||||
|
||||
template<ParameterType type>
|
||||
typename std::enable_if<type == ParameterType::PARAMETER_BOOL, bool>::type
|
||||
constexpr
|
||||
typename std::enable_if<type == ParameterType::PARAMETER_BOOL, const bool &>::type
|
||||
get() const
|
||||
{
|
||||
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_BOOL) {
|
||||
@@ -142,7 +154,8 @@ public:
|
||||
}
|
||||
|
||||
template<ParameterType type>
|
||||
typename std::enable_if<type == ParameterType::PARAMETER_INTEGER, int64_t>::type
|
||||
constexpr
|
||||
typename std::enable_if<type == ParameterType::PARAMETER_INTEGER, const int64_t &>::type
|
||||
get() const
|
||||
{
|
||||
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER) {
|
||||
@@ -152,7 +165,8 @@ public:
|
||||
}
|
||||
|
||||
template<ParameterType type>
|
||||
typename std::enable_if<type == ParameterType::PARAMETER_DOUBLE, double>::type
|
||||
constexpr
|
||||
typename std::enable_if<type == ParameterType::PARAMETER_DOUBLE, const double &>::type
|
||||
get() const
|
||||
{
|
||||
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE) {
|
||||
@@ -162,6 +176,7 @@ public:
|
||||
}
|
||||
|
||||
template<ParameterType type>
|
||||
constexpr
|
||||
typename std::enable_if<type == ParameterType::PARAMETER_STRING, const std::string &>::type
|
||||
get() const
|
||||
{
|
||||
@@ -172,6 +187,7 @@ public:
|
||||
}
|
||||
|
||||
template<ParameterType type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
type == ParameterType::PARAMETER_BYTE_ARRAY, const std::vector<uint8_t> &>::type
|
||||
get() const
|
||||
@@ -183,6 +199,7 @@ public:
|
||||
}
|
||||
|
||||
template<ParameterType type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
type == ParameterType::PARAMETER_BOOL_ARRAY, const std::vector<bool> &>::type
|
||||
get() const
|
||||
@@ -194,6 +211,7 @@ public:
|
||||
}
|
||||
|
||||
template<ParameterType type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
type == ParameterType::PARAMETER_INTEGER_ARRAY, const std::vector<int64_t> &>::type
|
||||
get() const
|
||||
@@ -205,6 +223,7 @@ public:
|
||||
}
|
||||
|
||||
template<ParameterType type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
type == ParameterType::PARAMETER_DOUBLE_ARRAY, const std::vector<double> &>::type
|
||||
get() const
|
||||
@@ -216,6 +235,7 @@ public:
|
||||
}
|
||||
|
||||
template<ParameterType type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
type == ParameterType::PARAMETER_STRING_ARRAY, const std::vector<std::string> &>::type
|
||||
get() const
|
||||
@@ -229,28 +249,32 @@ public:
|
||||
// The following get() variants allow the use of primitive types
|
||||
|
||||
template<typename type>
|
||||
typename std::enable_if<std::is_same<type, bool>::value, bool>::type
|
||||
constexpr
|
||||
typename std::enable_if<std::is_same<type, bool>::value, const bool &>::type
|
||||
get() const
|
||||
{
|
||||
return get<ParameterType::PARAMETER_BOOL>();
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
std::is_integral<type>::value && !std::is_same<type, bool>::value, int64_t>::type
|
||||
std::is_integral<type>::value && !std::is_same<type, bool>::value, const int64_t &>::type
|
||||
get() const
|
||||
{
|
||||
return get<ParameterType::PARAMETER_INTEGER>();
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
typename std::enable_if<std::is_floating_point<type>::value, double>::type
|
||||
constexpr
|
||||
typename std::enable_if<std::is_floating_point<type>::value, const double &>::type
|
||||
get() const
|
||||
{
|
||||
return get<ParameterType::PARAMETER_DOUBLE>();
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<std::is_convertible<type, std::string>::value, const std::string &>::type
|
||||
get() const
|
||||
{
|
||||
@@ -258,6 +282,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
std::is_convertible<
|
||||
type, const std::vector<uint8_t> &>::value, const std::vector<uint8_t> &>::type
|
||||
@@ -267,6 +292,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
std::is_convertible<
|
||||
type, const std::vector<bool> &>::value, const std::vector<bool> &>::type
|
||||
@@ -276,6 +302,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
std::is_convertible<
|
||||
type, const std::vector<int64_t> &>::value, const std::vector<int64_t> &>::type
|
||||
@@ -285,6 +312,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
std::is_convertible<
|
||||
type, const std::vector<double> &>::value, const std::vector<double> &>::type
|
||||
@@ -294,6 +322,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
std::is_convertible<
|
||||
type, const std::vector<std::string> &>::value, const std::vector<std::string> &>::type
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/publisher.h"
|
||||
@@ -31,125 +32,15 @@
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/intra_process_manager.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
// Forward declaration is used for friend statement.
|
||||
namespace node_interfaces
|
||||
{
|
||||
class NodeTopicsInterface;
|
||||
}
|
||||
|
||||
class PublisherBase
|
||||
{
|
||||
friend ::rclcpp::node_interfaces::NodeTopicsInterface;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
|
||||
|
||||
/// Default constructor.
|
||||
/**
|
||||
* Typically, a publisher is not created through this method, but instead is created through a
|
||||
* call to `Node::create_publisher`.
|
||||
* \param[in] node_base A pointer to the NodeBaseInterface for the parent node.
|
||||
* \param[in] topic The topic that this publisher publishes on.
|
||||
* \param[in] type_support The type support structure for the type to be published.
|
||||
* \param[in] publisher_options QoS settings for this publisher.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
PublisherBase(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::string & topic,
|
||||
const rosidl_message_type_support_t & type_support,
|
||||
const rcl_publisher_options_t & publisher_options);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~PublisherBase();
|
||||
|
||||
/// Get the topic that this publisher publishes on.
|
||||
/** \return The topic name. */
|
||||
RCLCPP_PUBLIC
|
||||
const char *
|
||||
get_topic_name() const;
|
||||
|
||||
/// Get the queue size for this publisher.
|
||||
/** \return The queue size. */
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_queue_size() const;
|
||||
|
||||
/// Get the global identifier for this publisher (used in rmw and by DDS).
|
||||
/** \return The gid. */
|
||||
RCLCPP_PUBLIC
|
||||
const rmw_gid_t &
|
||||
get_gid() const;
|
||||
|
||||
/// Get the global identifier for this publisher used by intra-process communication.
|
||||
/** \return The intra-process gid. */
|
||||
RCLCPP_PUBLIC
|
||||
const rmw_gid_t &
|
||||
get_intra_process_gid() const;
|
||||
|
||||
/// Get the rcl publisher handle.
|
||||
/** \return The rcl publisher handle. */
|
||||
RCLCPP_PUBLIC
|
||||
rcl_publisher_t *
|
||||
get_publisher_handle();
|
||||
|
||||
/// Get the rcl publisher handle.
|
||||
/** \return The rcl publisher handle. */
|
||||
RCLCPP_PUBLIC
|
||||
const rcl_publisher_t *
|
||||
get_publisher_handle() const;
|
||||
|
||||
/// Compare this publisher to a gid.
|
||||
/**
|
||||
* Note that this function calls the next function.
|
||||
* \param[in] gid Reference to a gid.
|
||||
* \return True if the publisher's gid matches the input.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator==(const rmw_gid_t & gid) const;
|
||||
|
||||
/// Compare this publisher to a pointer gid.
|
||||
/**
|
||||
* A wrapper for comparing this publisher's gid to the input using rmw_compare_gids_equal.
|
||||
* \param[in] gid A pointer to a gid.
|
||||
* \return True if this publisher's gid matches the input.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator==(const rmw_gid_t * gid) const;
|
||||
|
||||
using StoreMessageCallbackT = std::function<uint64_t(uint64_t, void *, const std::type_info &)>;
|
||||
|
||||
/// 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,
|
||||
const rcl_publisher_options_t & intra_process_options);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<rcl_node_t> rcl_node_handle_;
|
||||
|
||||
rcl_publisher_t publisher_handle_ = rcl_get_zero_initialized_publisher();
|
||||
rcl_publisher_t intra_process_publisher_handle_ = rcl_get_zero_initialized_publisher();
|
||||
|
||||
uint64_t intra_process_publisher_id_;
|
||||
StoreMessageCallbackT store_intra_process_message_;
|
||||
|
||||
rmw_gid_t rmw_gid_;
|
||||
rmw_gid_t intra_process_rmw_gid_;
|
||||
};
|
||||
|
||||
/// A publisher publishes messages of any type to a topic.
|
||||
template<typename MessageT, typename Alloc = std::allocator<void>>
|
||||
class Publisher : public PublisherBase
|
||||
@@ -159,6 +50,7 @@ public:
|
||||
using MessageAlloc = typename MessageAllocTraits::allocator_type;
|
||||
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
|
||||
using MessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, Alloc>)
|
||||
|
||||
@@ -166,6 +58,7 @@ public:
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::string & topic,
|
||||
const rcl_publisher_options_t & publisher_options,
|
||||
const PublisherEventCallbacks & event_callbacks,
|
||||
const std::shared_ptr<MessageAlloc> & allocator)
|
||||
: PublisherBase(
|
||||
node_base,
|
||||
@@ -175,108 +68,99 @@ public:
|
||||
message_allocator_(allocator)
|
||||
{
|
||||
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
|
||||
|
||||
if (event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(event_callbacks.deadline_callback,
|
||||
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
|
||||
}
|
||||
if (event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(event_callbacks.liveliness_callback,
|
||||
RCL_PUBLISHER_LIVELINESS_LOST);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~Publisher()
|
||||
{}
|
||||
|
||||
mapped_ring_buffer::MappedRingBufferBase::SharedPtr
|
||||
make_mapped_ring_buffer(size_t size) const override
|
||||
{
|
||||
return mapped_ring_buffer::MappedRingBuffer<
|
||||
MessageT,
|
||||
typename Publisher<MessageT, Alloc>::MessageAlloc
|
||||
>::make_shared(size, this->get_allocator());
|
||||
}
|
||||
|
||||
/// Send a message to the topic for this publisher.
|
||||
/**
|
||||
* This function is templated on the input message type, MessageT.
|
||||
* \param[in] msg A shared pointer to the message to send.
|
||||
*/
|
||||
virtual void
|
||||
publish(std::unique_ptr<MessageT, MessageDeleter> & msg)
|
||||
publish(std::unique_ptr<MessageT, MessageDeleter> msg)
|
||||
{
|
||||
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
|
||||
// the correct type.
|
||||
// TODO(wjwwood):
|
||||
// investigate how to transfer the custom deleter (if there is one)
|
||||
// from the incoming unique_ptr through to the ipm's unique_ptr.
|
||||
// See: http://stackoverflow.com/questions/11002641/dynamic-casting-for-unique-ptr
|
||||
MessageT * msg_ptr = msg.get();
|
||||
msg.release();
|
||||
uint64_t message_seq =
|
||||
store_intra_process_message_(intra_process_publisher_id_, msg_ptr, typeid(MessageT));
|
||||
rcl_interfaces::msg::IntraProcessMessage ipm;
|
||||
ipm.publisher_id = intra_process_publisher_id_;
|
||||
ipm.message_sequence = message_seq;
|
||||
auto status = rcl_publish(&intra_process_publisher_handle_, &ipm);
|
||||
if (RCL_RET_PUBLISHER_INVALID == status) {
|
||||
rcl_reset_error(); // next call will reset error message if not context
|
||||
if (rcl_publisher_is_valid_except_context(&intra_process_publisher_handle_)) {
|
||||
rcl_context_t * context = rcl_publisher_get_context(&intra_process_publisher_handle_);
|
||||
if (nullptr != context && !rcl_context_is_valid(context)) {
|
||||
// publisher is invalid due to context being shutdown
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (RCL_RET_OK != status) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish intra process message");
|
||||
}
|
||||
if (!intra_process_is_enabled_) {
|
||||
this->do_inter_process_publish(msg.get());
|
||||
return;
|
||||
}
|
||||
// If an interprocess subscription exist, then the unique_ptr is promoted
|
||||
// to a shared_ptr and published.
|
||||
// This allows doing the intraprocess publish first and then doing the
|
||||
// interprocess publish, resulting in lower publish-to-subscribe latency.
|
||||
// It's not possible to do that with an unique_ptr,
|
||||
// as do_intra_process_publish takes the ownership of the message.
|
||||
uint64_t message_seq;
|
||||
bool inter_process_publish_needed =
|
||||
get_subscription_count() > get_intra_process_subscription_count();
|
||||
MessageSharedPtr shared_msg;
|
||||
if (inter_process_publish_needed) {
|
||||
shared_msg = std::move(msg);
|
||||
message_seq =
|
||||
store_intra_process_message(intra_process_publisher_id_, shared_msg);
|
||||
} else {
|
||||
// Always destroy the message, even if we don't consume it, for consistency.
|
||||
msg.reset();
|
||||
message_seq =
|
||||
store_intra_process_message(intra_process_publisher_id_, std::move(msg));
|
||||
}
|
||||
this->do_intra_process_publish(message_seq);
|
||||
if (inter_process_publish_needed) {
|
||||
this->do_inter_process_publish(shared_msg.get());
|
||||
}
|
||||
}
|
||||
|
||||
// Skip deprecated attribute in windows, as it raise a warning in template specialization.
|
||||
#if !defined(_WIN32)
|
||||
[[deprecated(
|
||||
"publishing an unique_ptr is prefered when using intra process communication."
|
||||
" If using a shared_ptr, use publish(*msg).")]]
|
||||
#endif
|
||||
virtual void
|
||||
publish(const std::shared_ptr<MessageT> & msg)
|
||||
publish(const std::shared_ptr<const MessageT> & msg)
|
||||
{
|
||||
// Avoid allocating when not using intra process.
|
||||
if (!store_intra_process_message_) {
|
||||
// In this case we're not using intra process.
|
||||
return this->do_inter_process_publish(msg.get());
|
||||
}
|
||||
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
|
||||
// TODO(wjwwood):
|
||||
// The intra process manager should probably also be able to store
|
||||
// shared_ptr's and do the "smart" thing based on other intra process
|
||||
// subscriptions. For now call the other publish().
|
||||
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
|
||||
MessageAllocTraits::construct(*message_allocator_.get(), ptr, *msg.get());
|
||||
MessageUniquePtr unique_msg(ptr, message_deleter_);
|
||||
return this->publish(unique_msg);
|
||||
}
|
||||
|
||||
virtual void
|
||||
publish(std::shared_ptr<const MessageT> msg)
|
||||
{
|
||||
// Avoid allocating when not using intra process.
|
||||
if (!store_intra_process_message_) {
|
||||
// In this case we're not using intra process.
|
||||
return this->do_inter_process_publish(msg.get());
|
||||
}
|
||||
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
|
||||
// TODO(wjwwood):
|
||||
// The intra process manager should probably also be able to store
|
||||
// shared_ptr's and do the "smart" thing based on other intra process
|
||||
// subscriptions. For now call the other publish().
|
||||
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
|
||||
MessageAllocTraits::construct(*message_allocator_.get(), ptr, *msg.get());
|
||||
MessageUniquePtr unique_msg(ptr, message_deleter_);
|
||||
return this->publish(unique_msg);
|
||||
publish(*msg);
|
||||
}
|
||||
|
||||
virtual void
|
||||
publish(const MessageT & msg)
|
||||
{
|
||||
// Avoid allocating when not using intra process.
|
||||
if (!store_intra_process_message_) {
|
||||
if (!intra_process_is_enabled_) {
|
||||
// In this case we're not using intra process.
|
||||
return this->do_inter_process_publish(&msg);
|
||||
}
|
||||
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
|
||||
// As the message is not const, a copy should be made.
|
||||
// A shared_ptr<const MessageT> could also be constructed here.
|
||||
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
|
||||
MessageAllocTraits::construct(*message_allocator_.get(), ptr, msg);
|
||||
MessageUniquePtr unique_msg(ptr, message_deleter_);
|
||||
return this->publish(unique_msg);
|
||||
this->publish(std::move(unique_msg));
|
||||
}
|
||||
|
||||
// Skip deprecated attribute in windows, as it raise a warning in template specialization.
|
||||
#if !defined(_WIN32)
|
||||
[[deprecated(
|
||||
"Use publish(*msg). Check against nullptr before calling if necessary.")]]
|
||||
#endif
|
||||
virtual void
|
||||
publish(const MessageT * msg)
|
||||
{
|
||||
@@ -287,22 +171,31 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
publish(const rcl_serialized_message_t * serialized_msg)
|
||||
publish(const rcl_serialized_message_t & serialized_msg)
|
||||
{
|
||||
if (store_intra_process_message_) {
|
||||
// TODO(Karsten1987): support serialized message passed by intraprocess
|
||||
throw std::runtime_error("storing serialized messages in intra process is not supported yet");
|
||||
}
|
||||
auto status = rcl_publish_serialized_message(&publisher_handle_, serialized_msg);
|
||||
if (RCL_RET_OK != status) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish serialized message");
|
||||
}
|
||||
return this->do_serialized_publish(&serialized_msg);
|
||||
}
|
||||
|
||||
// Skip deprecated attribute in windows, as it raise a warning in template specialization.
|
||||
#if !defined(_WIN32)
|
||||
[[deprecated(
|
||||
"Use publish(*serialized_msg). Check against nullptr before calling if necessary.")]]
|
||||
#endif
|
||||
void
|
||||
publish(const rcl_serialized_message_t * serialized_msg)
|
||||
{
|
||||
return this->do_serialized_publish(serialized_msg);
|
||||
}
|
||||
|
||||
// Skip deprecated attribute in windows, as it raise a warning in template specialization.
|
||||
#if !defined(_WIN32)
|
||||
[[deprecated(
|
||||
"Use publish(*serialized_msg). Check against nullptr before calling if necessary.")]]
|
||||
#endif
|
||||
void
|
||||
publish(std::shared_ptr<const rcl_serialized_message_t> serialized_msg)
|
||||
{
|
||||
return this->publish(serialized_msg.get());
|
||||
return this->do_serialized_publish(serialized_msg.get());
|
||||
}
|
||||
|
||||
std::shared_ptr<MessageAlloc> get_allocator() const
|
||||
@@ -314,7 +207,7 @@ protected:
|
||||
void
|
||||
do_inter_process_publish(const MessageT * msg)
|
||||
{
|
||||
auto status = rcl_publish(&publisher_handle_, msg);
|
||||
auto status = rcl_publish(&publisher_handle_, msg, nullptr);
|
||||
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_)) {
|
||||
@@ -330,6 +223,77 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
do_serialized_publish(const rcl_serialized_message_t * serialized_msg)
|
||||
{
|
||||
if (intra_process_is_enabled_) {
|
||||
// TODO(Karsten1987): support serialized message passed by intraprocess
|
||||
throw std::runtime_error("storing serialized messages in intra process is not supported yet");
|
||||
}
|
||||
auto status = rcl_publish_serialized_message(&publisher_handle_, serialized_msg, nullptr);
|
||||
if (RCL_RET_OK != status) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish serialized message");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
do_intra_process_publish(uint64_t message_seq)
|
||||
{
|
||||
rcl_interfaces::msg::IntraProcessMessage ipm;
|
||||
ipm.publisher_id = intra_process_publisher_id_;
|
||||
ipm.message_sequence = message_seq;
|
||||
auto status = rcl_publish(&intra_process_publisher_handle_, &ipm, nullptr);
|
||||
if (RCL_RET_PUBLISHER_INVALID == status) {
|
||||
rcl_reset_error(); // next call will reset error message if not context
|
||||
if (rcl_publisher_is_valid_except_context(&intra_process_publisher_handle_)) {
|
||||
rcl_context_t * context = rcl_publisher_get_context(&intra_process_publisher_handle_);
|
||||
if (nullptr != context && !rcl_context_is_valid(context)) {
|
||||
// publisher is invalid due to context being shutdown
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (RCL_RET_OK != status) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish intra process message");
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
store_intra_process_message(
|
||||
uint64_t publisher_id,
|
||||
std::shared_ptr<const MessageT> msg)
|
||||
{
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!ipm) {
|
||||
throw std::runtime_error(
|
||||
"intra process publish called after destruction of intra process manager");
|
||||
}
|
||||
if (!msg) {
|
||||
throw std::runtime_error("cannot publisher msg which is a null pointer");
|
||||
}
|
||||
uint64_t message_seq =
|
||||
ipm->template store_intra_process_message<MessageT, Alloc>(publisher_id, msg);
|
||||
return message_seq;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
store_intra_process_message(
|
||||
uint64_t publisher_id,
|
||||
std::unique_ptr<MessageT, MessageDeleter> msg)
|
||||
{
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!ipm) {
|
||||
throw std::runtime_error(
|
||||
"intra process publish called after destruction of intra process manager");
|
||||
}
|
||||
if (!msg) {
|
||||
throw std::runtime_error("cannot publisher msg which is a null pointer");
|
||||
}
|
||||
uint64_t message_seq =
|
||||
ipm->template store_intra_process_message<MessageT, Alloc>(publisher_id, std::move(msg));
|
||||
return message_seq;
|
||||
}
|
||||
|
||||
std::shared_ptr<MessageAlloc> message_allocator_;
|
||||
|
||||
MessageDeleter message_deleter_;
|
||||
|
||||
232
rclcpp/include/rclcpp/publisher_base.hpp
Normal file
232
rclcpp/include/rclcpp/publisher_base.hpp
Normal file
@@ -0,0 +1,232 @@
|
||||
// Copyright 2014 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__PUBLISHER_BASE_HPP_
|
||||
#define RCLCPP__PUBLISHER_BASE_HPP_
|
||||
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/publisher.h"
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/mapped_ring_buffer.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
// Forward declaration is used for friend statement.
|
||||
namespace node_interfaces
|
||||
{
|
||||
class NodeBaseInterface;
|
||||
class NodeTopicsInterface;
|
||||
}
|
||||
|
||||
namespace intra_process_manager
|
||||
{
|
||||
/**
|
||||
* IntraProcessManager is forward declared here, avoiding a circular inclusion between
|
||||
* `intra_process_manager.hpp` and `publisher_base.hpp`.
|
||||
*/
|
||||
class IntraProcessManager;
|
||||
}
|
||||
|
||||
class PublisherBase
|
||||
{
|
||||
friend ::rclcpp::node_interfaces::NodeTopicsInterface;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
|
||||
|
||||
/// Default constructor.
|
||||
/**
|
||||
* Typically, a publisher is not created through this method, but instead is created through a
|
||||
* call to `Node::create_publisher`.
|
||||
* \param[in] node_base A pointer to the NodeBaseInterface for the parent node.
|
||||
* \param[in] topic The topic that this publisher publishes on.
|
||||
* \param[in] type_support The type support structure for the type to be published.
|
||||
* \param[in] publisher_options QoS settings for this publisher.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
PublisherBase(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::string & topic,
|
||||
const rosidl_message_type_support_t & type_support,
|
||||
const rcl_publisher_options_t & publisher_options);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~PublisherBase();
|
||||
|
||||
/// Get the topic that this publisher publishes on.
|
||||
/** \return The topic name. */
|
||||
RCLCPP_PUBLIC
|
||||
const char *
|
||||
get_topic_name() const;
|
||||
|
||||
/// Get the queue size for this publisher.
|
||||
/** \return The queue size. */
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_queue_size() const;
|
||||
|
||||
/// Get the global identifier for this publisher (used in rmw and by DDS).
|
||||
/** \return The gid. */
|
||||
RCLCPP_PUBLIC
|
||||
const rmw_gid_t &
|
||||
get_gid() const;
|
||||
|
||||
/// Get the global identifier for this publisher used by intra-process communication.
|
||||
/** \return The intra-process gid. */
|
||||
RCLCPP_PUBLIC
|
||||
const rmw_gid_t &
|
||||
get_intra_process_gid() const;
|
||||
|
||||
/// Get the rcl publisher handle.
|
||||
/** \return The rcl publisher handle. */
|
||||
RCLCPP_PUBLIC
|
||||
rcl_publisher_t *
|
||||
get_publisher_handle();
|
||||
|
||||
/// Get the rcl publisher handle.
|
||||
/** \return The rcl publisher handle. */
|
||||
RCLCPP_PUBLIC
|
||||
const rcl_publisher_t *
|
||||
get_publisher_handle() const;
|
||||
|
||||
/// Get all the QoS event handlers associated with this publisher.
|
||||
/** \return The vector of QoS event handlers. */
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
|
||||
get_event_handlers() 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;
|
||||
|
||||
/// Manually assert that this Publisher is alive (for RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC).
|
||||
/**
|
||||
* If the rmw Liveliness policy is set to RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC, the creator
|
||||
* of this publisher may manually call `assert_liveliness` at some point in time to signal to the
|
||||
* rest of the system that this Node is still alive.
|
||||
*
|
||||
* \return `true` if the liveliness was asserted successfully, otherwise `false`
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
RCUTILS_WARN_UNUSED
|
||||
bool
|
||||
assert_liveliness() 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.
|
||||
* \param[in] gid Reference to a gid.
|
||||
* \return True if the publisher's gid matches the input.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator==(const rmw_gid_t & gid) const;
|
||||
|
||||
/// Compare this publisher to a pointer gid.
|
||||
/**
|
||||
* A wrapper for comparing this publisher's gid to the input using rmw_compare_gids_equal.
|
||||
* \param[in] gid A pointer to a gid.
|
||||
* \return True if this publisher's gid matches the input.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator==(const rmw_gid_t * gid) const;
|
||||
|
||||
using IntraProcessManagerSharedPtr =
|
||||
std::shared_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
|
||||
|
||||
/// Implementation utility function that creates a typed mapped ring buffer.
|
||||
RCLCPP_PUBLIC
|
||||
mapped_ring_buffer::MappedRingBufferBase::SharedPtr
|
||||
virtual make_mapped_ring_buffer(size_t size) const;
|
||||
|
||||
/// Implementation utility function used to setup intra process publishing after creation.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
setup_intra_process(
|
||||
uint64_t intra_process_publisher_id,
|
||||
IntraProcessManagerSharedPtr ipm,
|
||||
const rcl_publisher_options_t & intra_process_options);
|
||||
|
||||
protected:
|
||||
template<typename EventCallbackT>
|
||||
void
|
||||
add_event_handler(
|
||||
const EventCallbackT & callback,
|
||||
const rcl_publisher_event_type_t event_type)
|
||||
{
|
||||
auto handler = std::make_shared<QOSEventHandler<EventCallbackT>>(
|
||||
callback,
|
||||
rcl_publisher_event_init,
|
||||
&publisher_handle_,
|
||||
event_type);
|
||||
event_handlers_.emplace_back(handler);
|
||||
}
|
||||
|
||||
std::shared_ptr<rcl_node_t> rcl_node_handle_;
|
||||
|
||||
rcl_publisher_t publisher_handle_ = rcl_get_zero_initialized_publisher();
|
||||
rcl_publisher_t intra_process_publisher_handle_ = rcl_get_zero_initialized_publisher();
|
||||
|
||||
std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
|
||||
|
||||
using IntraProcessManagerWeakPtr =
|
||||
std::weak_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
|
||||
bool intra_process_is_enabled_;
|
||||
IntraProcessManagerWeakPtr weak_ipm_;
|
||||
uint64_t intra_process_publisher_id_;
|
||||
|
||||
rmw_gid_t rmw_gid_;
|
||||
rmw_gid_t intra_process_rmw_gid_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__PUBLISHER_BASE_HPP_
|
||||
@@ -49,94 +49,38 @@ struct PublisherFactory
|
||||
rclcpp::PublisherBase::SharedPtr(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::string & topic_name,
|
||||
rcl_publisher_options_t & publisher_options)>;
|
||||
const rcl_publisher_options_t & publisher_options)>;
|
||||
|
||||
PublisherFactoryFunction create_typed_publisher;
|
||||
|
||||
// Adds the PublisherBase to the intraprocess manager with the correctly
|
||||
// templated call to IntraProcessManager::store_intra_process_message.
|
||||
using AddPublisherToIntraProcessManagerFunction = std::function<
|
||||
uint64_t(
|
||||
rclcpp::intra_process_manager::IntraProcessManager * ipm,
|
||||
rclcpp::PublisherBase::SharedPtr publisher)>;
|
||||
|
||||
AddPublisherToIntraProcessManagerFunction add_publisher_to_intra_process_manager;
|
||||
|
||||
// Creates the callback function which is called on each
|
||||
// PublisherT::publish() and which handles the intra process transmission of
|
||||
// the message being published.
|
||||
using SharedPublishCallbackFactoryFunction = std::function<
|
||||
rclcpp::PublisherBase::StoreMessageCallbackT(
|
||||
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm)>;
|
||||
|
||||
SharedPublishCallbackFactoryFunction create_shared_publish_callback;
|
||||
};
|
||||
|
||||
/// Return a PublisherFactory with functions setup for creating a PublisherT<MessageT, Alloc>.
|
||||
template<typename MessageT, typename Alloc, typename PublisherT>
|
||||
PublisherFactory
|
||||
create_publisher_factory(std::shared_ptr<Alloc> allocator)
|
||||
create_publisher_factory(
|
||||
const PublisherEventCallbacks & event_callbacks,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
{
|
||||
PublisherFactory factory;
|
||||
|
||||
// factory function that creates a MessageT specific PublisherT
|
||||
factory.create_typed_publisher =
|
||||
[allocator](
|
||||
[event_callbacks, allocator](
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::string & topic_name,
|
||||
rcl_publisher_options_t & publisher_options) -> std::shared_ptr<PublisherT>
|
||||
const rcl_publisher_options_t & publisher_options
|
||||
) -> std::shared_ptr<PublisherT>
|
||||
{
|
||||
auto options_copy = publisher_options;
|
||||
auto message_alloc = std::make_shared<typename PublisherT::MessageAlloc>(*allocator.get());
|
||||
publisher_options.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc.get());
|
||||
options_copy.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc.get());
|
||||
|
||||
return std::make_shared<PublisherT>(node_base, topic_name, publisher_options, message_alloc);
|
||||
};
|
||||
|
||||
// function to add a publisher to the intra process manager
|
||||
factory.add_publisher_to_intra_process_manager =
|
||||
[](
|
||||
rclcpp::intra_process_manager::IntraProcessManager * ipm,
|
||||
rclcpp::PublisherBase::SharedPtr publisher) -> uint64_t
|
||||
{
|
||||
return ipm->add_publisher<MessageT, Alloc>(std::dynamic_pointer_cast<PublisherT>(publisher));
|
||||
};
|
||||
|
||||
// function to create a shared publish callback std::function
|
||||
using StoreMessageCallbackT = rclcpp::PublisherBase::StoreMessageCallbackT;
|
||||
factory.create_shared_publish_callback =
|
||||
[](rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm) -> StoreMessageCallbackT
|
||||
{
|
||||
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = ipm;
|
||||
|
||||
// this function is called on each call to publish() and handles storing
|
||||
// of the published message in the intra process manager
|
||||
auto shared_publish_callback =
|
||||
[weak_ipm](uint64_t publisher_id, void * msg, const std::type_info & type_info) -> uint64_t
|
||||
{
|
||||
auto ipm = weak_ipm.lock();
|
||||
if (!ipm) {
|
||||
// TODO(wjwwood): should this just return silently? Or maybe return with a warning?
|
||||
throw std::runtime_error(
|
||||
"intra process publish called after destruction of intra process manager");
|
||||
}
|
||||
if (!msg) {
|
||||
throw std::runtime_error("cannot publisher msg which is a null pointer");
|
||||
}
|
||||
auto & message_type_info = typeid(MessageT);
|
||||
if (message_type_info != type_info) {
|
||||
throw std::runtime_error(
|
||||
std::string("published type '") + type_info.name() +
|
||||
"' is incompatible from the publisher type '" + message_type_info.name() + "'");
|
||||
}
|
||||
MessageT * typed_message_ptr = static_cast<MessageT *>(msg);
|
||||
using MessageDeleter = typename Publisher<MessageT, Alloc>::MessageDeleter;
|
||||
std::unique_ptr<MessageT, MessageDeleter> unique_msg(typed_message_ptr);
|
||||
uint64_t message_seq =
|
||||
ipm->store_intra_process_message<MessageT, Alloc>(publisher_id, unique_msg);
|
||||
return message_seq;
|
||||
};
|
||||
|
||||
return shared_publish_callback;
|
||||
return std::make_shared<PublisherT>(
|
||||
node_base,
|
||||
topic_name,
|
||||
options_copy,
|
||||
event_callbacks,
|
||||
message_alloc);
|
||||
};
|
||||
|
||||
// return the factory now that it is populated
|
||||
|
||||
78
rclcpp/include/rclcpp/publisher_options.hpp
Normal file
78
rclcpp/include/rclcpp/publisher_options.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
// 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/callback_group.hpp"
|
||||
#include "rclcpp/intra_process_setting.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rcl/publisher.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Non-templated part of PublisherOptionsWithAllocator<Allocator>.
|
||||
struct PublisherOptionsBase
|
||||
{
|
||||
/// Setting to explicitly set intraprocess communications.
|
||||
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault;
|
||||
|
||||
/// Callbacks for various events related to publishers.
|
||||
PublisherEventCallbacks event_callbacks;
|
||||
|
||||
/// Callback group in which the waitable items from the publisher should be placed.
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group;
|
||||
};
|
||||
|
||||
/// Structure containing optional configuration for Publishers.
|
||||
template<typename Allocator>
|
||||
struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
{
|
||||
/// Optional custom allocator.
|
||||
std::shared_ptr<Allocator> allocator = nullptr;
|
||||
|
||||
PublisherOptionsWithAllocator<Allocator>() {}
|
||||
|
||||
/// Constructor using base class as input.
|
||||
explicit PublisherOptionsWithAllocator(const PublisherOptionsBase & publisher_options_base)
|
||||
: PublisherOptionsBase(publisher_options_base)
|
||||
{}
|
||||
|
||||
/// Convert this class, and a rclcpp::QoS, into an rcl_publisher_options_t.
|
||||
template<typename MessageT>
|
||||
rcl_publisher_options_t
|
||||
to_rcl_publisher_options(const rclcpp::QoS & qos) const
|
||||
{
|
||||
rcl_publisher_options_t result;
|
||||
using AllocatorTraits = std::allocator_traits<Allocator>;
|
||||
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
|
||||
auto message_alloc = std::make_shared<MessageAllocatorT>(*allocator.get());
|
||||
result.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc);
|
||||
result.qos = qos.get_rmw_qos_profile();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
using PublisherOptions = PublisherOptionsWithAllocator<std::allocator<void>>;
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__PUBLISHER_OPTIONS_HPP_
|
||||
203
rclcpp/include/rclcpp/qos.hpp
Normal file
203
rclcpp/include/rclcpp/qos.hpp
Normal file
@@ -0,0 +1,203 @@
|
||||
// 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__QOS_HPP_
|
||||
#define RCLCPP__QOS_HPP_
|
||||
|
||||
#include <rmw/qos_profiles.h>
|
||||
#include <rmw/types.h>
|
||||
|
||||
#include "rclcpp/duration.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// QoS initialization values, cannot be created directly, use KeepAll or KeepLast instead.
|
||||
struct RCLCPP_PUBLIC QoSInitialization
|
||||
{
|
||||
rmw_qos_history_policy_t history_policy;
|
||||
size_t depth;
|
||||
|
||||
/// Constructor which takes both a history policy and a depth (even if it would be unused).
|
||||
QoSInitialization(rmw_qos_history_policy_t history_policy_arg, size_t depth_arg);
|
||||
|
||||
/// Create a QoSInitialization from an existing rmw_qos_profile_t, using its history and depth.
|
||||
static
|
||||
QoSInitialization
|
||||
from_rmw(const rmw_qos_profile_t & rmw_qos);
|
||||
};
|
||||
|
||||
/// Use to initialize the QoS with the keep_all history setting.
|
||||
struct RCLCPP_PUBLIC KeepAll : public rclcpp::QoSInitialization
|
||||
{
|
||||
KeepAll();
|
||||
};
|
||||
|
||||
/// Use to initialize the QoS with the keep_last history setting and the given depth.
|
||||
struct RCLCPP_PUBLIC KeepLast : public rclcpp::QoSInitialization
|
||||
{
|
||||
explicit KeepLast(size_t depth);
|
||||
};
|
||||
|
||||
/// Encapsulation of Quality of Service settings.
|
||||
class RCLCPP_PUBLIC QoS
|
||||
{
|
||||
public:
|
||||
/// Constructor which allows you to construct a QoS by giving the only required settings.
|
||||
explicit
|
||||
QoS(
|
||||
const QoSInitialization & qos_initialization,
|
||||
const rmw_qos_profile_t & initial_profile = rmw_qos_profile_default);
|
||||
|
||||
/// Conversion constructor to ease construction in the common case of just specifying depth.
|
||||
/**
|
||||
* Convenience constructor, equivalent to QoS(KeepLast(history_depth)).
|
||||
*/
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
QoS(size_t history_depth); // NOLINT(runtime/explicit): conversion constructor
|
||||
|
||||
/// Return the rmw qos profile.
|
||||
rmw_qos_profile_t &
|
||||
get_rmw_qos_profile();
|
||||
|
||||
/// Return the rmw qos profile.
|
||||
const rmw_qos_profile_t &
|
||||
get_rmw_qos_profile() const;
|
||||
|
||||
/// Set the history policy.
|
||||
QoS &
|
||||
history(rmw_qos_history_policy_t history);
|
||||
|
||||
/// Set the history to keep last.
|
||||
QoS &
|
||||
keep_last(size_t depth);
|
||||
|
||||
/// Set the history to keep all.
|
||||
QoS &
|
||||
keep_all();
|
||||
|
||||
/// Set the reliability setting.
|
||||
QoS &
|
||||
reliability(rmw_qos_reliability_policy_t reliability);
|
||||
|
||||
/// Set the reliability setting to reliable.
|
||||
QoS &
|
||||
reliable();
|
||||
|
||||
/// Set the reliability setting to best effort.
|
||||
QoS &
|
||||
best_effort();
|
||||
|
||||
/// Set the durability setting.
|
||||
QoS &
|
||||
durability(rmw_qos_durability_policy_t durability);
|
||||
|
||||
/// Set the durability setting to volatile.
|
||||
QoS &
|
||||
volitile();
|
||||
|
||||
/// Set the durability setting to transient local.
|
||||
QoS &
|
||||
transient_local();
|
||||
|
||||
/// Set the deadline setting.
|
||||
QoS &
|
||||
deadline(rmw_time_t deadline);
|
||||
|
||||
/// Set the deadline setting, rclcpp::Duration.
|
||||
QoS &
|
||||
deadline(const rclcpp::Duration & deadline);
|
||||
|
||||
/// Set the lifespan setting.
|
||||
QoS &
|
||||
lifespan(rmw_time_t lifespan);
|
||||
|
||||
/// Set the lifespan setting, rclcpp::Duration.
|
||||
QoS &
|
||||
lifespan(const rclcpp::Duration & lifespan);
|
||||
|
||||
/// Set the liveliness setting.
|
||||
QoS &
|
||||
liveliness(rmw_qos_liveliness_policy_t liveliness);
|
||||
|
||||
/// Set the liveliness_lease_duration setting.
|
||||
QoS &
|
||||
liveliness_lease_duration(rmw_time_t liveliness_lease_duration);
|
||||
|
||||
/// Set the liveliness_lease_duration setting, rclcpp::Duration.
|
||||
QoS &
|
||||
liveliness_lease_duration(const rclcpp::Duration & liveliness_lease_duration);
|
||||
|
||||
/// Set the avoid_ros_namespace_conventions setting.
|
||||
QoS &
|
||||
avoid_ros_namespace_conventions(bool avoid_ros_namespace_conventions);
|
||||
|
||||
private:
|
||||
rmw_qos_profile_t rmw_qos_profile_;
|
||||
};
|
||||
|
||||
class RCLCPP_PUBLIC SensorDataQoS : public QoS
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
SensorDataQoS(
|
||||
const QoSInitialization & qos_initialization = (
|
||||
QoSInitialization::from_rmw(rmw_qos_profile_sensor_data)
|
||||
));
|
||||
};
|
||||
|
||||
class RCLCPP_PUBLIC ParametersQoS : public QoS
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
ParametersQoS(
|
||||
const QoSInitialization & qos_initialization = (
|
||||
QoSInitialization::from_rmw(rmw_qos_profile_parameters)
|
||||
));
|
||||
};
|
||||
|
||||
class RCLCPP_PUBLIC ServicesQoS : public QoS
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
ServicesQoS(
|
||||
const QoSInitialization & qos_initialization = (
|
||||
QoSInitialization::from_rmw(rmw_qos_profile_services_default)
|
||||
));
|
||||
};
|
||||
|
||||
class RCLCPP_PUBLIC ParameterEventsQoS : public QoS
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
ParameterEventsQoS(
|
||||
const QoSInitialization & qos_initialization = (
|
||||
QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)
|
||||
));
|
||||
};
|
||||
|
||||
class RCLCPP_PUBLIC SystemDefaultsQoS : public QoS
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
SystemDefaultsQoS(
|
||||
const QoSInitialization & qos_initialization = (
|
||||
QoSInitialization::from_rmw(rmw_qos_profile_system_default)
|
||||
));
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__QOS_HPP_
|
||||
126
rclcpp/include/rclcpp/qos_event.hpp
Normal file
126
rclcpp/include/rclcpp/qos_event.hpp
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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__QOS_EVENT_HPP_
|
||||
#define RCLCPP__QOS_EVENT_HPP_
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
using QOSDeadlineRequestedInfo = rmw_requested_deadline_missed_status_t;
|
||||
using QOSDeadlineOfferedInfo = rmw_offered_deadline_missed_status_t;
|
||||
using QOSLivelinessChangedInfo = rmw_liveliness_changed_status_t;
|
||||
using QOSLivelinessLostInfo = rmw_liveliness_lost_status_t;
|
||||
|
||||
using QOSDeadlineRequestedCallbackType = std::function<void (QOSDeadlineRequestedInfo &)>;
|
||||
using QOSDeadlineOfferedCallbackType = std::function<void (QOSDeadlineOfferedInfo &)>;
|
||||
using QOSLivelinessChangedCallbackType = std::function<void (QOSLivelinessChangedInfo &)>;
|
||||
using QOSLivelinessLostCallbackType = std::function<void (QOSLivelinessLostInfo &)>;
|
||||
|
||||
/// Contains callbacks for various types of events a Publisher can receive from the middleware.
|
||||
struct PublisherEventCallbacks
|
||||
{
|
||||
QOSDeadlineOfferedCallbackType deadline_callback;
|
||||
QOSLivelinessLostCallbackType liveliness_callback;
|
||||
};
|
||||
|
||||
/// Contains callbacks for non-message events that a Subscription can receive from the middleware.
|
||||
struct SubscriptionEventCallbacks
|
||||
{
|
||||
QOSDeadlineRequestedCallbackType deadline_callback;
|
||||
QOSLivelinessChangedCallbackType liveliness_callback;
|
||||
};
|
||||
|
||||
class QOSEventHandlerBase : public Waitable
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~QOSEventHandlerBase();
|
||||
|
||||
/// Get the number of ready events
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_number_of_ready_events() override;
|
||||
|
||||
/// Add the Waitable to a wait set.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) override;
|
||||
|
||||
/// Check if the Waitable is ready.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set) override;
|
||||
|
||||
protected:
|
||||
rcl_event_t event_handle_;
|
||||
size_t wait_set_event_index_;
|
||||
};
|
||||
|
||||
template<typename EventCallbackT>
|
||||
class QOSEventHandler : public QOSEventHandlerBase
|
||||
{
|
||||
public:
|
||||
template<typename InitFuncT, typename ParentHandleT, typename EventTypeEnum>
|
||||
QOSEventHandler(
|
||||
const EventCallbackT & callback,
|
||||
InitFuncT init_func,
|
||||
ParentHandleT parent_handle,
|
||||
EventTypeEnum event_type)
|
||||
: event_callback_(callback)
|
||||
{
|
||||
event_handle_ = rcl_get_zero_initialized_event();
|
||||
rcl_ret_t ret = init_func(&event_handle_, parent_handle, event_type);
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create event");
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute any entities of the Waitable that are ready.
|
||||
void
|
||||
execute() override
|
||||
{
|
||||
EventCallbackInfoT callback_info;
|
||||
|
||||
rcl_ret_t ret = rcl_take_event(&event_handle_, &callback_info);
|
||||
if (ret != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Couldn't take event info: %s", rcl_get_error_string().str);
|
||||
return;
|
||||
}
|
||||
|
||||
event_callback_(callback_info);
|
||||
}
|
||||
|
||||
private:
|
||||
using EventCallbackInfoT = typename std::remove_reference<typename
|
||||
rclcpp::function_traits::function_traits<EventCallbackT>::template argument_type<0>>::type;
|
||||
|
||||
EventCallbackT event_callback_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__QOS_EVENT_HPP_
|
||||
@@ -430,6 +430,15 @@ public:
|
||||
return number_of_services;
|
||||
}
|
||||
|
||||
size_t number_of_ready_events() const
|
||||
{
|
||||
size_t number_of_events = 0;
|
||||
for (auto waitable : waitable_handles_) {
|
||||
number_of_events += waitable->get_number_of_ready_events();
|
||||
}
|
||||
return number_of_events;
|
||||
}
|
||||
|
||||
size_t number_of_ready_clients() const
|
||||
{
|
||||
size_t number_of_clients = client_handles_.size();
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/subscription.h"
|
||||
@@ -32,11 +34,15 @@
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
#include "rclcpp/intra_process_manager.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/message_memory_strategy.hpp"
|
||||
#include "rclcpp/subscription_base.hpp"
|
||||
#include "rclcpp/subscription_traits.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -46,101 +52,6 @@ namespace node_interfaces
|
||||
class NodeTopicsInterface;
|
||||
} // namespace node_interfaces
|
||||
|
||||
/// Virtual base class for subscriptions. This pattern allows us to iterate over different template
|
||||
/// specializations of Subscription, among other things.
|
||||
class SubscriptionBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)
|
||||
|
||||
/// Default constructor.
|
||||
/**
|
||||
* \param[in] node_handle The rcl representation of the node that owns this subscription.
|
||||
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
|
||||
* \param[in] topic_name Name of the topic to subscribe to.
|
||||
* \param[in] subscription_options options for the subscription.
|
||||
* \param[in] is_serialized is true if the message will be delivered still serialized
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
SubscriptionBase(
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
const rosidl_message_type_support_t & type_support_handle,
|
||||
const std::string & topic_name,
|
||||
const rcl_subscription_options_t & subscription_options,
|
||||
bool is_serialized = false);
|
||||
|
||||
/// Default destructor.
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~SubscriptionBase();
|
||||
|
||||
/// Get the topic that this subscription is subscribed on.
|
||||
RCLCPP_PUBLIC
|
||||
const char *
|
||||
get_topic_name() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rcl_subscription_t>
|
||||
get_subscription_handle();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::shared_ptr<rcl_subscription_t>
|
||||
get_subscription_handle() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual const std::shared_ptr<rcl_subscription_t>
|
||||
get_intra_process_subscription_handle() const;
|
||||
|
||||
/// Borrow a new message.
|
||||
/** \return Shared pointer to the fresh message. */
|
||||
virtual std::shared_ptr<void>
|
||||
create_message() = 0;
|
||||
|
||||
/// Borrow a new serialized message
|
||||
/** \return Shared pointer to a rcl_message_serialized_t. */
|
||||
virtual std::shared_ptr<rcl_serialized_message_t>
|
||||
create_serialized_message() = 0;
|
||||
|
||||
/// Check if we need to handle the message, and execute the callback if we do.
|
||||
/**
|
||||
* \param[in] message Shared pointer to the message to handle.
|
||||
* \param[in] message_info Metadata associated with this message.
|
||||
*/
|
||||
virtual void
|
||||
handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info) = 0;
|
||||
|
||||
/// Return the message borrowed in create_message.
|
||||
/** \param[in] message Shared pointer to the returned message. */
|
||||
virtual void
|
||||
return_message(std::shared_ptr<void> & message) = 0;
|
||||
|
||||
/// Return the message borrowed in create_serialized_message.
|
||||
/** \param[in] message Shared pointer to the returned message. */
|
||||
virtual void
|
||||
return_serialized_message(std::shared_ptr<rcl_serialized_message_t> & message) = 0;
|
||||
|
||||
virtual void
|
||||
handle_intra_process_message(
|
||||
rcl_interfaces::msg::IntraProcessMessage & ipm,
|
||||
const rmw_message_info_t & message_info) = 0;
|
||||
|
||||
const rosidl_message_type_support_t &
|
||||
get_message_type_support_handle() const;
|
||||
|
||||
bool
|
||||
is_serialized() 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_;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(SubscriptionBase)
|
||||
|
||||
rosidl_message_type_support_t type_support_;
|
||||
bool is_serialized_;
|
||||
};
|
||||
|
||||
/// Subscription implementation, templated on the type of message this subscription receives.
|
||||
template<
|
||||
typename CallbackMessageT,
|
||||
@@ -153,6 +64,7 @@ public:
|
||||
using MessageAllocTraits = allocator::AllocRebind<CallbackMessageT, Alloc>;
|
||||
using MessageAlloc = typename MessageAllocTraits::allocator_type;
|
||||
using MessageDeleter = allocator::Deleter<MessageAlloc, CallbackMessageT>;
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const CallbackMessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<CallbackMessageT, MessageDeleter>;
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
|
||||
@@ -174,6 +86,7 @@ public:
|
||||
const std::string & topic_name,
|
||||
const rcl_subscription_options_t & subscription_options,
|
||||
AnySubscriptionCallback<CallbackMessageT, Alloc> callback,
|
||||
const SubscriptionEventCallbacks & event_callbacks,
|
||||
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, Alloc>::SharedPtr
|
||||
memory_strategy = message_memory_strategy::MessageMemoryStrategy<CallbackMessageT,
|
||||
Alloc>::create_default())
|
||||
@@ -184,10 +97,17 @@ public:
|
||||
subscription_options,
|
||||
rclcpp::subscription_traits::is_serialized_subscription_argument<CallbackMessageT>::value),
|
||||
any_callback_(callback),
|
||||
message_memory_strategy_(memory_strategy),
|
||||
get_intra_process_message_callback_(nullptr),
|
||||
matches_any_intra_process_publishers_(nullptr)
|
||||
{}
|
||||
message_memory_strategy_(memory_strategy)
|
||||
{
|
||||
if (event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(event_callbacks.deadline_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
|
||||
}
|
||||
if (event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(event_callbacks.liveliness_callback,
|
||||
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
/// Support dynamically setting the message memory strategy.
|
||||
/**
|
||||
@@ -217,12 +137,10 @@ public:
|
||||
|
||||
void handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info)
|
||||
{
|
||||
if (matches_any_intra_process_publishers_) {
|
||||
if (matches_any_intra_process_publishers_(&message_info.publisher_gid)) {
|
||||
// In this case, the message will be delivered via intra process and
|
||||
// we should ignore this copy of the message.
|
||||
return;
|
||||
}
|
||||
if (matches_any_intra_process_publishers(&message_info.publisher_gid)) {
|
||||
// In this case, the message will be delivered via intra process and
|
||||
// we should ignore this copy of the message.
|
||||
return;
|
||||
}
|
||||
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
|
||||
any_callback_.dispatch(typed_message, message_info);
|
||||
@@ -245,85 +163,109 @@ public:
|
||||
rcl_interfaces::msg::IntraProcessMessage & ipm,
|
||||
const rmw_message_info_t & message_info)
|
||||
{
|
||||
if (!get_intra_process_message_callback_) {
|
||||
if (!use_intra_process_) {
|
||||
// throw std::runtime_error(
|
||||
// "handle_intra_process_message called before setup_intra_process");
|
||||
// TODO(wjwwood): for now, this could mean that intra process was just not enabled.
|
||||
// However, this can only really happen if this node has it disabled, but the other doesn't.
|
||||
return;
|
||||
}
|
||||
MessageUniquePtr msg;
|
||||
get_intra_process_message_callback_(
|
||||
ipm.publisher_id,
|
||||
ipm.message_sequence,
|
||||
intra_process_subscription_id_,
|
||||
msg);
|
||||
if (!msg) {
|
||||
// This either occurred because the publisher no longer exists or the
|
||||
// message requested is no longer being stored.
|
||||
// TODO(wjwwood): should we notify someone of this? log error, log warning?
|
||||
return;
|
||||
}
|
||||
any_callback_.dispatch_intra_process(msg, message_info);
|
||||
}
|
||||
|
||||
using GetMessageCallbackType =
|
||||
std::function<void (uint64_t, uint64_t, uint64_t, MessageUniquePtr &)>;
|
||||
using MatchesAnyPublishersCallbackType = std::function<bool (const rmw_gid_t *)>;
|
||||
|
||||
/// Implemenation detail.
|
||||
void setup_intra_process(
|
||||
uint64_t intra_process_subscription_id,
|
||||
GetMessageCallbackType get_message_callback,
|
||||
MatchesAnyPublishersCallbackType matches_any_publisher_callback,
|
||||
const rcl_subscription_options_t & intra_process_options)
|
||||
{
|
||||
std::string intra_process_topic_name = std::string(get_topic_name()) + "/_intra";
|
||||
rcl_ret_t ret = rcl_subscription_init(
|
||||
intra_process_subscription_handle_.get(),
|
||||
node_handle_.get(),
|
||||
rclcpp::type_support::get_intra_process_message_msg_type_support(),
|
||||
intra_process_topic_name.c_str(),
|
||||
&intra_process_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
|
||||
auto rcl_node_handle = node_handle_.get();
|
||||
// this will throw on any validation problem
|
||||
rcl_reset_error();
|
||||
expand_topic_or_service_name(
|
||||
intra_process_topic_name,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle));
|
||||
if (any_callback_.use_take_shared_method()) {
|
||||
ConstMessageSharedPtr msg;
|
||||
take_intra_process_message(
|
||||
ipm.publisher_id,
|
||||
ipm.message_sequence,
|
||||
intra_process_subscription_id_,
|
||||
msg);
|
||||
if (!msg) {
|
||||
// This either occurred because the publisher no longer exists or the
|
||||
// message requested is no longer being stored.
|
||||
RCLCPP_WARN(get_logger("rclcpp"),
|
||||
"Intra process message not longer being stored when trying to handle it");
|
||||
return;
|
||||
}
|
||||
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create intra process subscription");
|
||||
any_callback_.dispatch_intra_process(msg, message_info);
|
||||
} else {
|
||||
MessageUniquePtr msg;
|
||||
take_intra_process_message(
|
||||
ipm.publisher_id,
|
||||
ipm.message_sequence,
|
||||
intra_process_subscription_id_,
|
||||
msg);
|
||||
if (!msg) {
|
||||
// This either occurred because the publisher no longer exists or the
|
||||
// message requested is no longer being stored.
|
||||
RCLCPP_WARN(get_logger("rclcpp"),
|
||||
"Intra process message not longer being stored when trying to handle it");
|
||||
return;
|
||||
}
|
||||
any_callback_.dispatch_intra_process(std::move(msg), message_info);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// Implemenation detail.
|
||||
const std::shared_ptr<rcl_subscription_t>
|
||||
get_intra_process_subscription_handle() const
|
||||
{
|
||||
if (!get_intra_process_message_callback_) {
|
||||
if (!use_intra_process_) {
|
||||
return nullptr;
|
||||
}
|
||||
return intra_process_subscription_handle_;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
take_intra_process_message(
|
||||
uint64_t publisher_id,
|
||||
uint64_t message_sequence,
|
||||
uint64_t subscription_id,
|
||||
MessageUniquePtr & message)
|
||||
{
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!ipm) {
|
||||
throw std::runtime_error(
|
||||
"intra process take called after destruction of intra process manager");
|
||||
}
|
||||
ipm->template take_intra_process_message<CallbackMessageT, Alloc>(
|
||||
publisher_id, message_sequence, subscription_id, message);
|
||||
}
|
||||
|
||||
void
|
||||
take_intra_process_message(
|
||||
uint64_t publisher_id,
|
||||
uint64_t message_sequence,
|
||||
uint64_t subscription_id,
|
||||
ConstMessageSharedPtr & message)
|
||||
{
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!ipm) {
|
||||
throw std::runtime_error(
|
||||
"intra process take called after destruction of intra process manager");
|
||||
}
|
||||
ipm->template take_intra_process_message<CallbackMessageT, Alloc>(
|
||||
publisher_id, message_sequence, subscription_id, message);
|
||||
}
|
||||
|
||||
bool
|
||||
matches_any_intra_process_publishers(const rmw_gid_t * sender_gid)
|
||||
{
|
||||
if (!use_intra_process_) {
|
||||
return false;
|
||||
}
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!ipm) {
|
||||
throw std::runtime_error(
|
||||
"intra process publisher check called "
|
||||
"after destruction of intra process manager");
|
||||
}
|
||||
return ipm->matches_any_publishers(sender_gid);
|
||||
}
|
||||
|
||||
RCLCPP_DISABLE_COPY(Subscription)
|
||||
|
||||
AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
|
||||
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, Alloc>::SharedPtr
|
||||
message_memory_strategy_;
|
||||
|
||||
GetMessageCallbackType get_intra_process_message_callback_;
|
||||
MatchesAnyPublishersCallbackType matches_any_intra_process_publishers_;
|
||||
uint64_t intra_process_subscription_id_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
189
rclcpp/include/rclcpp/subscription_base.hpp
Normal file
189
rclcpp/include/rclcpp/subscription_base.hpp
Normal file
@@ -0,0 +1,189 @@
|
||||
// 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_BASE_HPP_
|
||||
#define RCLCPP__SUBSCRIPTION_BASE_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/subscription.h"
|
||||
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace node_interfaces
|
||||
{
|
||||
class NodeTopicsInterface;
|
||||
} // namespace node_interfaces
|
||||
|
||||
namespace intra_process_manager
|
||||
{
|
||||
/**
|
||||
* IntraProcessManager is forward declared here, avoiding a circular inclusion between
|
||||
* `intra_process_manager.hpp` and `subscription_base.hpp`.
|
||||
*/
|
||||
class IntraProcessManager;
|
||||
}
|
||||
|
||||
/// Virtual base class for subscriptions. This pattern allows us to iterate over different template
|
||||
/// specializations of Subscription, among other things.
|
||||
class SubscriptionBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)
|
||||
|
||||
/// Default constructor.
|
||||
/**
|
||||
* \param[in] node_handle The rcl representation of the node that owns this subscription.
|
||||
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
|
||||
* \param[in] topic_name Name of the topic to subscribe to.
|
||||
* \param[in] subscription_options options for the subscription.
|
||||
* \param[in] is_serialized is true if the message will be delivered still serialized
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
SubscriptionBase(
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
const rosidl_message_type_support_t & type_support_handle,
|
||||
const std::string & topic_name,
|
||||
const rcl_subscription_options_t & subscription_options,
|
||||
bool is_serialized = false);
|
||||
|
||||
/// Default destructor.
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~SubscriptionBase();
|
||||
|
||||
/// Get the topic that this subscription is subscribed on.
|
||||
RCLCPP_PUBLIC
|
||||
const char *
|
||||
get_topic_name() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rcl_subscription_t>
|
||||
get_subscription_handle();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::shared_ptr<rcl_subscription_t>
|
||||
get_subscription_handle() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual const std::shared_ptr<rcl_subscription_t>
|
||||
get_intra_process_subscription_handle() const;
|
||||
|
||||
/// Get all the QoS event handlers associated with this subscription.
|
||||
/** \return The vector of QoS event handlers. */
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
|
||||
get_event_handlers() const;
|
||||
|
||||
/// Borrow a new message.
|
||||
/** \return Shared pointer to the fresh message. */
|
||||
virtual std::shared_ptr<void>
|
||||
create_message() = 0;
|
||||
|
||||
/// Borrow a new serialized message
|
||||
/** \return Shared pointer to a rcl_message_serialized_t. */
|
||||
virtual std::shared_ptr<rcl_serialized_message_t>
|
||||
create_serialized_message() = 0;
|
||||
|
||||
/// Check if we need to handle the message, and execute the callback if we do.
|
||||
/**
|
||||
* \param[in] message Shared pointer to the message to handle.
|
||||
* \param[in] message_info Metadata associated with this message.
|
||||
*/
|
||||
virtual void
|
||||
handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info) = 0;
|
||||
|
||||
/// Return the message borrowed in create_message.
|
||||
/** \param[in] message Shared pointer to the returned message. */
|
||||
virtual void
|
||||
return_message(std::shared_ptr<void> & message) = 0;
|
||||
|
||||
/// Return the message borrowed in create_serialized_message.
|
||||
/** \param[in] message Shared pointer to the returned message. */
|
||||
virtual void
|
||||
return_serialized_message(std::shared_ptr<rcl_serialized_message_t> & message) = 0;
|
||||
|
||||
virtual void
|
||||
handle_intra_process_message(
|
||||
rcl_interfaces::msg::IntraProcessMessage & ipm,
|
||||
const rmw_message_info_t & message_info) = 0;
|
||||
|
||||
const rosidl_message_type_support_t &
|
||||
get_message_type_support_handle() const;
|
||||
|
||||
bool
|
||||
is_serialized() const;
|
||||
|
||||
/// Get matching publisher count.
|
||||
/** \return The number of publishers on this topic. */
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_publisher_count() const;
|
||||
|
||||
using IntraProcessManagerWeakPtr =
|
||||
std::weak_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
|
||||
|
||||
/// Implemenation detail.
|
||||
void setup_intra_process(
|
||||
uint64_t intra_process_subscription_id,
|
||||
IntraProcessManagerWeakPtr weak_ipm,
|
||||
const rcl_subscription_options_t & intra_process_options);
|
||||
|
||||
protected:
|
||||
template<typename EventCallbackT>
|
||||
void
|
||||
add_event_handler(
|
||||
const EventCallbackT & callback,
|
||||
const rcl_subscription_event_type_t event_type)
|
||||
{
|
||||
auto handler = std::make_shared<QOSEventHandler<EventCallbackT>>(
|
||||
callback,
|
||||
rcl_subscription_event_init,
|
||||
get_subscription_handle().get(),
|
||||
event_type);
|
||||
event_handlers_.emplace_back(handler);
|
||||
}
|
||||
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
std::shared_ptr<rcl_subscription_t> subscription_handle_;
|
||||
std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;
|
||||
|
||||
std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
|
||||
|
||||
bool use_intra_process_;
|
||||
IntraProcessManagerWeakPtr weak_ipm_;
|
||||
uint64_t intra_process_subscription_id_;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(SubscriptionBase)
|
||||
|
||||
rosidl_message_type_support_t type_support_;
|
||||
bool is_serialized_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__SUBSCRIPTION_BASE_HPP_
|
||||
@@ -51,7 +51,7 @@ struct SubscriptionFactory
|
||||
rclcpp::SubscriptionBase::SharedPtr(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::string & topic_name,
|
||||
rcl_subscription_options_t & subscription_options)>;
|
||||
const rcl_subscription_options_t & subscription_options)>;
|
||||
|
||||
SubscriptionFactoryFunction create_typed_subscription;
|
||||
|
||||
@@ -75,6 +75,7 @@ template<
|
||||
SubscriptionFactory
|
||||
create_subscription_factory(
|
||||
CallbackT && callback,
|
||||
const SubscriptionEventCallbacks & event_callbacks,
|
||||
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT, Alloc>::SharedPtr
|
||||
msg_mem_strat,
|
||||
@@ -91,13 +92,14 @@ create_subscription_factory(
|
||||
|
||||
// factory function that creates a MessageT specific SubscriptionT
|
||||
factory.create_typed_subscription =
|
||||
[allocator, msg_mem_strat, any_subscription_callback, message_alloc](
|
||||
[allocator, msg_mem_strat, any_subscription_callback, event_callbacks, message_alloc](
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::string & topic_name,
|
||||
rcl_subscription_options_t & subscription_options
|
||||
const rcl_subscription_options_t & subscription_options
|
||||
) -> rclcpp::SubscriptionBase::SharedPtr
|
||||
{
|
||||
subscription_options.allocator =
|
||||
auto options_copy = subscription_options;
|
||||
options_copy.allocator =
|
||||
rclcpp::allocator::get_rcl_allocator<CallbackMessageT>(*message_alloc.get());
|
||||
|
||||
using rclcpp::Subscription;
|
||||
@@ -107,70 +109,14 @@ create_subscription_factory(
|
||||
node_base->get_shared_rcl_node_handle(),
|
||||
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
|
||||
topic_name,
|
||||
subscription_options,
|
||||
options_copy,
|
||||
any_subscription_callback,
|
||||
event_callbacks,
|
||||
msg_mem_strat);
|
||||
auto sub_base_ptr = std::dynamic_pointer_cast<SubscriptionBase>(sub);
|
||||
return sub_base_ptr;
|
||||
};
|
||||
|
||||
// function that will setup intra process communications for the subscription
|
||||
factory.setup_intra_process =
|
||||
[message_alloc](
|
||||
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm,
|
||||
rclcpp::SubscriptionBase::SharedPtr subscription,
|
||||
const rcl_subscription_options_t & subscription_options)
|
||||
{
|
||||
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = ipm;
|
||||
uint64_t intra_process_subscription_id = ipm->add_subscription(subscription);
|
||||
|
||||
auto intra_process_options = rcl_subscription_get_default_options();
|
||||
intra_process_options.allocator = rclcpp::allocator::get_rcl_allocator<CallbackMessageT>(
|
||||
*message_alloc.get());
|
||||
intra_process_options.qos = subscription_options.qos;
|
||||
intra_process_options.ignore_local_publications = false;
|
||||
|
||||
// function that will be called to take a MessageT from the intra process manager
|
||||
auto take_intra_process_message_func =
|
||||
[weak_ipm](
|
||||
uint64_t publisher_id,
|
||||
uint64_t message_sequence,
|
||||
uint64_t subscription_id,
|
||||
typename rclcpp::Subscription<CallbackMessageT, Alloc>::MessageUniquePtr & message)
|
||||
{
|
||||
auto ipm = weak_ipm.lock();
|
||||
if (!ipm) {
|
||||
// TODO(wjwwood): should this just return silently? Or return with a logged warning?
|
||||
throw std::runtime_error(
|
||||
"intra process take called after destruction of intra process manager");
|
||||
}
|
||||
ipm->take_intra_process_message<CallbackMessageT, Alloc>(
|
||||
publisher_id, message_sequence, subscription_id, message);
|
||||
};
|
||||
|
||||
// function that is called to see if the publisher id matches any local publishers
|
||||
auto matches_any_publisher_func =
|
||||
[weak_ipm](const rmw_gid_t * sender_gid) -> bool
|
||||
{
|
||||
auto ipm = weak_ipm.lock();
|
||||
if (!ipm) {
|
||||
throw std::runtime_error(
|
||||
"intra process publisher check called "
|
||||
"after destruction of intra process manager");
|
||||
}
|
||||
return ipm->matches_any_publishers(sender_gid);
|
||||
};
|
||||
|
||||
auto typed_sub_ptr = std::dynamic_pointer_cast<SubscriptionT>(subscription);
|
||||
typed_sub_ptr->setup_intra_process(
|
||||
intra_process_subscription_id,
|
||||
take_intra_process_message_func,
|
||||
matches_any_publisher_func,
|
||||
intra_process_options
|
||||
);
|
||||
};
|
||||
// end definition of factory function to setup intra process
|
||||
|
||||
// return the factory now that it is populated
|
||||
return factory;
|
||||
}
|
||||
|
||||
79
rclcpp/include/rclcpp/subscription_options.hpp
Normal file
79
rclcpp/include/rclcpp/subscription_options.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
// 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/callback_group.hpp"
|
||||
#include "rclcpp/intra_process_setting.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Non-template base class for subscription options.
|
||||
struct SubscriptionOptionsBase
|
||||
{
|
||||
/// Callbacks for events related to this subscription.
|
||||
SubscriptionEventCallbacks event_callbacks;
|
||||
/// 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;
|
||||
/// Setting to explicitly set intraprocess communications.
|
||||
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault;
|
||||
};
|
||||
|
||||
/// Structure containing optional configuration for Subscriptions.
|
||||
template<typename Allocator>
|
||||
struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
|
||||
{
|
||||
/// Optional custom allocator.
|
||||
std::shared_ptr<Allocator> allocator = nullptr;
|
||||
|
||||
SubscriptionOptionsWithAllocator<Allocator>() {}
|
||||
|
||||
/// Constructor using base class as input.
|
||||
explicit SubscriptionOptionsWithAllocator(
|
||||
const SubscriptionOptionsBase & subscription_options_base)
|
||||
: SubscriptionOptionsBase(subscription_options_base)
|
||||
{}
|
||||
|
||||
/// Convert this class, with a rclcpp::QoS, into an rcl_subscription_options_t.
|
||||
template<typename MessageT>
|
||||
rcl_subscription_options_t
|
||||
to_rcl_subscription_options(const rclcpp::QoS & qos) const
|
||||
{
|
||||
rcl_subscription_options_t result;
|
||||
using AllocatorTraits = std::allocator_traits<Allocator>;
|
||||
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
|
||||
auto message_alloc = std::make_shared<MessageAllocatorT>(*allocator.get());
|
||||
result.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc);
|
||||
result.ignore_local_publications = this->ignore_local_publications;
|
||||
result.qos = qos.get_rmw_qos_profile();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
using SubscriptionOptions = SubscriptionOptionsWithAllocator<std::allocator<void>>;
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__SUBSCRIPTION_OPTIONS_HPP_
|
||||
@@ -18,9 +18,13 @@
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rcl/types.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
class QoS;
|
||||
|
||||
namespace subscription_traits
|
||||
{
|
||||
|
||||
@@ -69,7 +73,20 @@ template<typename MessageT, typename Deleter>
|
||||
struct extract_message_type<std::unique_ptr<MessageT, Deleter>>: extract_message_type<MessageT>
|
||||
{};
|
||||
|
||||
template<typename CallbackT>
|
||||
template<
|
||||
typename CallbackT,
|
||||
// Do not attempt if CallbackT is an integer (mistaken for depth)
|
||||
typename = std::enable_if_t<!std::is_integral<
|
||||
std::remove_cv_t<std::remove_reference_t<CallbackT>>>::value>,
|
||||
// Do not attempt if CallbackT is a QoS (mistaken for qos)
|
||||
typename = std::enable_if_t<!std::is_base_of<
|
||||
rclcpp::QoS,
|
||||
std::remove_cv_t<std::remove_reference_t<CallbackT>>>::value>,
|
||||
// Do not attempt if CallbackT is a rmw_qos_profile_t (mistaken for qos profile)
|
||||
typename = std::enable_if_t<!std::is_same<
|
||||
rmw_qos_profile_t,
|
||||
std::remove_cv_t<std::remove_reference_t<CallbackT>>>::value>
|
||||
>
|
||||
struct has_message_type : extract_message_type<
|
||||
typename rclcpp::function_traits::function_traits<CallbackT>::template argument_type<0>>
|
||||
{};
|
||||
|
||||
@@ -58,6 +58,16 @@ public:
|
||||
void
|
||||
cancel();
|
||||
|
||||
/// Return the timer cancellation state.
|
||||
/**
|
||||
* \return true if the timer has been cancelled, false otherwise
|
||||
* \throws std::runtime_error if the rcl_get_error_state returns 0
|
||||
* \throws RCLErrorBase some child class exception based on ret
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_canceled();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
reset();
|
||||
|
||||
@@ -18,17 +18,14 @@
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/init_options.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rmw/macros.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace std
|
||||
@@ -104,6 +101,7 @@ uninstall_signal_handlers();
|
||||
*
|
||||
* \sa rclcpp::Context::init() for more details on arguments and possible exceptions
|
||||
* \returns Members of the argument vector that are not ROS arguments.
|
||||
* \throws anything remove_ros_arguments can throw
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<std::string>
|
||||
@@ -122,6 +120,8 @@ init_and_remove_ros_arguments(
|
||||
* \param[in] argc Number of arguments.
|
||||
* \param[in] argv Argument vector.
|
||||
* \returns Members of the argument vector that are not ROS arguments.
|
||||
* \throws anything throw_from_rcl_error can throw
|
||||
* \throws rclcpp::exceptions::RCLErrorBase if the parsing fails
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<std::string>
|
||||
|
||||
@@ -61,6 +61,17 @@ public:
|
||||
size_t
|
||||
get_number_of_ready_clients();
|
||||
|
||||
/// Get the number of ready events
|
||||
/**
|
||||
* Returns a value of 0 by default.
|
||||
* This should be overridden if the Waitable contains one or more events.
|
||||
* \return The number of events associated with the Waitable.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
get_number_of_ready_events();
|
||||
|
||||
/// Get the number of ready services
|
||||
/**
|
||||
* Returns a value of 0 by default.
|
||||
@@ -88,6 +99,7 @@ public:
|
||||
/**
|
||||
* \param[in] wait_set A handle to the wait set to add the Waitable to.
|
||||
* \return `true` if the Waitable is added successfully, `false` otherwise.
|
||||
* \throws rclcpp::execptions::RCLError from rcl_wait_set_add_*()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
|
||||
@@ -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.2</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
|
||||
<license>Apache License 2.0</license>
|
||||
|
||||
77
rclcpp/resource/interface_traits.hpp.em
Normal file
77
rclcpp/resource/interface_traits.hpp.em
Normal file
@@ -0,0 +1,77 @@
|
||||
// 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_INTERFACES__INTERFACE_TRAITS_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__INTERFACE_TRAITS_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
@{
|
||||
node_interfaces = [
|
||||
'node_base_interface',
|
||||
'node_clock_interface',
|
||||
'node_graph_interface',
|
||||
'node_logging_interface',
|
||||
'node_parameters_interface',
|
||||
'node_services_interface',
|
||||
'node_time_source_interface',
|
||||
'node_timers_interface',
|
||||
'node_topics_interface',
|
||||
'node_waitables_interface',
|
||||
]
|
||||
|
||||
node_interface_types = [
|
||||
'NodeBaseInterface',
|
||||
'NodeClockInterface',
|
||||
'NodeGraphInterface',
|
||||
'NodeLoggingInterface',
|
||||
'NodeParametersInterface',
|
||||
'NodeServicesInterface',
|
||||
'NodeTimeSourceInterface',
|
||||
'NodeTimersInterface',
|
||||
'NodeTopicsInterface',
|
||||
'NodeWaitablesInterface',
|
||||
]
|
||||
|
||||
assert (len(node_interfaces) == len(node_interface_types))
|
||||
}@
|
||||
|
||||
@[for interface_ in node_interfaces]@
|
||||
#include "rclcpp/node_interfaces/@(interface_).hpp"
|
||||
@[end for]@
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
@[for (interface_, type_) in zip(node_interfaces, node_interface_types)]@
|
||||
using @(interface_)_getter_t = std::shared_ptr<rclcpp::node_interfaces::@(type_)>;
|
||||
|
||||
template<class T, typename = void>
|
||||
struct has_@(interface_) : std::false_type
|
||||
{};
|
||||
|
||||
template<class T>
|
||||
struct has_@(interface_)<
|
||||
T, typename std::enable_if<
|
||||
std::is_same<
|
||||
@(interface_)_getter_t, decltype(std::declval<T>().get_@(interface_)())>::value>::type> : std::true_type
|
||||
{};
|
||||
|
||||
@[end for]@
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
#endif // RCLCPP__NODE_INTERFACES__INTERFACE_TRAITS_HPP_
|
||||
@@ -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<typename std::remove_cv<decltype(logger)>::type>::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 =
|
||||
|
||||
@@ -14,14 +14,6 @@
|
||||
|
||||
#include "rclcpp/clock.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "builtin_interfaces/msg/time.hpp"
|
||||
|
||||
#include "rcl/time.h"
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
|
||||
#include "rcutils/logging_macros.h"
|
||||
@@ -30,8 +22,8 @@ namespace rclcpp
|
||||
{
|
||||
|
||||
JumpHandler::JumpHandler(
|
||||
std::function<void()> pre_callback,
|
||||
std::function<void(const rcl_time_jump_t &)> post_callback,
|
||||
pre_callback_t pre_callback,
|
||||
post_callback_t post_callback,
|
||||
const rcl_jump_threshold_t & threshold)
|
||||
: pre_callback(pre_callback),
|
||||
post_callback(post_callback),
|
||||
@@ -43,8 +35,7 @@ Clock::Clock(rcl_clock_type_t clock_type)
|
||||
allocator_ = rcl_get_default_allocator();
|
||||
auto ret = rcl_clock_init(clock_type, &rcl_clock_, &allocator_);
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "could not get current time stamp");
|
||||
exceptions::throw_from_rcl_error(ret, "could not get current time stamp");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +54,7 @@ Clock::now()
|
||||
|
||||
auto ret = rcl_clock_get_now(&rcl_clock_, &now.rcl_time_.nanoseconds);
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "could not get current time stamp");
|
||||
exceptions::throw_from_rcl_error(ret, "could not get current time stamp");
|
||||
}
|
||||
|
||||
return now;
|
||||
@@ -78,23 +68,23 @@ Clock::ros_time_is_active()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_enabled;
|
||||
bool is_enabled = false;
|
||||
auto ret = rcl_is_enabled_ros_time_override(&rcl_clock_, &is_enabled);
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
exceptions::throw_from_rcl_error(
|
||||
ret, "Failed to check ros_time_override_status");
|
||||
}
|
||||
return is_enabled;
|
||||
}
|
||||
|
||||
rcl_clock_t *
|
||||
Clock::get_clock_handle()
|
||||
Clock::get_clock_handle() noexcept
|
||||
{
|
||||
return &rcl_clock_;
|
||||
}
|
||||
|
||||
rcl_clock_type_t
|
||||
Clock::get_clock_type()
|
||||
Clock::get_clock_type() const noexcept
|
||||
{
|
||||
return rcl_clock_.type;
|
||||
}
|
||||
@@ -105,7 +95,10 @@ Clock::on_time_jump(
|
||||
bool before_jump,
|
||||
void * user_data)
|
||||
{
|
||||
rclcpp::JumpHandler * handler = static_cast<rclcpp::JumpHandler *>(user_data);
|
||||
const auto * handler = static_cast<JumpHandler *>(user_data);
|
||||
if (nullptr == handler) {
|
||||
return;
|
||||
}
|
||||
if (before_jump && handler->pre_callback) {
|
||||
handler->pre_callback();
|
||||
} else if (!before_jump && handler->post_callback) {
|
||||
@@ -113,31 +106,30 @@ Clock::on_time_jump(
|
||||
}
|
||||
}
|
||||
|
||||
rclcpp::JumpHandler::SharedPtr
|
||||
JumpHandler::SharedPtr
|
||||
Clock::create_jump_callback(
|
||||
std::function<void()> pre_callback,
|
||||
std::function<void(const rcl_time_jump_t &)> post_callback,
|
||||
JumpHandler::pre_callback_t pre_callback,
|
||||
JumpHandler::post_callback_t post_callback,
|
||||
const rcl_jump_threshold_t & threshold)
|
||||
{
|
||||
// Allocate a new jump handler
|
||||
auto handler = new rclcpp::JumpHandler(pre_callback, post_callback, threshold);
|
||||
JumpHandler::UniquePtr handler(new JumpHandler(pre_callback, post_callback, threshold));
|
||||
if (nullptr == handler) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(RCL_RET_BAD_ALLOC, "Failed to allocate jump handler");
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
// Try to add the jump callback to the clock
|
||||
rcl_ret_t ret = rcl_clock_add_jump_callback(&rcl_clock_, threshold,
|
||||
rclcpp::Clock::on_time_jump, handler);
|
||||
Clock::on_time_jump, handler.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
delete handler;
|
||||
handler = NULL;
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to add time jump callback");
|
||||
exceptions::throw_from_rcl_error(ret, "Failed to add time jump callback");
|
||||
}
|
||||
|
||||
// *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 {
|
||||
rcl_ret_t ret = rcl_clock_remove_jump_callback(&rcl_clock_, rclcpp::Clock::on_time_jump,
|
||||
// TODO(dorezyuk) UB, if the clock leaves scope before the JumpHandler
|
||||
return JumpHandler::SharedPtr(handler.release(), [this](JumpHandler * handler) noexcept {
|
||||
rcl_ret_t ret = rcl_clock_remove_jump_callback(&rcl_clock_, Clock::on_time_jump,
|
||||
handler);
|
||||
delete handler;
|
||||
handler = NULL;
|
||||
|
||||
@@ -65,10 +65,6 @@ Duration::Duration(const rcl_duration_t & duration)
|
||||
// noop
|
||||
}
|
||||
|
||||
Duration::~Duration()
|
||||
{
|
||||
}
|
||||
|
||||
Duration::operator builtin_interfaces::msg::Duration() const
|
||||
{
|
||||
builtin_interfaces::msg::Duration msg_duration;
|
||||
@@ -226,4 +222,15 @@ Duration::seconds() const
|
||||
return std::chrono::duration<double>(std::chrono::nanoseconds(rcl_duration_.nanoseconds)).count();
|
||||
}
|
||||
|
||||
rmw_time_t
|
||||
Duration::to_rmw_time() const
|
||||
{
|
||||
// reuse conversion logic from msg creation
|
||||
builtin_interfaces::msg::Duration msg = *this;
|
||||
rmw_time_t result;
|
||||
result.sec = msg.sec;
|
||||
result.nsec = msg.nanosec;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -39,8 +39,8 @@ NameValidationError::format_error(
|
||||
return msg;
|
||||
}
|
||||
|
||||
void
|
||||
throw_from_rcl_error(
|
||||
std::exception_ptr
|
||||
from_rcl_error(
|
||||
rcl_ret_t ret,
|
||||
const std::string & prefix,
|
||||
const rcl_error_state_t * error_state,
|
||||
@@ -55,9 +55,9 @@ throw_from_rcl_error(
|
||||
if (!error_state) {
|
||||
throw std::runtime_error("rcl error state is not set");
|
||||
}
|
||||
std::string formated_prefix = prefix;
|
||||
std::string formatted_prefix = prefix;
|
||||
if (!prefix.empty()) {
|
||||
formated_prefix += ": ";
|
||||
formatted_prefix += ": ";
|
||||
}
|
||||
RCLErrorBase base_exc(ret, error_state);
|
||||
if (reset_error) {
|
||||
@@ -65,14 +65,28 @@ throw_from_rcl_error(
|
||||
}
|
||||
switch (ret) {
|
||||
case RCL_RET_BAD_ALLOC:
|
||||
throw RCLBadAlloc(base_exc);
|
||||
return std::make_exception_ptr(RCLBadAlloc(base_exc));
|
||||
case RCL_RET_INVALID_ARGUMENT:
|
||||
throw RCLInvalidArgument(base_exc, formated_prefix);
|
||||
return std::make_exception_ptr(RCLInvalidArgument(base_exc, formatted_prefix));
|
||||
default:
|
||||
throw RCLError(base_exc, formated_prefix);
|
||||
return std::make_exception_ptr(RCLError(base_exc, formatted_prefix));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
throw_from_rcl_error(
|
||||
rcl_ret_t ret,
|
||||
const std::string & prefix,
|
||||
const rcl_error_state_t * error_state,
|
||||
void (* reset_error)())
|
||||
{
|
||||
// We expect this to either throw a standard error,
|
||||
// or to generate an error pointer (which is caught
|
||||
// in err, and immediately thrown)
|
||||
auto err = from_rcl_error(ret, prefix, error_state, reset_error);
|
||||
std::rethrow_exception(err);
|
||||
}
|
||||
|
||||
RCLErrorBase::RCLErrorBase(rcl_ret_t ret, const rcl_error_state_t * error_state)
|
||||
: ret(ret), message(error_state->message), file(error_state->file), line(error_state->line_number),
|
||||
formatted_message(rcl_get_error_string().str)
|
||||
|
||||
@@ -60,7 +60,11 @@ Executor::Executor(const ExecutorArgs & args)
|
||||
// Store the context for later use.
|
||||
context_ = args.context;
|
||||
|
||||
ret = rcl_wait_set_init(&wait_set_, 0, 2, 0, 0, 0, context_->get_rcl_context().get(), allocator);
|
||||
ret = rcl_wait_set_init(
|
||||
&wait_set_,
|
||||
0, 2, 0, 0, 0, 0,
|
||||
context_->get_rcl_context().get(),
|
||||
allocator);
|
||||
if (RCL_RET_OK != ret) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
@@ -304,7 +308,7 @@ Executor::execute_subscription(
|
||||
auto serialized_msg = subscription->create_serialized_message();
|
||||
auto ret = rcl_take_serialized_message(
|
||||
subscription->get_subscription_handle().get(),
|
||||
serialized_msg.get(), &message_info);
|
||||
serialized_msg.get(), &message_info, nullptr);
|
||||
if (RCL_RET_OK == ret) {
|
||||
auto void_serialized_msg = std::static_pointer_cast<void>(serialized_msg);
|
||||
subscription->handle_message(void_serialized_msg, message_info);
|
||||
@@ -320,7 +324,7 @@ Executor::execute_subscription(
|
||||
std::shared_ptr<void> message = subscription->create_message();
|
||||
auto ret = rcl_take(
|
||||
subscription->get_subscription_handle().get(),
|
||||
message.get(), &message_info);
|
||||
message.get(), &message_info, nullptr);
|
||||
if (RCL_RET_OK == ret) {
|
||||
subscription->handle_message(message, message_info);
|
||||
} else if (RCL_RET_SUBSCRIPTION_TAKE_FAILED != ret) {
|
||||
@@ -343,7 +347,8 @@ Executor::execute_intra_process_subscription(
|
||||
rcl_ret_t status = rcl_take(
|
||||
subscription->get_intra_process_subscription_handle().get(),
|
||||
&ipm,
|
||||
&message_info);
|
||||
&message_info,
|
||||
nullptr);
|
||||
|
||||
if (status == RCL_RET_OK) {
|
||||
message_info.from_intra_process = true;
|
||||
@@ -434,7 +439,8 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
|
||||
rcl_ret_t ret = rcl_wait_set_resize(
|
||||
&wait_set_, memory_strategy_->number_of_ready_subscriptions(),
|
||||
memory_strategy_->number_of_guard_conditions(), memory_strategy_->number_of_ready_timers(),
|
||||
memory_strategy_->number_of_ready_clients(), memory_strategy_->number_of_ready_services());
|
||||
memory_strategy_->number_of_ready_clients(), memory_strategy_->number_of_ready_services(),
|
||||
memory_strategy_->number_of_ready_events());
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw std::runtime_error(
|
||||
std::string("Couldn't resize the wait set : ") + rcl_get_error_string().str);
|
||||
|
||||
@@ -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,11 +95,14 @@ 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);
|
||||
}
|
||||
}
|
||||
// Clear the callback_group to prevent the AnyExecutable destructor from
|
||||
// resetting the callback group `can_be_taken_from`
|
||||
any_exec.callback_group.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ GraphListener::start_if_not_started()
|
||||
0, // number_of_timers
|
||||
0, // number_of_clients
|
||||
0, // number_of_services
|
||||
0, // number_of_events
|
||||
this->parent_context_->get_rcl_context().get(),
|
||||
rcl_get_default_allocator());
|
||||
if (RCL_RET_OK != ret) {
|
||||
@@ -145,7 +146,7 @@ GraphListener::run_loop()
|
||||
const size_t node_graph_interfaces_size = node_graph_interfaces_.size();
|
||||
// Add 2 for the interrupt and shutdown guard conditions
|
||||
if (wait_set_.size_of_guard_conditions < (node_graph_interfaces_size + 2)) {
|
||||
ret = rcl_wait_set_resize(&wait_set_, 0, node_graph_interfaces_size + 2, 0, 0, 0);
|
||||
ret = rcl_wait_set_resize(&wait_set_, 0, node_graph_interfaces_size + 2, 0, 0, 0, 0);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to resize wait set");
|
||||
}
|
||||
|
||||
@@ -29,6 +29,21 @@ IntraProcessManager::IntraProcessManager(
|
||||
IntraProcessManager::~IntraProcessManager()
|
||||
{}
|
||||
|
||||
uint64_t
|
||||
IntraProcessManager::add_publisher(
|
||||
rclcpp::PublisherBase::SharedPtr publisher,
|
||||
size_t buffer_size)
|
||||
{
|
||||
auto id = IntraProcessManager::get_next_unique_id();
|
||||
size_t size = buffer_size > 0 ? buffer_size : publisher->get_queue_size();
|
||||
auto mrb = publisher->make_mapped_ring_buffer(size);
|
||||
impl_->add_publisher(id, publisher, mrb, size);
|
||||
if (!mrb) {
|
||||
throw std::runtime_error("failed to create a mapped ring buffer");
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
IntraProcessManager::add_subscription(
|
||||
rclcpp::SubscriptionBase::SharedPtr subscription)
|
||||
@@ -56,6 +71,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()
|
||||
{
|
||||
|
||||
@@ -27,42 +27,93 @@
|
||||
#include "rclcpp/node_interfaces/node_clock.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph.hpp"
|
||||
#include "rclcpp/node_interfaces/node_logging.hpp"
|
||||
// When compiling this file, Windows produces a deprecation warning for the
|
||||
// deprecated function prototype of NodeParameters::register_param_change_callback().
|
||||
// Other compilers do not.
|
||||
#if defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
#include "rclcpp/node_interfaces/node_parameters.hpp"
|
||||
#if defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
#include "rclcpp/node_interfaces/node_services.hpp"
|
||||
#include "rclcpp/node_interfaces/node_time_source.hpp"
|
||||
#include "rclcpp/node_interfaces/node_timers.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics.hpp"
|
||||
#include "rclcpp/node_interfaces/node_waitables.hpp"
|
||||
|
||||
#include "rmw/validate_namespace.h"
|
||||
|
||||
using rclcpp::Node;
|
||||
using rclcpp::NodeOptions;
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
|
||||
RCLCPP_LOCAL
|
||||
std::string
|
||||
extend_sub_namespace(const std::string & existing_sub_namespace, const std::string & extension)
|
||||
{
|
||||
// Assumption is that the existing_sub_namespace does not need checking
|
||||
// because it would be checked already when it was set with this function.
|
||||
|
||||
// check if the new sub-namespace extension is absolute
|
||||
if (extension.front() == '/') {
|
||||
throw rclcpp::exceptions::NameValidationError(
|
||||
"sub_namespace",
|
||||
extension.c_str(),
|
||||
"a sub-namespace should not have a leading /",
|
||||
0);
|
||||
}
|
||||
|
||||
std::string new_sub_namespace;
|
||||
if (existing_sub_namespace.empty()) {
|
||||
new_sub_namespace = extension;
|
||||
} else {
|
||||
new_sub_namespace = existing_sub_namespace + "/" + extension;
|
||||
}
|
||||
|
||||
// remove any trailing `/` so that new extensions do no result in `//`
|
||||
if (new_sub_namespace.back() == '/') {
|
||||
new_sub_namespace = new_sub_namespace.substr(0, new_sub_namespace.size() - 1);
|
||||
}
|
||||
|
||||
return new_sub_namespace;
|
||||
}
|
||||
|
||||
RCLCPP_LOCAL
|
||||
std::string
|
||||
create_effective_namespace(const std::string & node_namespace, const std::string & sub_namespace)
|
||||
{
|
||||
// Assumption is that both the node_namespace and sub_namespace are conforming
|
||||
// and do not need trimming of `/` and other things, as they were validated
|
||||
// in other functions already.
|
||||
|
||||
if (node_namespace.back() == '/') {
|
||||
// this is the special case where node_namespace is just `/`
|
||||
return node_namespace + sub_namespace;
|
||||
} else {
|
||||
return node_namespace + "/" + sub_namespace;
|
||||
}
|
||||
}
|
||||
|
||||
Node::Node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool use_intra_process_comms)
|
||||
: Node(
|
||||
node_name,
|
||||
namespace_,
|
||||
rclcpp::contexts::default_context::get_global_default_context(),
|
||||
{},
|
||||
{},
|
||||
true,
|
||||
use_intra_process_comms,
|
||||
true)
|
||||
{}
|
||||
const NodeOptions & options)
|
||||
: Node(node_name, "", options)
|
||||
{
|
||||
}
|
||||
|
||||
Node::Node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::vector<std::string> & arguments,
|
||||
const std::vector<rclcpp::Parameter> & initial_parameters,
|
||||
bool use_global_arguments,
|
||||
bool use_intra_process_comms,
|
||||
bool start_parameter_services)
|
||||
const NodeOptions & options)
|
||||
: node_base_(new rclcpp::node_interfaces::NodeBase(
|
||||
node_name, namespace_, context, arguments, use_global_arguments)),
|
||||
node_name,
|
||||
namespace_,
|
||||
options.context(),
|
||||
*(options.get_rcl_node_options()),
|
||||
options.use_intra_process_comms())),
|
||||
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())),
|
||||
@@ -77,12 +128,17 @@ Node::Node(
|
||||
)),
|
||||
node_parameters_(new rclcpp::node_interfaces::NodeParameters(
|
||||
node_base_,
|
||||
node_logging_,
|
||||
node_topics_,
|
||||
node_services_,
|
||||
node_clock_,
|
||||
initial_parameters,
|
||||
use_intra_process_comms,
|
||||
start_parameter_services
|
||||
options.initial_parameters(),
|
||||
options.start_parameter_services(),
|
||||
options.start_parameter_event_publisher(),
|
||||
options.parameter_event_qos(),
|
||||
options.parameter_event_publisher_options(),
|
||||
options.allow_undeclared_parameters(),
|
||||
options.automatically_declare_initial_parameters()
|
||||
)),
|
||||
node_time_source_(new rclcpp::node_interfaces::NodeTimeSource(
|
||||
node_base_,
|
||||
@@ -94,10 +150,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 +207,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
|
||||
{
|
||||
@@ -132,20 +232,57 @@ Node::group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group)
|
||||
return node_base_->callback_group_in_node(group);
|
||||
}
|
||||
|
||||
const rclcpp::ParameterValue &
|
||||
Node::declare_parameter(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor)
|
||||
{
|
||||
return this->node_parameters_->declare_parameter(name, default_value, parameter_descriptor);
|
||||
}
|
||||
|
||||
void
|
||||
Node::undeclare_parameter(const std::string & name)
|
||||
{
|
||||
this->node_parameters_->undeclare_parameter(name);
|
||||
}
|
||||
|
||||
bool
|
||||
Node::has_parameter(const std::string & name) const
|
||||
{
|
||||
return this->node_parameters_->has_parameter(name);
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
Node::set_parameter(const rclcpp::Parameter & parameter)
|
||||
{
|
||||
return this->set_parameters_atomically({parameter});
|
||||
}
|
||||
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
Node::set_parameters(
|
||||
const std::vector<rclcpp::Parameter> & parameters)
|
||||
Node::set_parameters(const std::vector<rclcpp::Parameter> & parameters)
|
||||
{
|
||||
return node_parameters_->set_parameters(parameters);
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
Node::set_parameters_atomically(
|
||||
const std::vector<rclcpp::Parameter> & parameters)
|
||||
Node::set_parameters_atomically(const std::vector<rclcpp::Parameter> & parameters)
|
||||
{
|
||||
return node_parameters_->set_parameters_atomically(parameters);
|
||||
}
|
||||
|
||||
rclcpp::Parameter
|
||||
Node::get_parameter(const std::string & name) const
|
||||
{
|
||||
return node_parameters_->get_parameter(name);
|
||||
}
|
||||
|
||||
bool
|
||||
Node::get_parameter(const std::string & name, rclcpp::Parameter & parameter) const
|
||||
{
|
||||
return node_parameters_->get_parameter(name, parameter);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Parameter>
|
||||
Node::get_parameters(
|
||||
const std::vector<std::string> & names) const
|
||||
@@ -153,40 +290,43 @@ Node::get_parameters(
|
||||
return node_parameters_->get_parameters(names);
|
||||
}
|
||||
|
||||
rclcpp::Parameter
|
||||
Node::get_parameter(const std::string & name) const
|
||||
rcl_interfaces::msg::ParameterDescriptor
|
||||
Node::describe_parameter(const std::string & name) const
|
||||
{
|
||||
return node_parameters_->get_parameter(name);
|
||||
}
|
||||
|
||||
bool Node::get_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::Parameter & parameter) const
|
||||
{
|
||||
return node_parameters_->get_parameter(name, parameter);
|
||||
auto result = node_parameters_->describe_parameters({name});
|
||||
if (0 == result.size()) {
|
||||
throw rclcpp::exceptions::ParameterNotDeclaredException(name);
|
||||
}
|
||||
if (result.size() > 1) {
|
||||
throw std::runtime_error("number of described parameters unexpectedly more than one");
|
||||
}
|
||||
return result.front();
|
||||
}
|
||||
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor>
|
||||
Node::describe_parameters(
|
||||
const std::vector<std::string> & names) const
|
||||
Node::describe_parameters(const std::vector<std::string> & names) const
|
||||
{
|
||||
return node_parameters_->describe_parameters(names);
|
||||
}
|
||||
|
||||
std::vector<uint8_t>
|
||||
Node::get_parameter_types(
|
||||
const std::vector<std::string> & names) const
|
||||
Node::get_parameter_types(const std::vector<std::string> & names) const
|
||||
{
|
||||
return node_parameters_->get_parameter_types(names);
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
Node::list_parameters(
|
||||
const std::vector<std::string> & prefixes, uint64_t depth) const
|
||||
Node::list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const
|
||||
{
|
||||
return node_parameters_->list_parameters(prefixes, depth);
|
||||
}
|
||||
|
||||
rclcpp::Node::OnParametersSetCallbackType
|
||||
Node::set_on_parameters_set_callback(rclcpp::Node::OnParametersSetCallbackType callback)
|
||||
{
|
||||
return node_parameters_->set_on_parameters_set_callback(callback);
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
Node::get_node_names() const
|
||||
{
|
||||
@@ -308,3 +448,35 @@ 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_;
|
||||
}
|
||||
|
||||
bool
|
||||
Node::assert_liveliness() const
|
||||
{
|
||||
return node_base_->assert_liveliness();
|
||||
}
|
||||
|
||||
@@ -33,9 +33,10 @@ 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)
|
||||
const rcl_node_options_t & rcl_node_options,
|
||||
bool use_intra_process_default)
|
||||
: context_(context),
|
||||
use_intra_process_default_(use_intra_process_default),
|
||||
node_handle_(nullptr),
|
||||
default_callback_group_(nullptr),
|
||||
associated_with_executor_(false),
|
||||
@@ -44,7 +45,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_, 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 +60,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);
|
||||
context_->get_rcl_context().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 +136,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 +164,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()
|
||||
{
|
||||
@@ -262,6 +200,12 @@ NodeBase::get_shared_rcl_node_handle() const
|
||||
return node_handle_;
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBase::assert_liveliness() const
|
||||
{
|
||||
return RCL_RET_OK == rcl_node_assert_liveliness(get_rcl_node_handle());
|
||||
}
|
||||
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
NodeBase::create_callback_group(rclcpp::callback_group::CallbackGroupType group_type)
|
||||
{
|
||||
@@ -318,3 +262,9 @@ NodeBase::acquire_notify_guard_condition_lock() const
|
||||
{
|
||||
return std::unique_lock<std::recursive_mutex>(notify_guard_condition_mutex_);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBase::get_use_intra_process_default() const
|
||||
{
|
||||
return use_intra_process_default_;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "rclcpp/node_interfaces/node_graph.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -136,9 +137,24 @@ NodeGraph::get_node_names() const
|
||||
std::vector<std::string> nodes;
|
||||
auto names_and_namespaces = get_node_names_and_namespaces();
|
||||
|
||||
for (const auto & it : names_and_namespaces) {
|
||||
nodes.push_back(it.first);
|
||||
}
|
||||
std::transform(names_and_namespaces.begin(),
|
||||
names_and_namespaces.end(),
|
||||
std::back_inserter(nodes),
|
||||
[](std::pair<std::string, std::string> nns) {
|
||||
std::string return_string;
|
||||
if (nns.second.back() == '/') {
|
||||
return_string = nns.second + nns.first;
|
||||
} else {
|
||||
return_string = nns.second + '/' + nns.first;
|
||||
}
|
||||
// Quick check to make sure that we start with a slash
|
||||
// Since fully-qualified strings need to
|
||||
if (return_string.front() != '/') {
|
||||
return_string = "/" + return_string;
|
||||
}
|
||||
return return_string;
|
||||
}
|
||||
);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@@ -173,10 +189,12 @@ NodeGraph::get_node_names_and_namespaces() const
|
||||
throw std::runtime_error(error_msg);
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> node_names(node_names_c.size);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> node_names;
|
||||
node_names.reserve(node_names_c.size);
|
||||
for (size_t i = 0; i < node_names_c.size; ++i) {
|
||||
if (node_names_c.data[i] && node_namespaces_c.data[i]) {
|
||||
node_names.push_back(std::make_pair(node_names_c.data[i], node_namespaces_c.data[i]));
|
||||
node_names.emplace_back(node_names_c.data[i], node_namespaces_c.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// When compiling this file, Windows produces a deprecation warning for the
|
||||
// deprecated function prototype of NodeParameters::register_param_change_callback().
|
||||
// Other compilers do not.
|
||||
#if defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
#include "rclcpp/node_interfaces/node_parameters.hpp"
|
||||
#if defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include <rcl_yaml_param_parser/parser.h>
|
||||
|
||||
@@ -34,30 +44,41 @@ using rclcpp::node_interfaces::NodeParameters;
|
||||
|
||||
NodeParameters::NodeParameters(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
|
||||
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
|
||||
const rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
|
||||
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
|
||||
const rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
|
||||
const std::vector<rclcpp::Parameter> & initial_parameters,
|
||||
bool use_intra_process,
|
||||
bool start_parameter_services)
|
||||
: node_clock_(node_clock)
|
||||
bool start_parameter_services,
|
||||
bool start_parameter_event_publisher,
|
||||
const rclcpp::QoS & parameter_event_qos,
|
||||
const rclcpp::PublisherOptionsBase & parameter_event_publisher_options,
|
||||
bool allow_undeclared_parameters,
|
||||
bool automatically_declare_initial_parameters)
|
||||
: allow_undeclared_(allow_undeclared_parameters),
|
||||
events_publisher_(nullptr),
|
||||
node_logging_(node_logging),
|
||||
node_clock_(node_clock)
|
||||
{
|
||||
using MessageT = rcl_interfaces::msg::ParameterEvent;
|
||||
using PublisherT = rclcpp::Publisher<MessageT>;
|
||||
using AllocatorT = std::allocator<void>;
|
||||
// TODO(wjwwood): expose this allocator through the Parameter interface.
|
||||
auto allocator = std::make_shared<AllocatorT>();
|
||||
rclcpp::PublisherOptionsWithAllocator<AllocatorT> publisher_options(
|
||||
parameter_event_publisher_options);
|
||||
publisher_options.allocator = std::make_shared<AllocatorT>();
|
||||
|
||||
if (start_parameter_services) {
|
||||
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,
|
||||
"parameter_events",
|
||||
parameter_event_qos,
|
||||
publisher_options);
|
||||
}
|
||||
|
||||
// Get the node options
|
||||
const rcl_node_t * node = node_base->get_rcl_node_handle();
|
||||
@@ -101,20 +122,7 @@ NodeParameters::NodeParameters(
|
||||
get_yaml_paths(&(options->arguments));
|
||||
|
||||
// Get fully qualified node name post-remapping to use to find node's params in yaml files
|
||||
const std::string node_name = node_base->get_name();
|
||||
const std::string node_namespace = node_base->get_namespace();
|
||||
if (0u == node_namespace.size() || 0u == node_name.size()) {
|
||||
// Should never happen
|
||||
throw std::runtime_error("Node name and namespace were not set");
|
||||
}
|
||||
|
||||
if ('/' == node_namespace.at(node_namespace.size() - 1)) {
|
||||
combined_name_ = node_namespace + node_name;
|
||||
} else {
|
||||
combined_name_ = node_namespace + '/' + node_name;
|
||||
}
|
||||
|
||||
std::map<std::string, rclcpp::Parameter> parameters;
|
||||
combined_name_ = node_base->get_fully_qualified_name();
|
||||
|
||||
// TODO(sloretz) use rcl to parse yaml when circular dependency is solved
|
||||
// See https://github.com/ros2/rcl/issues/252
|
||||
@@ -140,27 +148,27 @@ NodeParameters::NodeParameters(
|
||||
|
||||
// Combine parameter yaml files, overwriting values in older ones
|
||||
for (auto & param : iter->second) {
|
||||
parameters[param.get_name()] = param;
|
||||
initial_parameter_values_[param.get_name()] =
|
||||
rclcpp::ParameterValue(param.get_value_message());
|
||||
}
|
||||
}
|
||||
|
||||
// initial values passed to constructor overwrite yaml file sources
|
||||
for (auto & param : initial_parameters) {
|
||||
parameters[param.get_name()] = param;
|
||||
initial_parameter_values_[param.get_name()] =
|
||||
rclcpp::ParameterValue(param.get_value_message());
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Parameter> combined_values;
|
||||
combined_values.reserve(parameters.size());
|
||||
for (auto & kv : parameters) {
|
||||
combined_values.emplace_back(kv.second);
|
||||
}
|
||||
|
||||
// TODO(sloretz) store initial values and use them when a parameter is created ros2/rclcpp#475
|
||||
// Set initial parameter values
|
||||
if (!combined_values.empty()) {
|
||||
rcl_interfaces::msg::SetParametersResult result = set_parameters_atomically(combined_values);
|
||||
if (!result.successful) {
|
||||
throw std::runtime_error("Failed to set initial parameters");
|
||||
// If asked, initialize any parameters that ended up in the initial parameter values,
|
||||
// but did not get declared explcitily by this point.
|
||||
if (automatically_declare_initial_parameters) {
|
||||
for (const auto & pair : this->get_initial_parameter_values()) {
|
||||
if (!this->has_parameter(pair.first)) {
|
||||
this->declare_parameter(
|
||||
pair.first,
|
||||
pair.second,
|
||||
rcl_interfaces::msg::ParameterDescriptor());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,72 +176,354 @@ NodeParameters::NodeParameters(
|
||||
NodeParameters::~NodeParameters()
|
||||
{}
|
||||
|
||||
RCLCPP_LOCAL
|
||||
bool
|
||||
__lockless_has_parameter(
|
||||
const std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameters,
|
||||
const std::string & name)
|
||||
{
|
||||
return parameters.find(name) != parameters.end();
|
||||
}
|
||||
|
||||
using OnParametersSetCallbackType =
|
||||
rclcpp::node_interfaces::NodeParametersInterface::OnParametersSetCallbackType;
|
||||
|
||||
RCLCPP_LOCAL
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
__set_parameters_atomically_common(
|
||||
const std::vector<rclcpp::Parameter> & parameters,
|
||||
std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameter_infos,
|
||||
OnParametersSetCallbackType on_set_parameters_callback)
|
||||
{
|
||||
// Call the user callback to see if the new value(s) are allowed.
|
||||
rcl_interfaces::msg::SetParametersResult result;
|
||||
result.successful = true;
|
||||
if (on_set_parameters_callback) {
|
||||
result = on_set_parameters_callback(parameters);
|
||||
}
|
||||
|
||||
// If accepted, actually set the values.
|
||||
if (result.successful) {
|
||||
for (size_t i = 0; i < parameters.size(); ++i) {
|
||||
const std::string & name = parameters[i].get_name();
|
||||
parameter_infos[name].descriptor.name = parameters[i].get_name();
|
||||
parameter_infos[name].descriptor.type = parameters[i].get_type();
|
||||
parameter_infos[name].value = parameters[i].get_parameter_value();
|
||||
}
|
||||
}
|
||||
|
||||
// Either way, return the result.
|
||||
return result;
|
||||
}
|
||||
|
||||
RCLCPP_LOCAL
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
__declare_parameter_common(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
|
||||
std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameters_out,
|
||||
const std::map<std::string, rclcpp::ParameterValue> & initial_values,
|
||||
OnParametersSetCallbackType on_set_parameters_callback,
|
||||
rcl_interfaces::msg::ParameterEvent * parameter_event_out)
|
||||
{
|
||||
using rclcpp::node_interfaces::ParameterInfo;
|
||||
std::map<std::string, ParameterInfo> parameter_infos {{name, ParameterInfo()}};
|
||||
parameter_infos.at(name).descriptor = parameter_descriptor;
|
||||
|
||||
// Use the value from the initial_values if available, otherwise use the default.
|
||||
const rclcpp::ParameterValue * initial_value = &default_value;
|
||||
auto initial_value_it = initial_values.find(name);
|
||||
if (initial_value_it != initial_values.end()) {
|
||||
initial_value = &initial_value_it->second;
|
||||
}
|
||||
|
||||
// Check with the user's callback to see if the initial value can be set.
|
||||
std::vector<rclcpp::Parameter> parameter_wrappers {rclcpp::Parameter(name, *initial_value)};
|
||||
// This function also takes care of default vs initial value.
|
||||
auto result = __set_parameters_atomically_common(
|
||||
parameter_wrappers,
|
||||
parameter_infos,
|
||||
on_set_parameters_callback);
|
||||
|
||||
// Add declared parameters to storage.
|
||||
parameters_out[name] = parameter_infos.at(name);
|
||||
|
||||
// Extend the given parameter event, if valid.
|
||||
if (parameter_event_out) {
|
||||
parameter_event_out->new_parameters.push_back(parameter_wrappers[0].to_parameter_msg());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const rclcpp::ParameterValue &
|
||||
NodeParameters::declare_parameter(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
// TODO(sloretz) parameter name validation
|
||||
if (name.empty()) {
|
||||
throw rclcpp::exceptions::InvalidParametersException("parameter name must not be empty");
|
||||
}
|
||||
|
||||
// Error if this parameter has already been declared and is different
|
||||
if (__lockless_has_parameter(parameters_, name)) {
|
||||
throw rclcpp::exceptions::ParameterAlreadyDeclaredException(
|
||||
"parameter '" + name + "' has already been declared");
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ParameterEvent parameter_event;
|
||||
auto result = __declare_parameter_common(
|
||||
name,
|
||||
default_value,
|
||||
parameter_descriptor,
|
||||
parameters_,
|
||||
initial_parameter_values_,
|
||||
on_parameters_set_callback_,
|
||||
¶meter_event);
|
||||
|
||||
// If it failed to be set, then throw an exception.
|
||||
if (!result.successful) {
|
||||
throw rclcpp::exceptions::InvalidParameterValueException(
|
||||
"parameter '" + name + "' could not be set: " + result.reason);
|
||||
}
|
||||
|
||||
// Publish if events_publisher_ is not nullptr, which may be if disabled in the constructor.
|
||||
if (nullptr != events_publisher_) {
|
||||
events_publisher_->publish(parameter_event);
|
||||
}
|
||||
|
||||
return parameters_.at(name).value;
|
||||
}
|
||||
|
||||
void
|
||||
NodeParameters::undeclare_parameter(const std::string & name)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto parameter_info = parameters_.find(name);
|
||||
if (parameter_info == parameters_.end()) {
|
||||
throw rclcpp::exceptions::ParameterNotDeclaredException(
|
||||
"cannot undeclare parameter '" + name + "' which has not yet been declared");
|
||||
}
|
||||
|
||||
if (parameter_info->second.descriptor.read_only) {
|
||||
throw rclcpp::exceptions::ParameterImmutableException(
|
||||
"cannot undeclare parameter '" + name + "' because it is read-only");
|
||||
}
|
||||
|
||||
parameters_.erase(parameter_info);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeParameters::has_parameter(const std::string & name) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
return __lockless_has_parameter(parameters_, name);
|
||||
}
|
||||
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
NodeParameters::set_parameters(
|
||||
const std::vector<rclcpp::Parameter> & parameters)
|
||||
NodeParameters::set_parameters(const std::vector<rclcpp::Parameter> & parameters)
|
||||
{
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult> results;
|
||||
for (auto p : parameters) {
|
||||
results.reserve(parameters.size());
|
||||
|
||||
for (const auto & p : parameters) {
|
||||
auto result = set_parameters_atomically({{p}});
|
||||
results.push_back(result);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
template<typename ParameterVectorType>
|
||||
auto
|
||||
__find_parameter_by_name(
|
||||
ParameterVectorType & parameters,
|
||||
const std::string & name)
|
||||
{
|
||||
return std::find_if(
|
||||
parameters.begin(),
|
||||
parameters.end(),
|
||||
[&](auto parameter) {return parameter.get_name() == name;});
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
NodeParameters::set_parameters_atomically(
|
||||
const std::vector<rclcpp::Parameter> & parameters)
|
||||
NodeParameters::set_parameters_atomically(const std::vector<rclcpp::Parameter> & parameters)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::map<std::string, rclcpp::Parameter> tmp_map;
|
||||
auto parameter_event = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
|
||||
|
||||
parameter_event->node = combined_name_;
|
||||
|
||||
// TODO(jacquelinekay): handle parameter constraints
|
||||
rcl_interfaces::msg::SetParametersResult result;
|
||||
if (parameters_callback_) {
|
||||
result = parameters_callback_(parameters);
|
||||
} else {
|
||||
result.successful = true;
|
||||
|
||||
// Check if any of the parameters are read-only, or if any parameters are not
|
||||
// declared.
|
||||
// If not declared, keep track of them in order to declare them later, when
|
||||
// undeclared parameters are allowed, and if they're not allowed, fail.
|
||||
std::vector<const rclcpp::Parameter *> parameters_to_be_declared;
|
||||
for (const auto & parameter : parameters) {
|
||||
const std::string & name = parameter.get_name();
|
||||
|
||||
// Check to make sure the parameter name is valid.
|
||||
if (name.empty()) {
|
||||
throw rclcpp::exceptions::InvalidParametersException("parameter name must not be empty");
|
||||
}
|
||||
|
||||
// Check to see if it is declared.
|
||||
auto parameter_info = parameters_.find(name);
|
||||
if (parameter_info == parameters_.end()) {
|
||||
// If not check to see if undeclared paramaters are allowed, ...
|
||||
if (allow_undeclared_) {
|
||||
// If so, mark the parameter to be declared for the user implicitly.
|
||||
parameters_to_be_declared.push_back(¶meter);
|
||||
// continue as it cannot be read-only, and because the declare will
|
||||
// implicitly set the parameter and parameter_infos is for setting only.
|
||||
continue;
|
||||
} else {
|
||||
// If not, then throw the exception as documented.
|
||||
throw rclcpp::exceptions::ParameterNotDeclaredException(
|
||||
"parameter '" + name + "' cannot be set because it was not declared");
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if it is read-only.
|
||||
if (parameter_info->second.descriptor.read_only) {
|
||||
result.successful = false;
|
||||
result.reason = "parameter '" + name + "' cannot be set because it is read-only";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Declare parameters into a temporary "staging area", incase one of the declares fail.
|
||||
// We will use the staged changes as input to the "set atomically" action.
|
||||
// We explicitly avoid calling the user callback here, so that it may be called once, with
|
||||
// all the other parameters to be set (already declared parameters).
|
||||
std::map<std::string, rclcpp::node_interfaces::ParameterInfo> staged_parameter_changes;
|
||||
rcl_interfaces::msg::ParameterEvent parameter_event_msg;
|
||||
parameter_event_msg.node = combined_name_;
|
||||
for (auto parameter_to_be_declared : parameters_to_be_declared) {
|
||||
// This should not throw, because we validated the name and checked that
|
||||
// the parameter was not already declared.
|
||||
result = __declare_parameter_common(
|
||||
parameter_to_be_declared->get_name(),
|
||||
parameter_to_be_declared->get_parameter_value(),
|
||||
rcl_interfaces::msg::ParameterDescriptor(), // Implicit declare uses default descriptor.
|
||||
staged_parameter_changes,
|
||||
initial_parameter_values_,
|
||||
nullptr, // callback is explicitly null, so that it is called only once, when setting below.
|
||||
¶meter_event_msg);
|
||||
if (!result.successful) {
|
||||
// Declare failed, return knowing that nothing was changed because the
|
||||
// staged changes were not applied.
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// If there were implicitly declared parameters, then we may need to copy the input parameters
|
||||
// and then assign the value that was selected after the declare (could be affected by the
|
||||
// initial parameter values).
|
||||
const std::vector<rclcpp::Parameter> * parameters_to_be_set = ¶meters;
|
||||
std::vector<rclcpp::Parameter> parameters_copy;
|
||||
if (0 != staged_parameter_changes.size()) { // If there were any implicitly declared parameters.
|
||||
bool any_initial_values_used = false;
|
||||
for (const auto & staged_parameter_change : staged_parameter_changes) {
|
||||
auto it = __find_parameter_by_name(parameters, staged_parameter_change.first);
|
||||
if (it->get_parameter_value() != staged_parameter_change.second.value) {
|
||||
// In this case, the value of the staged parameter differs from the
|
||||
// input from the user, and therefore we need to update things before setting.
|
||||
any_initial_values_used = true;
|
||||
// No need to search further since at least one initial value needs to be used.
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (any_initial_values_used) {
|
||||
parameters_copy = parameters;
|
||||
for (const auto & staged_parameter_change : staged_parameter_changes) {
|
||||
auto it = __find_parameter_by_name(parameters_copy, staged_parameter_change.first);
|
||||
*it = Parameter(staged_parameter_change.first, staged_parameter_change.second.value);
|
||||
}
|
||||
parameters_to_be_set = ¶meters_copy;
|
||||
}
|
||||
}
|
||||
|
||||
// Collect parameters who will have had their type changed to
|
||||
// rclcpp::PARAMETER_NOT_SET so they can later be implicitly undeclared.
|
||||
std::vector<const rclcpp::Parameter *> parameters_to_be_undeclared;
|
||||
for (const auto & parameter : *parameters_to_be_set) {
|
||||
if (rclcpp::PARAMETER_NOT_SET == parameter.get_type()) {
|
||||
auto it = parameters_.find(parameter.get_name());
|
||||
if (it != parameters_.end() && rclcpp::PARAMETER_NOT_SET != it->second.value.get_type()) {
|
||||
parameters_to_be_undeclared.push_back(¶meter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the all of the parameters including the ones declared implicitly above.
|
||||
result = __set_parameters_atomically_common(
|
||||
// either the original parameters given by the user, or ones updated with initial values
|
||||
*parameters_to_be_set,
|
||||
// they are actually set on the official parameter storage
|
||||
parameters_,
|
||||
// this will get called once, with all the parameters to be set
|
||||
on_parameters_set_callback_);
|
||||
|
||||
// If not successful, then stop here.
|
||||
if (!result.successful) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (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"
|
||||
// therefore we will erase the parameter from parameters_ later
|
||||
parameter_event->deleted_parameters.push_back(p.to_parameter_msg());
|
||||
}
|
||||
} else {
|
||||
if (parameters_.find(p.get_name()) == parameters_.end()) {
|
||||
// case: parameter not set before, and input is something other than "NOT_SET"
|
||||
parameter_event->new_parameters.push_back(p.to_parameter_msg());
|
||||
} else {
|
||||
// case: parameter was set before, and input is something other than "NOT_SET"
|
||||
parameter_event->changed_parameters.push_back(p.to_parameter_msg());
|
||||
}
|
||||
tmp_map[p.get_name()] = p;
|
||||
}
|
||||
// If successful, then update the parameter infos from the implicitly declared parameter's.
|
||||
for (const auto & kv_pair : staged_parameter_changes) {
|
||||
// assumption: the parameter is already present in parameters_ due to the above "set"
|
||||
assert(__lockless_has_parameter(parameters_, kv_pair.first));
|
||||
// assumption: the value in parameters_ is the same as the value resulting from the declare
|
||||
assert(parameters_[kv_pair.first].value == kv_pair.second.value);
|
||||
// This assignment should not change the name, type, or value, but may
|
||||
// change other things from the ParameterInfo.
|
||||
parameters_[kv_pair.first] = kv_pair.second;
|
||||
}
|
||||
// std::map::insert will not overwrite elements, so we'll keep the new
|
||||
// ones and add only those that already exist in the Node's internal map
|
||||
tmp_map.insert(parameters_.begin(), parameters_.end());
|
||||
|
||||
// remove explicitly deleted parameters
|
||||
for (auto p : parameters) {
|
||||
if (p.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET) {
|
||||
tmp_map.erase(p.get_name());
|
||||
// Undeclare parameters that need to be.
|
||||
for (auto parameter_to_undeclare : parameters_to_be_undeclared) {
|
||||
auto it = parameters_.find(parameter_to_undeclare->get_name());
|
||||
// assumption: the parameter to be undeclared should be in the parameter infos map
|
||||
assert(it != parameters_.end());
|
||||
if (it != parameters_.end()) {
|
||||
// Remove it and update the parameter event message.
|
||||
parameters_.erase(it);
|
||||
parameter_event_msg.deleted_parameters.push_back(
|
||||
rclcpp::Parameter(it->first, it->second.value).to_parameter_msg());
|
||||
}
|
||||
}
|
||||
|
||||
std::swap(tmp_map, parameters_);
|
||||
parameter_event->stamp = node_clock_->get_clock()->now();
|
||||
events_publisher_->publish(parameter_event);
|
||||
// Update the parameter event message for any parameters which were only set,
|
||||
// and not either declared or undeclared.
|
||||
for (const auto & parameter : *parameters_to_be_set) {
|
||||
if (staged_parameter_changes.find(parameter.get_name()) != staged_parameter_changes.end()) {
|
||||
// This parameter was declared.
|
||||
continue;
|
||||
}
|
||||
auto it = std::find_if(
|
||||
parameters_to_be_undeclared.begin(),
|
||||
parameters_to_be_undeclared.end(),
|
||||
[¶meter](const auto & p) {return p->get_name() == parameter.get_name();});
|
||||
if (it != parameters_to_be_undeclared.end()) {
|
||||
// This parameter was undeclared (deleted).
|
||||
continue;
|
||||
}
|
||||
// This parameter was neither declared nor undeclared.
|
||||
parameter_event_msg.changed_parameters.push_back(parameter.to_parameter_msg());
|
||||
}
|
||||
|
||||
// Publish if events_publisher_ is not nullptr, which may be if disabled in the constructor.
|
||||
if (nullptr != events_publisher_) {
|
||||
parameter_event_msg.stamp = node_clock_->get_clock()->now();
|
||||
events_publisher_->publish(parameter_event_msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -243,14 +533,19 @@ NodeParameters::get_parameters(const std::vector<std::string> & names) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<rclcpp::Parameter> results;
|
||||
results.reserve(names.size());
|
||||
|
||||
for (auto & name : names) {
|
||||
if (std::any_of(parameters_.cbegin(), parameters_.cend(),
|
||||
[&name](const std::pair<std::string, rclcpp::Parameter> & kv) {
|
||||
return name == kv.first;
|
||||
}))
|
||||
{
|
||||
results.push_back(parameters_.at(name));
|
||||
auto found_parameter = parameters_.find(name);
|
||||
if (found_parameter != parameters_.cend()) {
|
||||
// found
|
||||
results.emplace_back(name, found_parameter->second.value);
|
||||
} else if (this->allow_undeclared_) {
|
||||
// not found, but undeclared allowed
|
||||
results.emplace_back(name, rclcpp::ParameterValue());
|
||||
} else {
|
||||
// not found, and undeclared are not allowed
|
||||
throw rclcpp::exceptions::ParameterNotDeclaredException(name);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
@@ -263,8 +558,10 @@ NodeParameters::get_parameter(const std::string & name) const
|
||||
|
||||
if (get_parameter(name, parameter)) {
|
||||
return parameter;
|
||||
} else if (this->allow_undeclared_) {
|
||||
return parameter;
|
||||
} else {
|
||||
throw std::out_of_range("Parameter '" + name + "' not set");
|
||||
throw rclcpp::exceptions::ParameterNotDeclaredException(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,8 +572,12 @@ NodeParameters::get_parameter(
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (parameters_.count(name)) {
|
||||
parameter = parameters_.at(name);
|
||||
auto param_iter = parameters_.find(name);
|
||||
if (
|
||||
parameters_.end() != param_iter &&
|
||||
param_iter->second.value.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET)
|
||||
{
|
||||
parameter = {name, param_iter->second.value};
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -288,15 +589,15 @@ NodeParameters::get_parameters_by_prefix(
|
||||
const std::string & prefix,
|
||||
std::map<std::string, rclcpp::Parameter> & parameters) const
|
||||
{
|
||||
std::string prefix_with_dot = prefix + ".";
|
||||
bool ret = false;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
std::string prefix_with_dot = prefix.empty() ? prefix : prefix + ".";
|
||||
bool ret = false;
|
||||
|
||||
for (const auto & param : parameters_) {
|
||||
if (param.first.find(prefix_with_dot) == 0 && param.first.length() > prefix_with_dot.length()) {
|
||||
// Found one!
|
||||
parameters[param.first.substr(prefix_with_dot.length())] = param.second;
|
||||
parameters[param.first.substr(prefix_with_dot.length())] = rclcpp::Parameter(param.second);
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
@@ -309,17 +610,26 @@ NodeParameters::describe_parameters(const std::vector<std::string> & names) cons
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor> results;
|
||||
for (auto & kv : parameters_) {
|
||||
if (std::any_of(names.cbegin(), names.cend(), [&kv](const std::string & name) {
|
||||
return name == kv.first;
|
||||
}))
|
||||
{
|
||||
rcl_interfaces::msg::ParameterDescriptor parameter_descriptor;
|
||||
parameter_descriptor.name = kv.first;
|
||||
parameter_descriptor.type = kv.second.get_type();
|
||||
results.push_back(parameter_descriptor);
|
||||
results.reserve(names.size());
|
||||
|
||||
for (const auto & name : names) {
|
||||
auto it = parameters_.find(name);
|
||||
if (it != parameters_.cend()) {
|
||||
results.push_back(it->second.descriptor);
|
||||
} else if (allow_undeclared_) {
|
||||
// parameter not found, but undeclared allowed, so return empty
|
||||
rcl_interfaces::msg::ParameterDescriptor default_description;
|
||||
default_description.name = name;
|
||||
results.push_back(default_description);
|
||||
} else {
|
||||
throw rclcpp::exceptions::ParameterNotDeclaredException(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (results.size() != names.size()) {
|
||||
throw std::runtime_error("results and names unexpectedly different sizes");
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -328,16 +638,24 @@ NodeParameters::get_parameter_types(const std::vector<std::string> & names) cons
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<uint8_t> results;
|
||||
for (auto & kv : parameters_) {
|
||||
if (std::any_of(names.cbegin(), names.cend(), [&kv](const std::string & name) {
|
||||
return name == kv.first;
|
||||
}))
|
||||
{
|
||||
results.push_back(kv.second.get_type());
|
||||
} else {
|
||||
results.reserve(names.size());
|
||||
|
||||
for (const auto & name : names) {
|
||||
auto it = parameters_.find(name);
|
||||
if (it != parameters_.cend()) {
|
||||
results.push_back(it->second.value.get_type());
|
||||
} else if (allow_undeclared_) {
|
||||
// parameter not found, but undeclared allowed, so return not set
|
||||
results.push_back(rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET);
|
||||
} else {
|
||||
throw rclcpp::exceptions::ParameterNotDeclaredException(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (results.size() != names.size()) {
|
||||
throw std::runtime_error("results and names unexpectedly different sizes");
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -383,12 +701,39 @@ NodeParameters::list_parameters(const std::vector<std::string> & prefixes, uint6
|
||||
return result;
|
||||
}
|
||||
|
||||
NodeParameters::OnParametersSetCallbackType
|
||||
NodeParameters::set_on_parameters_set_callback(OnParametersSetCallbackType callback)
|
||||
{
|
||||
auto existing_callback = on_parameters_set_callback_;
|
||||
on_parameters_set_callback_ = callback;
|
||||
return existing_callback;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
void
|
||||
NodeParameters::register_param_change_callback(ParametersCallbackFunction callback)
|
||||
{
|
||||
if (parameters_callback_) {
|
||||
RCUTILS_LOG_WARN("param_change_callback already registered, "
|
||||
"overwriting previous callback");
|
||||
if (on_parameters_set_callback_) {
|
||||
RCLCPP_WARN(
|
||||
node_logging_->get_logger(),
|
||||
"on_parameters_set_callback already registered, overwriting previous callback");
|
||||
}
|
||||
parameters_callback_ = callback;
|
||||
on_parameters_set_callback_ = callback;
|
||||
}
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
const std::map<std::string, rclcpp::ParameterValue> &
|
||||
NodeParameters::get_initial_parameter_values() const
|
||||
{
|
||||
return initial_parameter_values_;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ rclcpp::PublisherBase::SharedPtr
|
||||
NodeTopics::create_publisher(
|
||||
const std::string & topic_name,
|
||||
const rclcpp::PublisherFactory & publisher_factory,
|
||||
rcl_publisher_options_t & publisher_options,
|
||||
const rcl_publisher_options_t & publisher_options,
|
||||
bool use_intra_process)
|
||||
{
|
||||
// Create the MessageT specific Publisher using the factory, but store it as PublisherBase.
|
||||
@@ -47,13 +47,10 @@ NodeTopics::create_publisher(
|
||||
// Get the intra process manager instance for this context.
|
||||
auto ipm = context->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
|
||||
// Register the publisher with the intra process manager.
|
||||
uint64_t intra_process_publisher_id =
|
||||
publisher_factory.add_publisher_to_intra_process_manager(ipm.get(), publisher);
|
||||
// Create a function to be called when publisher to do the intra process publish.
|
||||
auto shared_publish_callback = publisher_factory.create_shared_publish_callback(ipm);
|
||||
uint64_t intra_process_publisher_id = ipm->add_publisher(publisher);
|
||||
publisher->setup_intra_process(
|
||||
intra_process_publisher_id,
|
||||
shared_publish_callback,
|
||||
ipm,
|
||||
publisher_options);
|
||||
}
|
||||
|
||||
@@ -63,11 +60,22 @@ NodeTopics::create_publisher(
|
||||
|
||||
void
|
||||
NodeTopics::add_publisher(
|
||||
rclcpp::PublisherBase::SharedPtr publisher)
|
||||
rclcpp::PublisherBase::SharedPtr publisher,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group)
|
||||
{
|
||||
// The publisher is not added to a callback group or anthing like that for now.
|
||||
// It may be stored within the NodeTopics class or the NodeBase class in the future.
|
||||
(void)publisher;
|
||||
// Assign to a group.
|
||||
if (callback_group) {
|
||||
if (!node_base_->callback_group_in_node(callback_group)) {
|
||||
throw std::runtime_error("Cannot create publisher, callback group not in node.");
|
||||
}
|
||||
} else {
|
||||
callback_group = node_base_->get_default_callback_group();
|
||||
}
|
||||
|
||||
for (auto & publisher_event : publisher->get_event_handlers()) {
|
||||
callback_group->add_waitable(publisher_event);
|
||||
}
|
||||
|
||||
// Notify the executor that a new publisher was created using the parent Node.
|
||||
{
|
||||
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
|
||||
@@ -83,7 +91,7 @@ rclcpp::SubscriptionBase::SharedPtr
|
||||
NodeTopics::create_subscription(
|
||||
const std::string & topic_name,
|
||||
const rclcpp::SubscriptionFactory & subscription_factory,
|
||||
rcl_subscription_options_t & subscription_options,
|
||||
const rcl_subscription_options_t & subscription_options,
|
||||
bool use_intra_process)
|
||||
{
|
||||
auto subscription = subscription_factory.create_typed_subscription(
|
||||
@@ -92,10 +100,12 @@ NodeTopics::create_subscription(
|
||||
// Setup intra process publishing if requested.
|
||||
if (use_intra_process) {
|
||||
auto context = node_base_->get_context();
|
||||
auto intra_process_manager =
|
||||
auto ipm =
|
||||
context->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
|
||||
subscription_factory.setup_intra_process(
|
||||
intra_process_manager, subscription, subscription_options);
|
||||
uint64_t intra_process_subscription_id = ipm->add_subscription(subscription);
|
||||
auto options_copy = subscription_options;
|
||||
options_copy.ignore_local_publications = false;
|
||||
subscription->setup_intra_process(intra_process_subscription_id, ipm, options_copy);
|
||||
}
|
||||
|
||||
// Return the completed subscription.
|
||||
@@ -113,9 +123,13 @@ NodeTopics::add_subscription(
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create subscription, callback group not in node.");
|
||||
}
|
||||
callback_group->add_subscription(subscription);
|
||||
} else {
|
||||
node_base_->get_default_callback_group()->add_subscription(subscription);
|
||||
callback_group = node_base_->get_default_callback_group();
|
||||
}
|
||||
|
||||
callback_group->add_subscription(subscription);
|
||||
for (auto & subscription_event : subscription->get_event_handlers()) {
|
||||
callback_group->add_waitable(subscription_event);
|
||||
}
|
||||
|
||||
// Notify the executor that a new subscription was created using the parent Node.
|
||||
@@ -129,3 +143,9 @@ NodeTopics::add_subscription(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeBaseInterface *
|
||||
NodeTopics::get_node_base_interface() const
|
||||
{
|
||||
return node_base_;
|
||||
}
|
||||
|
||||
313
rclcpp/src/rclcpp/node_options.cpp
Normal file
313
rclcpp/src/rclcpp/node_options.cpp
Normal file
@@ -0,0 +1,313 @@
|
||||
// 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"
|
||||
#include "rclcpp/publisher_options.hpp"
|
||||
#include "rclcpp/qos.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_;
|
||||
this->allow_undeclared_parameters_ = other.allow_undeclared_parameters_;
|
||||
this->automatically_declare_initial_parameters_ =
|
||||
other.automatically_declare_initial_parameters_;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
bool
|
||||
NodeOptions::use_global_arguments() const
|
||||
{
|
||||
return this->node_options_->use_global_arguments;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::use_global_arguments(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;
|
||||
}
|
||||
|
||||
bool
|
||||
NodeOptions::use_intra_process_comms() const
|
||||
{
|
||||
return this->use_intra_process_comms_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::use_intra_process_comms(bool use_intra_process_comms)
|
||||
{
|
||||
this->use_intra_process_comms_ = use_intra_process_comms;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
NodeOptions::start_parameter_services() const
|
||||
{
|
||||
return this->start_parameter_services_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::start_parameter_services(bool start_parameter_services)
|
||||
{
|
||||
this->start_parameter_services_ = start_parameter_services;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
NodeOptions::start_parameter_event_publisher() const
|
||||
{
|
||||
return this->start_parameter_event_publisher_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::start_parameter_event_publisher(bool start_parameter_event_publisher)
|
||||
{
|
||||
this->start_parameter_event_publisher_ = start_parameter_event_publisher;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const rclcpp::QoS &
|
||||
NodeOptions::parameter_event_qos() const
|
||||
{
|
||||
return this->parameter_event_qos_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::parameter_event_qos(const rclcpp::QoS & parameter_event_qos)
|
||||
{
|
||||
this->parameter_event_qos_ = parameter_event_qos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const rclcpp::PublisherOptionsBase &
|
||||
NodeOptions::parameter_event_publisher_options() const
|
||||
{
|
||||
return parameter_event_publisher_options_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::parameter_event_publisher_options(
|
||||
const rclcpp::PublisherOptionsBase & parameter_event_publisher_options)
|
||||
{
|
||||
parameter_event_publisher_options_ = parameter_event_publisher_options;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
NodeOptions::allow_undeclared_parameters() const
|
||||
{
|
||||
return this->allow_undeclared_parameters_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::allow_undeclared_parameters(bool allow_undeclared_parameters)
|
||||
{
|
||||
this->allow_undeclared_parameters_ = allow_undeclared_parameters;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
NodeOptions::automatically_declare_initial_parameters() const
|
||||
{
|
||||
return this->automatically_declare_initial_parameters_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::automatically_declare_initial_parameters(
|
||||
bool automatically_declare_initial_parameters)
|
||||
{
|
||||
this->automatically_declare_initial_parameters_ = automatically_declare_initial_parameters;
|
||||
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
|
||||
@@ -12,12 +12,24 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/parameter.hpp"
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/parameter.hpp"
|
||||
// When compiling this file, Windows produces a deprecation warning for the
|
||||
// deprecated function prototype of NodeParameters::register_param_change_callback().
|
||||
// Other compilers do not.
|
||||
#if defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
#include "rclcpp/node_interfaces/node_parameters.hpp"
|
||||
#if defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
using rclcpp::ParameterType;
|
||||
@@ -28,11 +40,33 @@ Parameter::Parameter()
|
||||
{
|
||||
}
|
||||
|
||||
Parameter::Parameter(const std::string & name)
|
||||
: name_(name), value_()
|
||||
{
|
||||
}
|
||||
|
||||
Parameter::Parameter(const std::string & name, const rclcpp::ParameterValue & value)
|
||||
: name_(name), value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
Parameter::Parameter(const rclcpp::node_interfaces::ParameterInfo & parameter_info)
|
||||
: Parameter(parameter_info.descriptor.name, parameter_info.value)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
Parameter::operator==(const Parameter & rhs) const
|
||||
{
|
||||
return this->name_ == rhs.name_ && this->value_ == rhs.value_;
|
||||
}
|
||||
|
||||
bool
|
||||
Parameter::operator!=(const Parameter & rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
ParameterType
|
||||
Parameter::get_type() const
|
||||
{
|
||||
@@ -57,6 +91,12 @@ Parameter::get_value_message() const
|
||||
return value_.to_value_msg();
|
||||
}
|
||||
|
||||
const rclcpp::ParameterValue &
|
||||
Parameter::get_parameter_value() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
bool
|
||||
Parameter::as_bool() const
|
||||
{
|
||||
|
||||
@@ -227,3 +227,15 @@ ParameterValue::to_value_msg() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
bool
|
||||
ParameterValue::operator==(const ParameterValue & rhs) const
|
||||
{
|
||||
return this->value_ == rhs.value_;
|
||||
}
|
||||
|
||||
bool
|
||||
ParameterValue::operator!=(const ParameterValue & rhs) const
|
||||
{
|
||||
return this->value_ != rhs.value_;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/publisher.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/rmw.h>
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
#include "rcutils/logging_macros.h"
|
||||
@@ -30,9 +31,10 @@
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
#include "rclcpp/intra_process_manager.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
|
||||
using rclcpp::PublisherBase;
|
||||
|
||||
@@ -42,7 +44,7 @@ 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)
|
||||
{
|
||||
rcl_ret_t ret = rcl_publisher_init(
|
||||
&publisher_handle_,
|
||||
@@ -79,6 +81,9 @@ PublisherBase::PublisherBase(
|
||||
|
||||
PublisherBase::~PublisherBase()
|
||||
{
|
||||
// must fini the events before fini-ing the publisher
|
||||
event_handlers_.clear();
|
||||
|
||||
if (rcl_publisher_fini(&intra_process_publisher_handle_, rcl_node_handle_.get()) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
@@ -94,6 +99,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 +157,72 @@ PublisherBase::get_publisher_handle() const
|
||||
return &publisher_handle_;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
|
||||
PublisherBase::get_event_handlers() const
|
||||
{
|
||||
return event_handlers_;
|
||||
}
|
||||
|
||||
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::assert_liveliness() const
|
||||
{
|
||||
return RCL_RET_OK == rcl_publisher_assert_liveliness(&publisher_handle_);
|
||||
}
|
||||
|
||||
bool
|
||||
PublisherBase::operator==(const rmw_gid_t & gid) const
|
||||
{
|
||||
@@ -165,12 +250,24 @@ PublisherBase::operator==(const rmw_gid_t * gid) const
|
||||
return result;
|
||||
}
|
||||
|
||||
rclcpp::mapped_ring_buffer::MappedRingBufferBase::SharedPtr
|
||||
PublisherBase::make_mapped_ring_buffer(size_t size) const
|
||||
{
|
||||
(void)size;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
PublisherBase::setup_intra_process(
|
||||
uint64_t intra_process_publisher_id,
|
||||
StoreMessageCallbackT 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 +296,9 @@ PublisherBase::setup_intra_process(
|
||||
}
|
||||
|
||||
intra_process_publisher_id_ = intra_process_publisher_id;
|
||||
store_intra_process_message_ = 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_);
|
||||
207
rclcpp/src/rclcpp/qos.cpp
Normal file
207
rclcpp/src/rclcpp/qos.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
// 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/qos.hpp"
|
||||
|
||||
#include <rmw/types.h>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
QoSInitialization::QoSInitialization(rmw_qos_history_policy_t history_policy_arg, size_t depth_arg)
|
||||
: history_policy(history_policy_arg), depth(depth_arg)
|
||||
{}
|
||||
|
||||
QoSInitialization
|
||||
QoSInitialization::from_rmw(const rmw_qos_profile_t & rmw_qos)
|
||||
{
|
||||
switch (rmw_qos.history) {
|
||||
case RMW_QOS_POLICY_HISTORY_KEEP_ALL:
|
||||
return KeepAll();
|
||||
case RMW_QOS_POLICY_HISTORY_KEEP_LAST:
|
||||
case RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT:
|
||||
case RMW_QOS_POLICY_HISTORY_UNKNOWN:
|
||||
default:
|
||||
return KeepLast(rmw_qos.depth);
|
||||
}
|
||||
}
|
||||
|
||||
KeepAll::KeepAll()
|
||||
: QoSInitialization(RMW_QOS_POLICY_HISTORY_KEEP_ALL, 0)
|
||||
{}
|
||||
|
||||
KeepLast::KeepLast(size_t depth)
|
||||
: QoSInitialization(RMW_QOS_POLICY_HISTORY_KEEP_LAST, depth)
|
||||
{}
|
||||
|
||||
QoS::QoS(
|
||||
const QoSInitialization & qos_initialization,
|
||||
const rmw_qos_profile_t & initial_profile)
|
||||
: rmw_qos_profile_(initial_profile)
|
||||
{
|
||||
rmw_qos_profile_.history = qos_initialization.history_policy;
|
||||
rmw_qos_profile_.depth = qos_initialization.depth;
|
||||
}
|
||||
|
||||
QoS::QoS(size_t history_depth)
|
||||
: QoS(KeepLast(history_depth))
|
||||
{}
|
||||
|
||||
rmw_qos_profile_t &
|
||||
QoS::get_rmw_qos_profile()
|
||||
{
|
||||
return rmw_qos_profile_;
|
||||
}
|
||||
|
||||
const rmw_qos_profile_t &
|
||||
QoS::get_rmw_qos_profile() const
|
||||
{
|
||||
return rmw_qos_profile_;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::history(rmw_qos_history_policy_t history)
|
||||
{
|
||||
rmw_qos_profile_.history = history;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::keep_last(size_t depth)
|
||||
{
|
||||
rmw_qos_profile_.history = RMW_QOS_POLICY_HISTORY_KEEP_LAST;
|
||||
rmw_qos_profile_.depth = depth;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::keep_all()
|
||||
{
|
||||
rmw_qos_profile_.history = RMW_QOS_POLICY_HISTORY_KEEP_ALL;
|
||||
rmw_qos_profile_.depth = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::reliability(rmw_qos_reliability_policy_t reliability)
|
||||
{
|
||||
rmw_qos_profile_.reliability = reliability;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::reliable()
|
||||
{
|
||||
return this->reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::best_effort()
|
||||
{
|
||||
return this->reliability(RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT);
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::durability(rmw_qos_durability_policy_t durability)
|
||||
{
|
||||
rmw_qos_profile_.durability = durability;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::volitile()
|
||||
{
|
||||
return this->durability(RMW_QOS_POLICY_DURABILITY_VOLATILE);
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::transient_local()
|
||||
{
|
||||
return this->durability(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL);
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::deadline(rmw_time_t deadline)
|
||||
{
|
||||
rmw_qos_profile_.deadline = deadline;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::deadline(const rclcpp::Duration & deadline)
|
||||
{
|
||||
return this->deadline(deadline.to_rmw_time());
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::lifespan(rmw_time_t lifespan)
|
||||
{
|
||||
rmw_qos_profile_.lifespan = lifespan;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::lifespan(const rclcpp::Duration & lifespan)
|
||||
{
|
||||
return this->lifespan(lifespan.to_rmw_time());
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::liveliness(rmw_qos_liveliness_policy_t liveliness)
|
||||
{
|
||||
rmw_qos_profile_.liveliness = liveliness;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::liveliness_lease_duration(rmw_time_t liveliness_lease_duration)
|
||||
{
|
||||
rmw_qos_profile_.liveliness_lease_duration = liveliness_lease_duration;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::liveliness_lease_duration(const rclcpp::Duration & liveliness_lease_duration)
|
||||
{
|
||||
return this->liveliness_lease_duration(liveliness_lease_duration.to_rmw_time());
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::avoid_ros_namespace_conventions(bool avoid_ros_namespace_conventions)
|
||||
{
|
||||
rmw_qos_profile_.avoid_ros_namespace_conventions = avoid_ros_namespace_conventions;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SensorDataQoS::SensorDataQoS(const QoSInitialization & qos_initialization)
|
||||
: QoS(qos_initialization, rmw_qos_profile_sensor_data)
|
||||
{}
|
||||
|
||||
ParametersQoS::ParametersQoS(const QoSInitialization & qos_initialization)
|
||||
: QoS(qos_initialization, rmw_qos_profile_parameters)
|
||||
{}
|
||||
|
||||
ServicesQoS::ServicesQoS(const QoSInitialization & qos_initialization)
|
||||
: QoS(qos_initialization, rmw_qos_profile_services_default)
|
||||
{}
|
||||
|
||||
ParameterEventsQoS::ParameterEventsQoS(const QoSInitialization & qos_initialization)
|
||||
: QoS(qos_initialization, rmw_qos_profile_parameter_events)
|
||||
{}
|
||||
|
||||
SystemDefaultsQoS::SystemDefaultsQoS(const QoSInitialization & qos_initialization)
|
||||
: QoS(qos_initialization, rmw_qos_profile_system_default)
|
||||
{}
|
||||
|
||||
} // namespace rclcpp
|
||||
55
rclcpp/src/rclcpp/qos_event.cpp
Normal file
55
rclcpp/src/rclcpp/qos_event.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
// 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/qos_event.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
QOSEventHandlerBase::~QOSEventHandlerBase()
|
||||
{
|
||||
if (rcl_event_fini(&event_handle_) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Error in destruction of rcl event handle: %s", rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the number of ready events.
|
||||
size_t
|
||||
QOSEventHandlerBase::get_number_of_ready_events()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// Add the Waitable to a wait set.
|
||||
bool
|
||||
QOSEventHandlerBase::add_to_wait_set(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
rcl_ret_t ret = rcl_wait_set_add_event(wait_set, &event_handle_, &wait_set_event_index_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
exceptions::throw_from_rcl_error(ret, "Couldn't add event to wait set");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Check if the Waitable is ready.
|
||||
bool
|
||||
QOSEventHandlerBase::is_ready(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
return wait_set->events[wait_set_event_index_] == &event_handle_;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
@@ -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
|
||||
|
||||
@@ -12,14 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/subscription_base.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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 +36,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 +84,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 *
|
||||
@@ -106,6 +122,12 @@ SubscriptionBase::get_intra_process_subscription_handle() const
|
||||
return intra_process_subscription_handle_;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
|
||||
SubscriptionBase::get_event_handlers() const
|
||||
{
|
||||
return event_handlers_;
|
||||
}
|
||||
|
||||
const rosidl_message_type_support_t &
|
||||
SubscriptionBase::get_message_type_support_handle() const
|
||||
{
|
||||
@@ -117,3 +139,49 @@ 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;
|
||||
}
|
||||
|
||||
void SubscriptionBase::setup_intra_process(
|
||||
uint64_t intra_process_subscription_id,
|
||||
IntraProcessManagerWeakPtr weak_ipm,
|
||||
const rcl_subscription_options_t & intra_process_options)
|
||||
{
|
||||
std::string intra_process_topic_name = std::string(get_topic_name()) + "/_intra";
|
||||
rcl_ret_t ret = rcl_subscription_init(
|
||||
intra_process_subscription_handle_.get(),
|
||||
node_handle_.get(),
|
||||
rclcpp::type_support::get_intra_process_message_msg_type_support(),
|
||||
intra_process_topic_name.c_str(),
|
||||
&intra_process_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
|
||||
auto rcl_node_handle = node_handle_.get();
|
||||
// this will throw on any validation problem
|
||||
rcl_reset_error();
|
||||
expand_topic_or_service_name(
|
||||
intra_process_topic_name,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle));
|
||||
}
|
||||
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create intra process subscription");
|
||||
}
|
||||
|
||||
intra_process_subscription_id_ = intra_process_subscription_id;
|
||||
weak_ipm_ = weak_ipm;
|
||||
use_intra_process_ = true;
|
||||
}
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "rclcpp/time.hpp"
|
||||
#include "rclcpp/time_source.hpp"
|
||||
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
@@ -78,20 +77,29 @@ void TimeSource::attachNode(
|
||||
|
||||
logger_ = node_logging_->get_logger();
|
||||
|
||||
rclcpp::Parameter use_sim_time_param;
|
||||
if (node_parameters_->get_parameter("use_sim_time", use_sim_time_param)) {
|
||||
if (use_sim_time_param.get_type() == rclcpp::PARAMETER_BOOL) {
|
||||
if (use_sim_time_param.get_value<bool>() == true) {
|
||||
parameter_state_ = SET_TRUE;
|
||||
enable_ros_time();
|
||||
create_clock_sub();
|
||||
}
|
||||
} else {
|
||||
RCLCPP_ERROR(logger_, "Invalid type for parameter 'use_sim_time' %s should be bool",
|
||||
use_sim_time_param.get_type_name().c_str());
|
||||
// Though this defaults to false, it can be overridden by initial parameter values for the node,
|
||||
// which may be given by the user at the node's construction or even by command-line arguments.
|
||||
rclcpp::ParameterValue use_sim_time_param;
|
||||
const char * use_sim_time_name = "use_sim_time";
|
||||
if (!node_parameters_->has_parameter(use_sim_time_name)) {
|
||||
use_sim_time_param = node_parameters_->declare_parameter(
|
||||
use_sim_time_name,
|
||||
rclcpp::ParameterValue(false),
|
||||
rcl_interfaces::msg::ParameterDescriptor());
|
||||
} else {
|
||||
use_sim_time_param = node_parameters_->get_parameter(use_sim_time_name).get_parameter_value();
|
||||
}
|
||||
if (use_sim_time_param.get_type() == rclcpp::PARAMETER_BOOL) {
|
||||
if (use_sim_time_param.get<bool>()) {
|
||||
parameter_state_ = SET_TRUE;
|
||||
enable_ros_time();
|
||||
create_clock_sub();
|
||||
}
|
||||
} else {
|
||||
RCLCPP_DEBUG(logger_, "'use_sim_time' parameter not set, using wall time by default.");
|
||||
// TODO(wjwwood): use set_on_parameters_set_callback to catch the type mismatch,
|
||||
// before the use_sim_time parameter can ever be set to an invalid value
|
||||
RCLCPP_ERROR(logger_, "Invalid type '%s' for parameter 'use_sim_time', should be 'bool'",
|
||||
rclcpp::to_string(use_sim_time_param.get_type()).c_str());
|
||||
}
|
||||
|
||||
// TODO(tfoote) use parameters interface not subscribe to events via topic ticketed #609
|
||||
@@ -199,26 +207,13 @@ void TimeSource::create_clock_sub()
|
||||
// Subscription already created.
|
||||
return;
|
||||
}
|
||||
const std::string topic_name = "/clock";
|
||||
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group;
|
||||
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
|
||||
auto msg_mem_strat = MessageMemoryStrategy<MessageT, Alloc>::create_default();
|
||||
auto allocator = std::make_shared<Alloc>();
|
||||
auto cb = std::bind(&TimeSource::clock_cb, this, std::placeholders::_1);
|
||||
|
||||
clock_subscription_ = rclcpp::create_subscription<
|
||||
MessageT, decltype(cb), Alloc, MessageT, SubscriptionT
|
||||
>(
|
||||
node_topics_.get(),
|
||||
topic_name,
|
||||
std::move(cb),
|
||||
rmw_qos_profile_default,
|
||||
group,
|
||||
false,
|
||||
false,
|
||||
msg_mem_strat,
|
||||
allocator);
|
||||
clock_subscription_ = rclcpp::create_subscription<rosgraph_msgs::msg::Clock>(
|
||||
node_topics_,
|
||||
"/clock",
|
||||
rclcpp::QoS(QoSInitialization::from_rmw(rmw_qos_profile_default)),
|
||||
std::bind(&TimeSource::clock_cb, this, std::placeholders::_1)
|
||||
);
|
||||
}
|
||||
|
||||
void TimeSource::destroy_clock_sub()
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
using rclcpp::TimerBase;
|
||||
@@ -75,6 +77,17 @@ TimerBase::cancel()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TimerBase::is_canceled()
|
||||
{
|
||||
bool is_canceled = false;
|
||||
rcl_ret_t ret = rcl_timer_is_canceled(timer_handle_.get(), &is_canceled);
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't get timer cancelled state");
|
||||
}
|
||||
return is_canceled;
|
||||
}
|
||||
|
||||
void
|
||||
TimerBase::reset()
|
||||
{
|
||||
|
||||
@@ -24,45 +24,48 @@
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/rcl.h"
|
||||
|
||||
void
|
||||
rclcpp::init(int argc, char const * const argv[], const rclcpp::InitOptions & init_options)
|
||||
namespace rclcpp
|
||||
{
|
||||
using rclcpp::contexts::default_context::get_global_default_context;
|
||||
|
||||
void
|
||||
init(int argc, char const * const argv[], const InitOptions & init_options)
|
||||
{
|
||||
using contexts::default_context::get_global_default_context;
|
||||
get_global_default_context()->init(argc, argv, init_options);
|
||||
// Install the signal handlers.
|
||||
rclcpp::install_signal_handlers();
|
||||
install_signal_handlers();
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::install_signal_handlers()
|
||||
install_signal_handlers()
|
||||
{
|
||||
return rclcpp::SignalHandler::get_global_signal_handler().install();
|
||||
return SignalHandler::get_global_signal_handler().install();
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::signal_handlers_installed()
|
||||
signal_handlers_installed()
|
||||
{
|
||||
return rclcpp::SignalHandler::get_global_signal_handler().is_installed();
|
||||
return SignalHandler::get_global_signal_handler().is_installed();
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::uninstall_signal_handlers()
|
||||
uninstall_signal_handlers()
|
||||
{
|
||||
return rclcpp::SignalHandler::get_global_signal_handler().uninstall();
|
||||
return SignalHandler::get_global_signal_handler().uninstall();
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
rclcpp::init_and_remove_ros_arguments(
|
||||
init_and_remove_ros_arguments(
|
||||
int argc,
|
||||
char const * const argv[],
|
||||
const rclcpp::InitOptions & init_options)
|
||||
const InitOptions & init_options)
|
||||
{
|
||||
rclcpp::init(argc, argv, init_options);
|
||||
return rclcpp::remove_ros_arguments(argc, argv);
|
||||
init(argc, argv, init_options);
|
||||
return remove_ros_arguments(argc, argv);
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
rclcpp::remove_ros_arguments(int argc, char const * const argv[])
|
||||
remove_ros_arguments(int argc, char const * const argv[])
|
||||
{
|
||||
rcl_allocator_t alloc = rcl_get_default_allocator();
|
||||
rcl_arguments_t parsed_args = rcl_get_zero_initialized_arguments();
|
||||
@@ -71,7 +74,7 @@ rclcpp::remove_ros_arguments(int argc, char const * const argv[])
|
||||
|
||||
ret = rcl_parse_arguments(argc, argv, alloc, &parsed_args);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to parse arguments");
|
||||
exceptions::throw_from_rcl_error(ret, "failed to parse arguments");
|
||||
}
|
||||
|
||||
int nonros_argc = 0;
|
||||
@@ -84,9 +87,9 @@ rclcpp::remove_ros_arguments(int argc, char const * const argv[])
|
||||
&nonros_argc,
|
||||
&nonros_argv);
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
if (RCL_RET_OK != ret || nonros_argc < 0) {
|
||||
// Not using throw_from_rcl_error, because we may need to append deallocation failures.
|
||||
rclcpp::exceptions::RCLErrorBase base_exc(ret, rcl_get_error_state());
|
||||
exceptions::RCLErrorBase base_exc(ret, rcl_get_error_state());
|
||||
rcl_reset_error();
|
||||
if (NULL != nonros_argv) {
|
||||
alloc.deallocate(nonros_argv, alloc.state);
|
||||
@@ -97,11 +100,10 @@ rclcpp::remove_ros_arguments(int argc, char const * const argv[])
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
}
|
||||
throw rclcpp::exceptions::RCLError(base_exc, "");
|
||||
throw exceptions::RCLError(base_exc, "");
|
||||
}
|
||||
|
||||
std::vector<std::string> return_arguments;
|
||||
return_arguments.resize(nonros_argc);
|
||||
std::vector<std::string> return_arguments(nonros_argc);
|
||||
|
||||
for (int ii = 0; ii < nonros_argc; ++ii) {
|
||||
return_arguments[ii] = std::string(nonros_argv[ii]);
|
||||
@@ -113,7 +115,7 @@ rclcpp::remove_ros_arguments(int argc, char const * const argv[])
|
||||
|
||||
ret = rcl_arguments_fini(&parsed_args);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
exceptions::throw_from_rcl_error(
|
||||
ret, "failed to cleanup parsed arguments, leaking memory");
|
||||
}
|
||||
|
||||
@@ -121,9 +123,9 @@ rclcpp::remove_ros_arguments(int argc, char const * const argv[])
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::ok(rclcpp::Context::SharedPtr context)
|
||||
ok(Context::SharedPtr context)
|
||||
{
|
||||
using rclcpp::contexts::default_context::get_global_default_context;
|
||||
using contexts::default_context::get_global_default_context;
|
||||
if (nullptr == context) {
|
||||
context = get_global_default_context();
|
||||
}
|
||||
@@ -131,30 +133,30 @@ rclcpp::ok(rclcpp::Context::SharedPtr context)
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::is_initialized(rclcpp::Context::SharedPtr context)
|
||||
is_initialized(Context::SharedPtr context)
|
||||
{
|
||||
return rclcpp::ok(context);
|
||||
return ok(context);
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::shutdown(rclcpp::Context::SharedPtr context, const std::string & reason)
|
||||
shutdown(Context::SharedPtr context, const std::string & reason)
|
||||
{
|
||||
using rclcpp::contexts::default_context::get_global_default_context;
|
||||
using contexts::default_context::get_global_default_context;
|
||||
auto default_context = get_global_default_context();
|
||||
if (nullptr == context) {
|
||||
context = default_context;
|
||||
}
|
||||
bool ret = context->shutdown(reason);
|
||||
if (context == default_context) {
|
||||
rclcpp::uninstall_signal_handlers();
|
||||
uninstall_signal_handlers();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::on_shutdown(std::function<void()> callback, rclcpp::Context::SharedPtr context)
|
||||
on_shutdown(std::function<void()> callback, Context::SharedPtr context)
|
||||
{
|
||||
using rclcpp::contexts::default_context::get_global_default_context;
|
||||
using contexts::default_context::get_global_default_context;
|
||||
if (nullptr == context) {
|
||||
context = get_global_default_context();
|
||||
}
|
||||
@@ -162,9 +164,9 @@ rclcpp::on_shutdown(std::function<void()> callback, rclcpp::Context::SharedPtr c
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::sleep_for(const std::chrono::nanoseconds & nanoseconds, rclcpp::Context::SharedPtr context)
|
||||
sleep_for(const std::chrono::nanoseconds & nanoseconds, Context::SharedPtr context)
|
||||
{
|
||||
using rclcpp::contexts::default_context::get_global_default_context;
|
||||
using contexts::default_context::get_global_default_context;
|
||||
if (nullptr == context) {
|
||||
context = get_global_default_context();
|
||||
}
|
||||
@@ -172,13 +174,15 @@ rclcpp::sleep_for(const std::chrono::nanoseconds & nanoseconds, rclcpp::Context:
|
||||
}
|
||||
|
||||
const char *
|
||||
rclcpp::get_c_string(const char * string_in)
|
||||
get_c_string(const char * string_in)
|
||||
{
|
||||
return string_in;
|
||||
}
|
||||
|
||||
const char *
|
||||
rclcpp::get_c_string(const std::string & string_in)
|
||||
get_c_string(const std::string & string_in)
|
||||
{
|
||||
return string_in.c_str();
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -34,6 +34,12 @@ Waitable::get_number_of_ready_clients()
|
||||
return 0u;
|
||||
}
|
||||
|
||||
size_t
|
||||
Waitable::get_number_of_ready_events()
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
size_t
|
||||
Waitable::get_number_of_ready_services()
|
||||
{
|
||||
|
||||
64
rclcpp/test/node_interfaces/node_wrapper.hpp
Normal file
64
rclcpp/test/node_interfaces/node_wrapper.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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 NODE_INTERFACES__NODE_WRAPPER_HPP_
|
||||
#define NODE_INTERFACES__NODE_WRAPPER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
class NodeWrapper
|
||||
{
|
||||
public:
|
||||
explicit NodeWrapper(const std::string & name)
|
||||
: node(std::make_shared<rclcpp::Node>(name))
|
||||
{}
|
||||
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
||||
get_node_base_interface() {return this->node->get_node_base_interface();}
|
||||
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr
|
||||
get_node_clock_interface() {return this->node->get_node_clock_interface();}
|
||||
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr
|
||||
get_node_graph_interface() {return this->node->get_node_graph_interface();}
|
||||
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr
|
||||
get_node_logging_interface() {return this->node->get_node_logging_interface();}
|
||||
|
||||
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
|
||||
get_node_timers_interface() {return this->node->get_node_timers_interface();}
|
||||
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr
|
||||
get_node_topics_interface() {return this->node->get_node_topics_interface();}
|
||||
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
|
||||
get_node_services_interface() {return this->node->get_node_services_interface();}
|
||||
|
||||
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr
|
||||
get_node_waitables_interface() {return this->node->get_node_waitables_interface();}
|
||||
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
|
||||
get_node_parameters_interface() {return this->node->get_node_parameters_interface();}
|
||||
|
||||
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr
|
||||
get_node_time_source_interface() {return this->node->get_node_time_source_interface();}
|
||||
|
||||
private:
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
#endif // NODE_INTERFACES__NODE_WRAPPER_HPP_
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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 "rclcpp/node_interfaces/get_node_topics_interface.hpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node");
|
||||
std::shared_ptr<const rclcpp::Node> const_node_ptr = node;
|
||||
// Should fail because a const node cannot have a non-const method called on it.
|
||||
rclcpp::node_interfaces::NodeTopicsInterface * result =
|
||||
rclcpp::node_interfaces::get_node_topics_interface(const_node_ptr);
|
||||
(void)result;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 "rclcpp/node_interfaces/get_node_topics_interface.hpp"
|
||||
|
||||
#include "../node_wrapper.hpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
auto node = std::make_shared<NodeWrapper>("test_wrapped_node");
|
||||
std::shared_ptr<const NodeWrapper> const_node_ptr = node;
|
||||
// Should fail because a const node cannot have a non-const method called on it.
|
||||
rclcpp::node_interfaces::NodeTopicsInterface * result =
|
||||
rclcpp::node_interfaces::get_node_topics_interface(const_node_ptr);
|
||||
(void)result;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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 "rclcpp/node_interfaces/get_node_topics_interface.hpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node");
|
||||
const rclcpp::Node & const_node_reference = *node;
|
||||
// Should fail because a const node cannot have a non-const method called on it.
|
||||
rclcpp::node_interfaces::NodeTopicsInterface * result =
|
||||
rclcpp::node_interfaces::get_node_topics_interface(const_node_reference);
|
||||
(void)result;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 "rclcpp/node_interfaces/get_node_topics_interface.hpp"
|
||||
|
||||
#include "../node_wrapper.hpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
auto node = std::make_shared<NodeWrapper>("test_wrapped_node");
|
||||
const NodeWrapper & const_node_reference = *node;
|
||||
// Should fail because a const node cannot have a non-const method called on it.
|
||||
rclcpp::node_interfaces::NodeTopicsInterface * result =
|
||||
rclcpp::node_interfaces::get_node_topics_interface(const_node_reference);
|
||||
(void)result;
|
||||
}
|
||||
97
rclcpp/test/node_interfaces/test_get_node_interfaces.cpp
Normal file
97
rclcpp/test/node_interfaces/test_get_node_interfaces.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
// 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 <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "./node_wrapper.hpp"
|
||||
|
||||
static const std::string node_suffix = "test_get_node_interfaces"; // NOLINT
|
||||
|
||||
class TestGetNodeInterfaces : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
node = std::make_shared<rclcpp::Node>(node_suffix);
|
||||
wrapped_node = std::make_shared<NodeWrapper>("wrapped_" + node_suffix);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
node.reset();
|
||||
wrapped_node.reset();
|
||||
}
|
||||
|
||||
static rclcpp::Node::SharedPtr node;
|
||||
static std::shared_ptr<NodeWrapper> wrapped_node;
|
||||
};
|
||||
|
||||
rclcpp::Node::SharedPtr TestGetNodeInterfaces::node = nullptr;
|
||||
std::shared_ptr<NodeWrapper> TestGetNodeInterfaces::wrapped_node = nullptr;
|
||||
|
||||
TEST_F(TestGetNodeInterfaces, rclcpp_node_shared_ptr) {
|
||||
auto result = rclcpp::node_interfaces::get_node_topics_interface(this->node);
|
||||
static_assert(
|
||||
std::is_same<
|
||||
rclcpp::node_interfaces::NodeTopicsInterface *,
|
||||
decltype(result)
|
||||
>::value, "expected rclcpp::node_interfaces::NodeTopicsInterface *");
|
||||
}
|
||||
|
||||
TEST_F(TestGetNodeInterfaces, node_shared_ptr) {
|
||||
auto result = rclcpp::node_interfaces::get_node_topics_interface(this->wrapped_node);
|
||||
static_assert(
|
||||
std::is_same<
|
||||
rclcpp::node_interfaces::NodeTopicsInterface *,
|
||||
decltype(result)
|
||||
>::value, "expected rclcpp::node_interfaces::NodeTopicsInterface *");
|
||||
}
|
||||
|
||||
TEST_F(TestGetNodeInterfaces, rclcpp_node_reference) {
|
||||
rclcpp::Node & node_reference = *this->node;
|
||||
auto result = rclcpp::node_interfaces::get_node_topics_interface(node_reference);
|
||||
static_assert(
|
||||
std::is_same<
|
||||
rclcpp::node_interfaces::NodeTopicsInterface *,
|
||||
decltype(result)
|
||||
>::value, "expected rclcpp::node_interfaces::NodeTopicsInterface *");
|
||||
}
|
||||
|
||||
TEST_F(TestGetNodeInterfaces, node_reference) {
|
||||
NodeWrapper & wrapped_node_reference = *this->wrapped_node;
|
||||
auto result = rclcpp::node_interfaces::get_node_topics_interface(wrapped_node_reference);
|
||||
static_assert(
|
||||
std::is_same<
|
||||
rclcpp::node_interfaces::NodeTopicsInterface *,
|
||||
decltype(result)
|
||||
>::value, "expected rclcpp::node_interfaces::NodeTopicsInterface *");
|
||||
}
|
||||
|
||||
TEST_F(TestGetNodeInterfaces, interface_shared_pointer) {
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeTopicsInterface> interface_shared_ptr =
|
||||
this->node->get_node_topics_interface();
|
||||
auto result = rclcpp::node_interfaces::get_node_topics_interface(interface_shared_ptr);
|
||||
static_assert(
|
||||
std::is_same<
|
||||
rclcpp::node_interfaces::NodeTopicsInterface *,
|
||||
decltype(result)
|
||||
>::value, "expected rclcpp::node_interfaces::NodeTopicsInterface *");
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ TEST(TestDuration, operators) {
|
||||
rclcpp::Duration time = rclcpp::Duration(0, 0);
|
||||
rclcpp::Duration copy_constructor_duration(time);
|
||||
rclcpp::Duration assignment_op_duration = rclcpp::Duration(1, 0);
|
||||
(void)assignment_op_duration;
|
||||
assignment_op_duration = time;
|
||||
|
||||
EXPECT_TRUE(time == copy_constructor_duration);
|
||||
@@ -75,6 +76,14 @@ TEST(TestDuration, chrono_overloads) {
|
||||
EXPECT_EQ(d1, d2);
|
||||
EXPECT_EQ(d1, d3);
|
||||
EXPECT_EQ(d2, d3);
|
||||
|
||||
// check non-nanosecond durations
|
||||
std::chrono::milliseconds chrono_ms(100);
|
||||
auto d4 = rclcpp::Duration(chrono_ms);
|
||||
EXPECT_EQ(chrono_ms, d4.to_chrono<std::chrono::nanoseconds>());
|
||||
std::chrono::duration<double, std::chrono::seconds::period> chrono_float_seconds(3.14);
|
||||
auto d5 = rclcpp::Duration(chrono_float_seconds);
|
||||
EXPECT_EQ(chrono_float_seconds, d5.to_chrono<decltype(chrono_float_seconds)>());
|
||||
}
|
||||
|
||||
TEST(TestDuration, overflows) {
|
||||
|
||||
51
rclcpp/test/test_interface_traits.cpp
Normal file
51
rclcpp/test/test_interface_traits.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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 "rclcpp/node_interfaces/interface_traits.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
|
||||
class MyNode
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> get_node_base_interface() const
|
||||
{
|
||||
return std::make_shared<rclcpp::node_interfaces::NodeBase>("my_node_name", "my_node_namespace", nullptr, rclcpp::NodeOptions());
|
||||
}
|
||||
};
|
||||
|
||||
class WrongNode
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> not_get_node_base_interface()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, typename std::enable_if<rclcpp::node_interfaces::has_node_base_interface<T>::value>::type* = nullptr>
|
||||
void get_node_name(const T & nodelike) {
|
||||
ASSERT_STREQ("my_node_name", nodelike.get_node_base_interface()->get_name());
|
||||
}
|
||||
|
||||
TEST(TestInterfaceTraits, has_node_base_interface) {
|
||||
ASSERT_TRUE(rclcpp::node_interfaces::has_node_base_interface<MyNode>::value);
|
||||
ASSERT_FALSE(rclcpp::node_interfaces::has_node_base_interface<WrongNode>::value);
|
||||
ASSERT_TRUE(rclcpp::node_interfaces::has_node_base_interface<rclcpp::Node>::value);
|
||||
|
||||
get_node_name(MyNode());
|
||||
}
|
||||
|
||||
@@ -14,10 +14,13 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#define RCLCPP_BUILDING_LIBRARY 1
|
||||
#include "gtest/gtest.h"
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/mapped_ring_buffer.hpp"
|
||||
#include "rmw/types.h"
|
||||
|
||||
// Mock up publisher and subscription base to avoid needing an rmw impl.
|
||||
@@ -50,6 +53,14 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual
|
||||
mapped_ring_buffer::MappedRingBufferBase::SharedPtr
|
||||
make_mapped_ring_buffer(size_t size) const
|
||||
{
|
||||
(void)size;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string mock_topic_name;
|
||||
size_t mock_queue_size;
|
||||
};
|
||||
@@ -71,6 +82,15 @@ public:
|
||||
allocator_ = std::make_shared<MessageAlloc>();
|
||||
}
|
||||
|
||||
mapped_ring_buffer::MappedRingBufferBase::SharedPtr
|
||||
make_mapped_ring_buffer(size_t size) const override
|
||||
{
|
||||
return mapped_ring_buffer::MappedRingBuffer<
|
||||
T,
|
||||
typename Publisher<T, Alloc>::MessageAlloc
|
||||
>::make_shared(size, allocator_);
|
||||
}
|
||||
|
||||
std::shared_ptr<MessageAlloc> get_allocator()
|
||||
{
|
||||
return allocator_;
|
||||
@@ -109,10 +129,9 @@ public:
|
||||
} // namespace mock
|
||||
} // namespace rclcpp
|
||||
|
||||
// Prevent rclcpp/publisher.hpp and rclcpp/subscription.hpp from being imported.
|
||||
#define RCLCPP__PUBLISHER_HPP_
|
||||
#define RCLCPP__SUBSCRIPTION_HPP_
|
||||
#define RCLCPP_BUILDING_LIBRARY 1
|
||||
// Prevent rclcpp/publisher_base.hpp and rclcpp/subscription.hpp from being imported.
|
||||
#define RCLCPP__PUBLISHER_BASE_HPP_
|
||||
#define RCLCPP__SUBSCRIPTION_BASE_HPP_
|
||||
// Force ipm to use our mock publisher class.
|
||||
#define Publisher mock::Publisher
|
||||
#define PublisherBase mock::PublisherBase
|
||||
@@ -155,10 +174,8 @@ TEST(TestIntraProcessManager, nominal) {
|
||||
s1->mock_topic_name = "nominal1";
|
||||
s1->mock_queue_size = 10;
|
||||
|
||||
auto p1_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
|
||||
auto p2_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p2);
|
||||
auto p1_id = ipm.add_publisher(p1);
|
||||
auto p2_id = ipm.add_publisher(p2);
|
||||
auto s1_id = ipm.add_subscription(s1);
|
||||
|
||||
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
|
||||
@@ -169,14 +186,14 @@ TEST(TestIntraProcessManager, nominal) {
|
||||
);
|
||||
|
||||
auto p1_m1_original_address = unique_msg.get();
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
ipm_msg->message_sequence = 43;
|
||||
ipm_msg->publisher_id = 43;
|
||||
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
|
||||
|
||||
auto p2_m1_id = ipm.store_intra_process_message(p2_id, unique_msg);
|
||||
auto p2_m1_id = ipm.store_intra_process_message(p2_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
ipm.take_intra_process_message(p2_id, p2_m1_id, s1_id, unique_msg);
|
||||
@@ -198,26 +215,22 @@ TEST(TestIntraProcessManager, nominal) {
|
||||
ipm_msg->publisher_id = 44;
|
||||
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
|
||||
|
||||
ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
ipm_msg->message_sequence = 45;
|
||||
ipm_msg->publisher_id = 45;
|
||||
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
|
||||
|
||||
ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
ipm_msg->message_sequence = 46;
|
||||
ipm_msg->publisher_id = 46;
|
||||
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
|
||||
|
||||
ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
ASSERT_NE(nullptr, unique_msg);
|
||||
if (unique_msg) {
|
||||
EXPECT_EQ(44ul, unique_msg->message_sequence);
|
||||
EXPECT_EQ(44ul, unique_msg->publisher_id);
|
||||
}
|
||||
ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -240,8 +253,7 @@ TEST(TestIntraProcessManager, remove_publisher_before_trying_to_take) {
|
||||
s1->mock_topic_name = "nominal1";
|
||||
s1->mock_queue_size = 10;
|
||||
|
||||
auto p1_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
|
||||
auto p1_id = ipm.add_publisher(p1);
|
||||
auto s1_id = ipm.add_subscription(s1);
|
||||
|
||||
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
|
||||
@@ -251,7 +263,7 @@ TEST(TestIntraProcessManager, remove_publisher_before_trying_to_take) {
|
||||
new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg)
|
||||
);
|
||||
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
ipm.remove_publisher(p1_id);
|
||||
@@ -290,8 +302,7 @@ TEST(TestIntraProcessManager, removed_subscription_affects_take) {
|
||||
s3->mock_topic_name = "nominal1";
|
||||
s3->mock_queue_size = 10;
|
||||
|
||||
auto p1_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
|
||||
auto p1_id = ipm.add_publisher(p1);
|
||||
auto s1_id = ipm.add_subscription(s1);
|
||||
auto s2_id = ipm.add_subscription(s2);
|
||||
auto s3_id = ipm.add_subscription(s3);
|
||||
@@ -304,7 +315,7 @@ TEST(TestIntraProcessManager, removed_subscription_affects_take) {
|
||||
);
|
||||
|
||||
auto original_message_pointer = unique_msg.get();
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
ipm.take_intra_process_message(p1_id, p1_m1_id, s1_id, unique_msg);
|
||||
@@ -361,8 +372,7 @@ TEST(TestIntraProcessManager, multiple_subscriptions_one_publisher) {
|
||||
s3->mock_topic_name = "nominal1";
|
||||
s3->mock_queue_size = 10;
|
||||
|
||||
auto p1_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
|
||||
auto p1_id = ipm.add_publisher(p1);
|
||||
auto s1_id = ipm.add_subscription(s1);
|
||||
auto s2_id = ipm.add_subscription(s2);
|
||||
auto s3_id = ipm.add_subscription(s3);
|
||||
@@ -375,7 +385,7 @@ TEST(TestIntraProcessManager, multiple_subscriptions_one_publisher) {
|
||||
);
|
||||
|
||||
auto original_message_pointer = unique_msg.get();
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
ipm.take_intra_process_message(p1_id, p1_m1_id, s1_id, unique_msg);
|
||||
@@ -437,12 +447,9 @@ TEST(TestIntraProcessManager, multiple_publishers_one_subscription) {
|
||||
s1->mock_topic_name = "nominal1";
|
||||
s1->mock_queue_size = 10;
|
||||
|
||||
auto p1_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
|
||||
auto p2_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p2);
|
||||
auto p3_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p3);
|
||||
auto p1_id = ipm.add_publisher(p1);
|
||||
auto p2_id = ipm.add_publisher(p2);
|
||||
auto p3_id = ipm.add_publisher(p3);
|
||||
auto s1_id = ipm.add_subscription(s1);
|
||||
|
||||
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
|
||||
@@ -454,7 +461,7 @@ TEST(TestIntraProcessManager, multiple_publishers_one_subscription) {
|
||||
);
|
||||
|
||||
auto original_message_pointer1 = unique_msg.get();
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
// Second publish
|
||||
@@ -463,7 +470,7 @@ TEST(TestIntraProcessManager, multiple_publishers_one_subscription) {
|
||||
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
|
||||
|
||||
auto original_message_pointer2 = unique_msg.get();
|
||||
auto p2_m1_id = ipm.store_intra_process_message(p2_id, unique_msg);
|
||||
auto p2_m1_id = ipm.store_intra_process_message(p2_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
// Third publish
|
||||
@@ -472,7 +479,7 @@ TEST(TestIntraProcessManager, multiple_publishers_one_subscription) {
|
||||
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
|
||||
|
||||
auto original_message_pointer3 = unique_msg.get();
|
||||
auto p3_m1_id = ipm.store_intra_process_message(p3_id, unique_msg);
|
||||
auto p3_m1_id = ipm.store_intra_process_message(p3_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
// First take
|
||||
@@ -545,12 +552,9 @@ TEST(TestIntraProcessManager, multiple_publishers_multiple_subscription) {
|
||||
s3->mock_topic_name = "nominal1";
|
||||
s3->mock_queue_size = 10;
|
||||
|
||||
auto p1_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
|
||||
auto p2_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p2);
|
||||
auto p3_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p3);
|
||||
auto p1_id = ipm.add_publisher(p1);
|
||||
auto p2_id = ipm.add_publisher(p2);
|
||||
auto p3_id = ipm.add_publisher(p3);
|
||||
auto s1_id = ipm.add_subscription(s1);
|
||||
auto s2_id = ipm.add_subscription(s2);
|
||||
auto s3_id = ipm.add_subscription(s3);
|
||||
@@ -564,7 +568,7 @@ TEST(TestIntraProcessManager, multiple_publishers_multiple_subscription) {
|
||||
);
|
||||
|
||||
auto original_message_pointer1 = unique_msg.get();
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
// Second publish
|
||||
@@ -573,7 +577,7 @@ TEST(TestIntraProcessManager, multiple_publishers_multiple_subscription) {
|
||||
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
|
||||
|
||||
auto original_message_pointer2 = unique_msg.get();
|
||||
auto p2_m1_id = ipm.store_intra_process_message(p2_id, unique_msg);
|
||||
auto p2_m1_id = ipm.store_intra_process_message(p2_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
// Third publish
|
||||
@@ -582,7 +586,7 @@ TEST(TestIntraProcessManager, multiple_publishers_multiple_subscription) {
|
||||
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
|
||||
|
||||
auto original_message_pointer3 = unique_msg.get();
|
||||
auto p3_m1_id = ipm.store_intra_process_message(p3_id, unique_msg);
|
||||
auto p3_m1_id = ipm.store_intra_process_message(p3_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
// First take
|
||||
@@ -692,8 +696,7 @@ TEST(TestIntraProcessManager, ring_buffer_displacement) {
|
||||
s1->mock_topic_name = "nominal1";
|
||||
s1->mock_queue_size = 10;
|
||||
|
||||
auto p1_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
|
||||
auto p1_id = ipm.add_publisher(p1);
|
||||
auto s1_id = ipm.add_subscription(s1);
|
||||
|
||||
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
|
||||
@@ -703,8 +706,7 @@ TEST(TestIntraProcessManager, ring_buffer_displacement) {
|
||||
new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg)
|
||||
);
|
||||
|
||||
auto original_message_pointer1 = unique_msg.get();
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
ipm_msg->message_sequence = 43;
|
||||
@@ -712,7 +714,7 @@ TEST(TestIntraProcessManager, ring_buffer_displacement) {
|
||||
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
|
||||
|
||||
auto original_message_pointer2 = unique_msg.get();
|
||||
auto p1_m2_id = ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
auto p1_m2_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
ipm.take_intra_process_message(p1_id, p1_m2_id, s1_id, unique_msg);
|
||||
@@ -728,14 +730,8 @@ TEST(TestIntraProcessManager, ring_buffer_displacement) {
|
||||
ipm_msg->publisher_id = 44;
|
||||
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
|
||||
|
||||
ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
EXPECT_NE(nullptr, unique_msg); // Should return the thing in the ring buffer it displaced.
|
||||
if (unique_msg) {
|
||||
// This should have been the first published message.
|
||||
EXPECT_EQ(42ul, unique_msg->message_sequence);
|
||||
EXPECT_EQ(42ul, unique_msg->publisher_id);
|
||||
EXPECT_EQ(original_message_pointer1, unique_msg.get());
|
||||
}
|
||||
ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
EXPECT_EQ(nullptr, unique_msg);
|
||||
unique_msg.reset();
|
||||
|
||||
// Since it just got displaced it should no longer be there to take.
|
||||
@@ -759,8 +755,7 @@ TEST(TestIntraProcessManager, subscription_creation_race_condition) {
|
||||
p1->mock_topic_name = "nominal1";
|
||||
p1->mock_queue_size = 2;
|
||||
|
||||
auto p1_id =
|
||||
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
|
||||
auto p1_id = ipm.add_publisher(p1);
|
||||
|
||||
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
|
||||
ipm_msg->message_sequence = 42;
|
||||
@@ -769,7 +764,7 @@ TEST(TestIntraProcessManager, subscription_creation_race_condition) {
|
||||
new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg)
|
||||
);
|
||||
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
auto s1 = std::make_shared<rclcpp::mock::SubscriptionBase>();
|
||||
@@ -808,7 +803,7 @@ TEST(TestIntraProcessManager, publisher_out_of_scope_take) {
|
||||
p1->mock_topic_name = "nominal1";
|
||||
p1->mock_queue_size = 2;
|
||||
|
||||
p1_id = ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
|
||||
p1_id = ipm.add_publisher(p1);
|
||||
|
||||
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
|
||||
ipm_msg->message_sequence = 42;
|
||||
@@ -817,7 +812,7 @@ TEST(TestIntraProcessManager, publisher_out_of_scope_take) {
|
||||
new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg)
|
||||
);
|
||||
|
||||
p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
|
||||
p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
|
||||
// Explicitly remove publisher from ipm (emulate's publisher's destructor).
|
||||
@@ -847,7 +842,7 @@ TEST(TestIntraProcessManager, publisher_out_of_scope_store) {
|
||||
p1->mock_topic_name = "nominal1";
|
||||
p1->mock_queue_size = 2;
|
||||
|
||||
p1_id = ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
|
||||
p1_id = ipm.add_publisher(p1);
|
||||
}
|
||||
|
||||
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
|
||||
@@ -857,6 +852,6 @@ TEST(TestIntraProcessManager, publisher_out_of_scope_store) {
|
||||
new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg)
|
||||
);
|
||||
|
||||
EXPECT_THROW(ipm.store_intra_process_message(p1_id, unique_msg), std::runtime_error);
|
||||
EXPECT_THROW(ipm.store_intra_process_message(p1_id, std::move(unique_msg)), std::runtime_error);
|
||||
ASSERT_EQ(nullptr, unique_msg);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
TEST(test_local_parameters, set_parameter_if_not_set) {
|
||||
auto node = rclcpp::Node::make_shared("test_local_parameters_set_parameter_if_not_set");
|
||||
auto node = rclcpp::Node::make_shared(
|
||||
"test_local_parameters_set_parameter_if_not_set",
|
||||
rclcpp::NodeOptions().allow_undeclared_parameters(true));
|
||||
|
||||
{
|
||||
// try to set a map of parameters
|
||||
@@ -29,7 +31,19 @@ TEST(test_local_parameters, set_parameter_if_not_set) {
|
||||
{"x", 0.5},
|
||||
{"y", 1.0},
|
||||
};
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
node->set_parameters_if_not_set("bar", bar_map);
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
double bar_x_value;
|
||||
ASSERT_TRUE(node->get_parameter("bar.x", bar_x_value));
|
||||
EXPECT_EQ(bar_x_value, 0.5);
|
||||
@@ -51,8 +65,20 @@ TEST(test_local_parameters, set_parameter_if_not_set) {
|
||||
|
||||
{
|
||||
// set parameters for a map with different types, then try to get them back as a map
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
node->set_parameter_if_not_set("baz.x", 1.0);
|
||||
node->set_parameter_if_not_set("baz.y", "hello");
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
std::map<std::string, double> baz_map;
|
||||
EXPECT_THROW(node->get_parameters("baz", baz_map), rclcpp::ParameterTypeException);
|
||||
}
|
||||
|
||||
@@ -12,12 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#define RCLCPP_BUILDING_LIBRARY 1 // Prevent including unavailable symbols
|
||||
#include <rclcpp/mapped_ring_buffer.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include "rclcpp/mapped_ring_buffer.hpp"
|
||||
|
||||
/*
|
||||
Tests get_copy and pop on an empty mrb.
|
||||
@@ -28,60 +29,90 @@ TEST(TestMappedRingBuffer, empty) {
|
||||
// Getting or popping an empty buffer should result in a nullptr.
|
||||
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(1);
|
||||
|
||||
std::unique_ptr<char> actual;
|
||||
mrb.get_copy_at_key(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
std::unique_ptr<char> unique;
|
||||
mrb.get(1, unique);
|
||||
EXPECT_EQ(nullptr, unique);
|
||||
|
||||
mrb.pop_at_key(1, actual);
|
||||
mrb.pop(1, unique);
|
||||
EXPECT_EQ(nullptr, unique);
|
||||
|
||||
std::shared_ptr<const char> shared;
|
||||
mrb.get(1, shared);
|
||||
EXPECT_EQ(nullptr, shared);
|
||||
|
||||
mrb.pop(1, shared);
|
||||
EXPECT_EQ(nullptr, shared);
|
||||
}
|
||||
|
||||
/*
|
||||
Tests push_and_replace with a temporary object, and using
|
||||
get and pop methods with shared_ptr signature.
|
||||
*/
|
||||
TEST(TestMappedRingBuffer, temporary_l_value_with_shared_get_pop) {
|
||||
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
|
||||
// Pass in value with temporary object
|
||||
mrb.push_and_replace(1, std::shared_ptr<const char>(new char('a')));
|
||||
|
||||
std::shared_ptr<const char> actual;
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ('a', *actual);
|
||||
|
||||
mrb.pop(1, actual);
|
||||
EXPECT_EQ('a', *actual);
|
||||
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
}
|
||||
|
||||
/*
|
||||
Tests push_and_replace with a temporary object.
|
||||
Tests push_and_replace with a temporary object, and using
|
||||
get and pop methods with unique_ptr signature.
|
||||
*/
|
||||
TEST(TestMappedRingBuffer, temporary_l_value) {
|
||||
TEST(TestMappedRingBuffer, temporary_l_value_with_unique_get_pop) {
|
||||
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
|
||||
// Pass in value with temporary object
|
||||
mrb.push_and_replace(1, std::unique_ptr<char>(new char('a')));
|
||||
mrb.push_and_replace(1, std::shared_ptr<const char>(new char('a')));
|
||||
|
||||
std::unique_ptr<char> actual;
|
||||
mrb.get_copy_at_key(1, actual);
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ('a', *actual);
|
||||
|
||||
mrb.pop_at_key(1, actual);
|
||||
mrb.pop(1, actual);
|
||||
EXPECT_EQ('a', *actual);
|
||||
|
||||
mrb.get_copy_at_key(1, actual);
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
}
|
||||
|
||||
/*
|
||||
Tests normal usage of the mrb.
|
||||
Using shared push_and_replace, get and pop methods.
|
||||
*/
|
||||
TEST(TestMappedRingBuffer, nominal) {
|
||||
TEST(TestMappedRingBuffer, nominal_push_shared_get_pop_shared) {
|
||||
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
|
||||
std::unique_ptr<char> expected(new char('a'));
|
||||
// Store expected value's address for later comparison.
|
||||
char * expected_orig = expected.get();
|
||||
std::shared_ptr<const char> expected(new char('a'));
|
||||
|
||||
EXPECT_FALSE(mrb.push_and_replace(1, expected));
|
||||
EXPECT_EQ(2, expected.use_count());
|
||||
|
||||
std::unique_ptr<char> actual;
|
||||
mrb.get_copy_at_key(1, actual);
|
||||
std::shared_ptr<const char> actual;
|
||||
mrb.get(1, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('a', *actual);
|
||||
}
|
||||
EXPECT_NE(expected_orig, actual.get());
|
||||
EXPECT_EQ(expected, actual);
|
||||
EXPECT_EQ(3, actual.use_count());
|
||||
|
||||
mrb.pop_at_key(1, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
mrb.pop(1, actual);
|
||||
EXPECT_EQ(expected, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('a', *actual);
|
||||
}
|
||||
EXPECT_EQ(expected_orig, actual.get());
|
||||
expected.reset();
|
||||
EXPECT_TRUE(actual.unique());
|
||||
|
||||
mrb.get_copy_at_key(1, actual);
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
|
||||
expected.reset(new char('a'));
|
||||
@@ -93,16 +124,16 @@ TEST(TestMappedRingBuffer, nominal) {
|
||||
expected.reset(new char('c'));
|
||||
EXPECT_TRUE(mrb.push_and_replace(3, expected));
|
||||
|
||||
mrb.get_copy_at_key(1, actual);
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
|
||||
mrb.get_copy_at_key(2, actual);
|
||||
mrb.get(2, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('b', *actual);
|
||||
}
|
||||
|
||||
mrb.get_copy_at_key(3, actual);
|
||||
mrb.get(3, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('c', *actual);
|
||||
@@ -110,40 +141,167 @@ TEST(TestMappedRingBuffer, nominal) {
|
||||
}
|
||||
|
||||
/*
|
||||
Tests get_ownership on a normal mrb.
|
||||
Tests normal usage of the mrb.
|
||||
Using shared push_and_replace, unique get and pop methods.
|
||||
*/
|
||||
TEST(TestMappedRingBuffer, get_ownership) {
|
||||
TEST(TestMappedRingBuffer, nominal_push_shared_get_pop_unique) {
|
||||
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
|
||||
std::unique_ptr<char> expected(new char('a'));
|
||||
// Store expected value's address for later comparison.
|
||||
char * expected_orig = expected.get();
|
||||
std::shared_ptr<const char> expected(new char('a'));
|
||||
const char * expected_orig = expected.get();
|
||||
|
||||
EXPECT_FALSE(mrb.push_and_replace(1, expected));
|
||||
EXPECT_EQ(2, expected.use_count());
|
||||
|
||||
std::unique_ptr<char> actual;
|
||||
mrb.get_copy_at_key(1, actual);
|
||||
mrb.get(1, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('a', *actual);
|
||||
}
|
||||
EXPECT_NE(expected_orig, actual.get());
|
||||
mrb.pop(1, actual);
|
||||
EXPECT_NE(expected_orig, actual.get());
|
||||
if (actual) {
|
||||
EXPECT_EQ('a', *actual);
|
||||
}
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
|
||||
mrb.get_ownership_at_key(1, actual);
|
||||
EXPECT_FALSE(mrb.push_and_replace(1, expected));
|
||||
expected.reset();
|
||||
mrb.pop(1, actual);
|
||||
EXPECT_NE(expected_orig, actual.get());
|
||||
if (actual) {
|
||||
EXPECT_EQ('a', *actual);
|
||||
}
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
|
||||
expected.reset(new char('a'));
|
||||
EXPECT_FALSE(mrb.push_and_replace(1, expected));
|
||||
|
||||
expected.reset(new char('b'));
|
||||
EXPECT_FALSE(mrb.push_and_replace(2, expected));
|
||||
|
||||
expected.reset(new char('c'));
|
||||
EXPECT_TRUE(mrb.push_and_replace(3, expected));
|
||||
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
|
||||
mrb.get(2, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('b', *actual);
|
||||
}
|
||||
|
||||
mrb.get(3, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('c', *actual);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests normal usage of the mrb.
|
||||
Using unique push_and_replace, get and pop methods.
|
||||
*/
|
||||
TEST(TestMappedRingBuffer, nominal_push_unique_get_pop_unique) {
|
||||
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
|
||||
std::unique_ptr<char> expected(new char('a'));
|
||||
const char * expected_orig = expected.get();
|
||||
|
||||
EXPECT_FALSE(mrb.push_and_replace(1, std::move(expected)));
|
||||
|
||||
std::unique_ptr<char> actual;
|
||||
mrb.get(1, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('a', *actual);
|
||||
}
|
||||
EXPECT_NE(expected_orig, actual.get());
|
||||
mrb.pop(1, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('a', *actual);
|
||||
}
|
||||
EXPECT_EQ(expected_orig, actual.get());
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
|
||||
expected.reset(new char('a'));
|
||||
EXPECT_FALSE(mrb.push_and_replace(1, std::move(expected)));
|
||||
|
||||
expected.reset(new char('b'));
|
||||
EXPECT_FALSE(mrb.push_and_replace(2, std::move(expected)));
|
||||
|
||||
expected.reset(new char('c'));
|
||||
EXPECT_TRUE(mrb.push_and_replace(3, std::move(expected)));
|
||||
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
|
||||
mrb.get(2, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('b', *actual);
|
||||
}
|
||||
|
||||
mrb.get(3, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('c', *actual);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests normal usage of the mrb.
|
||||
Using unique push_and_replace, shared get and pop methods.
|
||||
*/
|
||||
TEST(TestMappedRingBuffer, nominal_push_unique_get_pop_shared) {
|
||||
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
|
||||
std::unique_ptr<char> expected(new char('a'));
|
||||
const char * expected_orig = expected.get();
|
||||
|
||||
EXPECT_FALSE(mrb.push_and_replace(1, std::move(expected)));
|
||||
|
||||
std::shared_ptr<const char> actual;
|
||||
mrb.get(1, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('a', *actual);
|
||||
}
|
||||
EXPECT_EQ(expected_orig, actual.get());
|
||||
mrb.pop(1, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('a', *actual);
|
||||
}
|
||||
EXPECT_EQ(expected_orig, actual.get());
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
|
||||
mrb.pop_at_key(1, actual);
|
||||
expected.reset(new char('a'));
|
||||
EXPECT_FALSE(mrb.push_and_replace(1, std::move(expected)));
|
||||
|
||||
expected.reset(new char('b'));
|
||||
EXPECT_FALSE(mrb.push_and_replace(2, std::move(expected)));
|
||||
|
||||
expected.reset(new char('c'));
|
||||
EXPECT_TRUE(mrb.push_and_replace(3, std::move(expected)));
|
||||
|
||||
mrb.get(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
|
||||
mrb.get(2, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('a', *actual); // The value should be the same.
|
||||
EXPECT_EQ('b', *actual);
|
||||
}
|
||||
EXPECT_NE(expected_orig, actual.get()); // Even though we pop'ed, we didn't get the original.
|
||||
|
||||
mrb.get_copy_at_key(1, actual);
|
||||
EXPECT_EQ(nullptr, actual);
|
||||
mrb.get(3, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('c', *actual);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -152,22 +310,23 @@ TEST(TestMappedRingBuffer, get_ownership) {
|
||||
TEST(TestMappedRingBuffer, non_unique_keys) {
|
||||
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
|
||||
|
||||
std::unique_ptr<char> input(new char('a'));
|
||||
std::shared_ptr<const char> input(new char('a'));
|
||||
mrb.push_and_replace(1, input);
|
||||
input.reset(new char('b'));
|
||||
|
||||
// Different value, same key.
|
||||
mrb.push_and_replace(1, input);
|
||||
input.reset();
|
||||
|
||||
std::unique_ptr<char> actual;
|
||||
mrb.pop_at_key(1, actual);
|
||||
mrb.pop(1, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('a', *actual);
|
||||
}
|
||||
|
||||
actual = nullptr;
|
||||
mrb.pop_at_key(1, actual);
|
||||
mrb.pop(1, actual);
|
||||
EXPECT_NE(nullptr, actual);
|
||||
if (actual) {
|
||||
EXPECT_EQ('b', *actual);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright 2018 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
class TestNodeWithInitialValues : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, NULL);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
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 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 = {
|
||||
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 list_params_result = node->list_parameters({}, 0);
|
||||
EXPECT_EQ(3u, list_params_result.names.size());
|
||||
EXPECT_TRUE(node->get_parameter("foo").get_value<bool>());
|
||||
EXPECT_STREQ("hello world", node->get_parameter("bar").get_value<std::string>().c_str());
|
||||
std::vector<double> double_array = node->get_parameter("baz").get_value<std::vector<double>>();
|
||||
ASSERT_EQ(2u, double_array.size());
|
||||
EXPECT_DOUBLE_EQ(3.14, double_array.at(0));
|
||||
EXPECT_DOUBLE_EQ(2.718, double_array.at(1));
|
||||
}
|
||||
@@ -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,18 +43,171 @@ 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;
|
||||
};
|
||||
|
||||
static constexpr rmw_qos_profile_t invalid_qos_profile()
|
||||
{
|
||||
rmw_qos_profile_t profile = rmw_qos_profile_default;
|
||||
profile.depth = 1;
|
||||
profile.durability = RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL;
|
||||
return profile;
|
||||
}
|
||||
|
||||
/*
|
||||
Testing publisher construction and destruction.
|
||||
*/
|
||||
TEST_F(TestPublisher, construction_and_destruction) {
|
||||
initialize();
|
||||
using rcl_interfaces::msg::IntraProcessMessage;
|
||||
{
|
||||
auto publisher = node->create_publisher<IntraProcessMessage>("topic");
|
||||
auto publisher = node->create_publisher<IntraProcessMessage>("topic", 42);
|
||||
(void)publisher;
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto publisher = node->create_publisher<IntraProcessMessage>("invalid_topic?");
|
||||
auto publisher = node->create_publisher<IntraProcessMessage>("invalid_topic?", 42);
|
||||
}, rclcpp::exceptions::InvalidTopicNameError);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing publisher creation signatures.
|
||||
*/
|
||||
TEST_F(TestPublisher, various_creation_signatures) {
|
||||
initialize();
|
||||
using rcl_interfaces::msg::IntraProcessMessage;
|
||||
{
|
||||
auto publisher = node->create_publisher<IntraProcessMessage>("topic", 42);
|
||||
(void)publisher;
|
||||
}
|
||||
{
|
||||
auto publisher = node->create_publisher<IntraProcessMessage>("topic", rclcpp::QoS(42));
|
||||
(void)publisher;
|
||||
}
|
||||
{
|
||||
auto publisher =
|
||||
node->create_publisher<IntraProcessMessage>("topic", rclcpp::QoS(rclcpp::KeepLast(42)));
|
||||
(void)publisher;
|
||||
}
|
||||
{
|
||||
auto publisher =
|
||||
node->create_publisher<IntraProcessMessage>("topic", rclcpp::QoS(rclcpp::KeepAll()));
|
||||
(void)publisher;
|
||||
}
|
||||
{
|
||||
auto publisher =
|
||||
node->create_publisher<IntraProcessMessage>("topic", 42, rclcpp::PublisherOptions());
|
||||
(void)publisher;
|
||||
}
|
||||
{
|
||||
auto publisher =
|
||||
rclcpp::create_publisher<IntraProcessMessage>(node, "topic", 42, rclcpp::PublisherOptions());
|
||||
(void)publisher;
|
||||
}
|
||||
// Now deprecated functions.
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
{
|
||||
auto publisher = node->create_publisher<IntraProcessMessage>("topic");
|
||||
(void)publisher;
|
||||
}
|
||||
{
|
||||
auto publisher = node->create_publisher<IntraProcessMessage>(
|
||||
"topic",
|
||||
42,
|
||||
std::make_shared<std::allocator<IntraProcessMessage>>());
|
||||
(void)publisher;
|
||||
}
|
||||
{
|
||||
auto publisher = node->create_publisher<IntraProcessMessage>("topic", rmw_qos_profile_default);
|
||||
(void)publisher;
|
||||
}
|
||||
{
|
||||
auto publisher = node->create_publisher<IntraProcessMessage>(
|
||||
"topic",
|
||||
rmw_qos_profile_default,
|
||||
std::make_shared<std::allocator<IntraProcessMessage>>());
|
||||
(void)publisher;
|
||||
}
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
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 = invalid_qos_profile();
|
||||
using rcl_interfaces::msg::IntraProcessMessage;
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_THROW(
|
||||
{auto publisher = node->create_publisher<IntraProcessMessage>("topic", qos);},
|
||||
rclcpp::exceptions::InvalidParametersException);
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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", 42);
|
||||
|
||||
EXPECT_STREQ(publisher->get_topic_name(), "/ns/sub_ns/topic");
|
||||
}
|
||||
|
||||
{
|
||||
auto publisher = subnode->create_publisher<IntraProcessMessage>("/topic", 42);
|
||||
|
||||
EXPECT_STREQ(publisher->get_topic_name(), "/topic");
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto publisher = subnode->create_publisher<IntraProcessMessage>("invalid_topic?", 42);
|
||||
}, 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", 10);
|
||||
|
||||
EXPECT_EQ(publisher->get_subscription_count(), 0u);
|
||||
EXPECT_EQ(publisher->get_intra_process_subscription_count(), 0u);
|
||||
{
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("/topic", 10, &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", 10, &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());
|
||||
@@ -56,8 +56,8 @@ TEST(TestSerializedMessageAllocator, borrow_from_subscription) {
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("test_serialized_message_allocator_node");
|
||||
std::shared_ptr<rclcpp::SubscriptionBase> sub =
|
||||
node->create_subscription<test_msgs::msg::Empty>("~/dummy_topic", [](
|
||||
std::shared_ptr<test_msgs::msg::Empty> test_msg) {(void) test_msg;});
|
||||
node->create_subscription<test_msgs::msg::Empty>("~/dummy_topic", 10,
|
||||
[](std::shared_ptr<test_msgs::msg::Empty> test_msg) {(void) test_msg;});
|
||||
|
||||
auto msg0 = sub->create_serialized_message();
|
||||
EXPECT_EQ(0u, msg0->buffer_capacity);
|
||||
|
||||
@@ -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:
|
||||
@@ -67,7 +95,7 @@ public:
|
||||
auto callback = std::bind(
|
||||
&SubscriptionClassNodeInheritance::OnMessage, this, std::placeholders::_1);
|
||||
using rcl_interfaces::msg::IntraProcessMessage;
|
||||
auto sub = this->create_subscription<IntraProcessMessage>("topic", callback);
|
||||
auto sub = this->create_subscription<IntraProcessMessage>("topic", 10, callback);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -84,7 +112,7 @@ public:
|
||||
auto node = std::make_shared<rclcpp::Node>("test_subscription_member_callback", "/ns");
|
||||
auto callback = std::bind(&SubscriptionClass::OnMessage, this, std::placeholders::_1);
|
||||
using rcl_interfaces::msg::IntraProcessMessage;
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("topic", callback);
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("topic", 10, callback);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -97,16 +125,118 @@ TEST_F(TestSubscription, construction_and_destruction) {
|
||||
(void)msg;
|
||||
};
|
||||
{
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("topic", callback);
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("topic", 10, callback);
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("invalid_topic?", callback);
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("invalid_topic?", 10, callback);
|
||||
}, rclcpp::exceptions::InvalidTopicNameError);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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", 1, callback);
|
||||
EXPECT_STREQ(sub->get_topic_name(), "/ns/sub_ns/topic");
|
||||
}
|
||||
|
||||
{
|
||||
auto sub = subnode->create_subscription<IntraProcessMessage>("/topic", 1, callback);
|
||||
EXPECT_STREQ(sub->get_topic_name(), "/topic");
|
||||
}
|
||||
|
||||
{
|
||||
auto sub = subnode->create_subscription<IntraProcessMessage>("~/topic", 1, 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?", 1, callback);
|
||||
}, rclcpp::exceptions::InvalidTopicNameError);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing subscription creation signatures.
|
||||
*/
|
||||
TEST_F(TestSubscription, various_creation_signatures) {
|
||||
using rcl_interfaces::msg::IntraProcessMessage;
|
||||
auto cb = [](rcl_interfaces::msg::IntraProcessMessage::SharedPtr) {};
|
||||
{
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("topic", 1, cb);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("topic", rclcpp::QoS(1), cb);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub =
|
||||
node->create_subscription<IntraProcessMessage>("topic", rclcpp::QoS(rclcpp::KeepLast(1)), cb);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub =
|
||||
node->create_subscription<IntraProcessMessage>("topic", rclcpp::QoS(rclcpp::KeepAll()), cb);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub = node->create_subscription<IntraProcessMessage>(
|
||||
"topic", 42, cb, rclcpp::SubscriptionOptions());
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub = rclcpp::create_subscription<IntraProcessMessage>(
|
||||
node, "topic", 42, cb, rclcpp::SubscriptionOptions());
|
||||
(void)sub;
|
||||
}
|
||||
// Now deprecated functions.
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
{
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("topic", cb, 42);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("topic", cb);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("topic", cb, rmw_qos_profile_default);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub =
|
||||
node->create_subscription<IntraProcessMessage>("topic", cb, rmw_qos_profile_default, nullptr);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("topic", cb, 42, nullptr);
|
||||
(void)sub;
|
||||
}
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
Testing subscriptions using std::bind.
|
||||
*/
|
||||
@@ -127,6 +257,6 @@ TEST_F(TestSubscription, callback_bind) {
|
||||
// Regression test for https://github.com/ros2/rclcpp/issues/479 where the TEST_F GTest macro
|
||||
// was interfering with rclcpp's `function_traits`.
|
||||
auto callback = std::bind(&TestSubscription::OnMessage, this, std::placeholders::_1);
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("topic", callback);
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("topic", 1, callback);
|
||||
}
|
||||
}
|
||||
|
||||
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", 10, &OnMessage);
|
||||
|
||||
EXPECT_EQ(subscription->get_publisher_count(), 0u);
|
||||
{
|
||||
auto pub = node->create_publisher<IntraProcessMessage>("/topic", 10);
|
||||
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", 10);
|
||||
|
||||
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());
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user