From 411e74863216af3414e649f3cb2df8f213f76d42 Mon Sep 17 00:00:00 2001 From: Shane Loretz Date: Mon, 10 Jun 2019 16:37:55 -0700 Subject: [PATCH] Add rclcpp::create_timer() (#757) * Add rclcpp::create_timer() Signed-off-by: Shane Loretz * Friendly overload with node-like object Signed-off-by: Shane Loretz * forward Signed-off-by: Shane Loretz * Make sure test with NodeWrapper compiles Signed-off-by: Shane Loretz --- rclcpp/CMakeLists.txt | 12 ++ rclcpp/include/rclcpp/create_timer.hpp | 73 +++++++++ .../get_node_base_interface.hpp | 147 ++++++++++++++++++ .../get_node_timers_interface.hpp | 147 ++++++++++++++++++ rclcpp/test/test_create_timer.cpp | 63 ++++++++ 5 files changed, 442 insertions(+) create mode 100644 rclcpp/include/rclcpp/create_timer.hpp create mode 100644 rclcpp/include/rclcpp/node_interfaces/get_node_base_interface.hpp create mode 100644 rclcpp/include/rclcpp/node_interfaces/get_node_timers_interface.hpp create mode 100644 rclcpp/test/test_create_timer.cpp diff --git a/rclcpp/CMakeLists.txt b/rclcpp/CMakeLists.txt index c218494..32b7664 100644 --- a/rclcpp/CMakeLists.txt +++ b/rclcpp/CMakeLists.txt @@ -155,6 +155,18 @@ if(BUILD_TESTING) ) target_link_libraries(test_client ${PROJECT_NAME}) endif() + ament_add_gtest(test_create_timer test/test_create_timer.cpp) + if(TARGET test_create_timer) + ament_target_dependencies(test_create_timer + "rcl_interfaces" + "rmw" + "rcl" + "rosidl_generator_cpp" + "rosidl_typesupport_cpp" + ) + target_link_libraries(test_create_timer ${PROJECT_NAME}) + target_include_directories(test_create_timer PRIVATE test/) + 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) ament_target_dependencies(test_expand_topic_or_service_name diff --git a/rclcpp/include/rclcpp/create_timer.hpp b/rclcpp/include/rclcpp/create_timer.hpp new file mode 100644 index 0000000..442dcad --- /dev/null +++ b/rclcpp/include/rclcpp/create_timer.hpp @@ -0,0 +1,73 @@ +// 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__CREATE_TIMER_HPP_ +#define RCLCPP__CREATE_TIMER_HPP_ + +#include +#include +#include + +#include "rclcpp/duration.hpp" +# include "rclcpp/node_interfaces/get_node_base_interface.hpp" +# include "rclcpp/node_interfaces/get_node_timers_interface.hpp" +#include "rclcpp/node_interfaces/node_base_interface.hpp" +#include "rclcpp/node_interfaces/node_timers_interface.hpp" + +namespace rclcpp +{ +/// Create a timer with a given clock +/// \internal +template +typename rclcpp::TimerBase::SharedPtr +create_timer( + node_interfaces::NodeBaseInterface * node_base, + node_interfaces::NodeTimersInterface * node_timers, + rclcpp::Clock::SharedPtr clock, + rclcpp::Duration period, + CallbackT && callback, + rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr) +{ + auto timer = rclcpp::GenericTimer::make_shared( + clock, + period.to_chrono(), + std::forward(callback), + node_base->get_context()); + + node_timers->add_timer(timer, group); + return timer; +} + +/// Create a timer with a given clock +template +typename rclcpp::TimerBase::SharedPtr +create_timer( + NodeT node, + rclcpp::Clock::SharedPtr clock, + rclcpp::Duration period, + CallbackT && callback, + rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr) +{ + return create_timer( + rclcpp::node_interfaces::get_node_base_interface(node), + rclcpp::node_interfaces::get_node_timers_interface(node), + clock, + period, + std::forward(callback), + group); +} + +} // namespace rclcpp + +#endif // RCLCPP__CREATE_TIMER_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/get_node_base_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/get_node_base_interface.hpp new file mode 100644 index 0000000..eee825a --- /dev/null +++ b/rclcpp/include/rclcpp/node_interfaces/get_node_base_interface.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_BASE_INTERFACE_HPP_ +#define RCLCPP__NODE_INTERFACES__GET_NODE_BASE_INTERFACE_HPP_ + +#include +#include +#include + +#include "rclcpp/node_interfaces/node_base_interface.hpp" + +/// This header provides the get_node_base_interface() template function. +/** + * This function is useful for getting the NodeBaseInterface pointer from + * various kinds of Node-like classes. + * + * It's able to get the NodeBaseInterface pointer so long as the class + * has a method called ``get_node_base_interface()`` which returns + * either a pointer (const or not) to a NodeBaseInterface or a + * std::shared_ptr to a NodeBaseInterface. + */ + +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_base_interface() which returns various types, +// e.g. const pointer or a shared pointer. +template +struct has_get_node_base_interface +{ +private: + template + static constexpr + auto + check(T *)->typename std::is_same< + decltype(std::declval().get_node_base_interface()), + ReturnType + >::type; + + template + static constexpr + std::false_type + check(...); + +public: + using type = decltype(check(nullptr)); + static constexpr bool value = type::value; +}; + +// If NodeType is a pointer to NodeBaseInterface already (just normal function overload). +inline +rclcpp::node_interfaces::NodeBaseInterface * +get_node_base_interface_from_pointer(rclcpp::node_interfaces::NodeBaseInterface * pointer) +{ + return pointer; +} + +// If NodeType has a method called get_node_base_interface() which returns a shared pointer. +template< + typename NodeType, + typename std::enable_if::type, + std::shared_ptr + >::value, int>::type = 0 +> +rclcpp::node_interfaces::NodeBaseInterface * +get_node_base_interface_from_pointer(NodeType node_pointer) +{ + return node_pointer->get_node_base_interface().get(); +} + +// If NodeType has a method called get_node_base_interface() which returns a pointer. +template< + typename NodeType, + typename std::enable_if::type, + rclcpp::node_interfaces::NodeBaseInterface * + >::value, int>::type = 0 +> +rclcpp::node_interfaces::NodeBaseInterface * +get_node_base_interface_from_pointer(NodeType node_pointer) +{ + return node_pointer->get_node_base_interface(); +} + +// Forward shared_ptr's to const node pointer signatures. +template< + typename NodeType, + typename std::enable_if::type::element_type> * + >::value, int>::type = 0 +> +rclcpp::node_interfaces::NodeBaseInterface * +get_node_base_interface_from_pointer(NodeType node_shared_pointer) +{ + return get_node_base_interface_from_pointer(node_shared_pointer->get()); +} + +} // namespace detail + +/// Get the NodeBaseInterface as a pointer from a pointer to a "Node like" object. +template< + typename NodeType, + typename std::enable_if::value, int>::type = 0 +> +rclcpp::node_interfaces::NodeBaseInterface * +get_node_base_interface(NodeType && node_pointer) +{ + // Forward pointers to detail implmentation directly. + return detail::get_node_base_interface_from_pointer(std::forward(node_pointer)); +} + +/// Get the NodeBaseInterface as a pointer from a "Node like" object. +template< + typename NodeType, + typename std::enable_if::value, int>::type = 0 +> +rclcpp::node_interfaces::NodeBaseInterface * +get_node_base_interface(NodeType && node_reference) +{ + // Forward references to detail implmentation as a pointer. + return detail::get_node_base_interface_from_pointer(&node_reference); +} + +} // namespace node_interfaces +} // namespace rclcpp + +#endif // RCLCPP__NODE_INTERFACES__GET_NODE_BASE_INTERFACE_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/get_node_timers_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/get_node_timers_interface.hpp new file mode 100644 index 0000000..54282a8 --- /dev/null +++ b/rclcpp/include/rclcpp/node_interfaces/get_node_timers_interface.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_TIMERS_INTERFACE_HPP_ +#define RCLCPP__NODE_INTERFACES__GET_NODE_TIMERS_INTERFACE_HPP_ + +#include +#include +#include + +#include "rclcpp/node_interfaces/node_timers_interface.hpp" + +/// This header provides the get_node_timers_interface() template function. +/** + * This function is useful for getting the NodeTimersInterface pointer from + * various kinds of Node-like classes. + * + * It's able to get the NodeTimersInterface pointer so long as the class + * has a method called ``get_node_timers_interface()`` which returns + * either a pointer (const or not) to a NodeTimersInterface or a + * std::shared_ptr to a NodeTimersInterface. + */ + +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_timers_interface() which returns various types, +// e.g. const pointer or a shared pointer. +template +struct has_get_node_timers_interface +{ +private: + template + static constexpr + auto + check(T *)->typename std::is_same< + decltype(std::declval().get_node_timers_interface()), + ReturnType + >::type; + + template + static constexpr + std::false_type + check(...); + +public: + using type = decltype(check(nullptr)); + static constexpr bool value = type::value; +}; + +// If NodeType is a pointer to NodeTimersInterface already (just normal function overload). +inline +rclcpp::node_interfaces::NodeTimersInterface * +get_node_timers_interface_from_pointer(rclcpp::node_interfaces::NodeTimersInterface * pointer) +{ + return pointer; +} + +// If NodeType has a method called get_node_timers_interface() which returns a shared pointer. +template< + typename NodeType, + typename std::enable_if::type, + std::shared_ptr + >::value, int>::type = 0 +> +rclcpp::node_interfaces::NodeTimersInterface * +get_node_timers_interface_from_pointer(NodeType node_pointer) +{ + return node_pointer->get_node_timers_interface().get(); +} + +// If NodeType has a method called get_node_timers_interface() which returns a pointer. +template< + typename NodeType, + typename std::enable_if::type, + rclcpp::node_interfaces::NodeTimersInterface * + >::value, int>::type = 0 +> +rclcpp::node_interfaces::NodeTimersInterface * +get_node_timers_interface_from_pointer(NodeType node_pointer) +{ + return node_pointer->get_node_timers_interface(); +} + +// Forward shared_ptr's to const node pointer signatures. +template< + typename NodeType, + typename std::enable_if::type::element_type> * + >::value, int>::type = 0 +> +rclcpp::node_interfaces::NodeTimersInterface * +get_node_timers_interface_from_pointer(NodeType node_shared_pointer) +{ + return get_node_timers_interface_from_pointer(node_shared_pointer->get()); +} + +} // namespace detail + +/// Get the NodeTimersInterface as a pointer from a pointer to a "Node like" object. +template< + typename NodeType, + typename std::enable_if::value, int>::type = 0 +> +rclcpp::node_interfaces::NodeTimersInterface * +get_node_timers_interface(NodeType && node_pointer) +{ + // Forward pointers to detail implmentation directly. + return detail::get_node_timers_interface_from_pointer(std::forward(node_pointer)); +} + +/// Get the NodeTimersInterface as a pointer from a "Node like" object. +template< + typename NodeType, + typename std::enable_if::value, int>::type = 0 +> +rclcpp::node_interfaces::NodeTimersInterface * +get_node_timers_interface(NodeType && node_reference) +{ + // Forward references to detail implmentation as a pointer. + return detail::get_node_timers_interface_from_pointer(&node_reference); +} + +} // namespace node_interfaces +} // namespace rclcpp + +#endif // RCLCPP__NODE_INTERFACES__GET_NODE_TIMERS_INTERFACE_HPP_ diff --git a/rclcpp/test/test_create_timer.cpp b/rclcpp/test/test_create_timer.cpp new file mode 100644 index 0000000..cff870a --- /dev/null +++ b/rclcpp/test/test_create_timer.cpp @@ -0,0 +1,63 @@ +// 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 + +#include +#include +#include + +#include "rclcpp/create_timer.hpp" +#include "rclcpp/executors.hpp" +#include "rclcpp/node.hpp" +#include "node_interfaces/node_wrapper.hpp" + +using namespace std::chrono_literals; + +TEST(TestCreateTimer, timer_executes) +{ + rclcpp::init(0, nullptr); + auto node = std::make_shared("test_create_timer_node"); + + std::atomic got_callback{false}; + + rclcpp::TimerBase::SharedPtr timer; + timer = rclcpp::create_timer( + node, + node->get_clock(), + rclcpp::Duration(0ms), + [&got_callback, &timer]() { + got_callback = true; + timer->cancel(); + }); + + rclcpp::spin_some(node); + + ASSERT_TRUE(got_callback); + rclcpp::shutdown(); +} + +TEST(TestCreateTimer, call_with_node_wrapper_compiles) +{ + rclcpp::init(0, nullptr); + NodeWrapper node("test_create_timer_call_with_node_wrapper_compiles"); + + rclcpp::TimerBase::SharedPtr timer; + timer = rclcpp::create_timer( + node, + node.get_node_clock_interface()->get_clock(), + rclcpp::Duration(0ms), + []() {}); + rclcpp::shutdown(); +}