ROS 2 rclcpp + rcl - kilted  kilted
ROS 2 C++ Client Library with ROS Client Library
create_timer.hpp
1 // Copyright 2019 Open Source Robotics Foundation, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef RCLCPP__CREATE_TIMER_HPP_
16 #define RCLCPP__CREATE_TIMER_HPP_
17 
18 #include <chrono>
19 #include <exception>
20 #include <memory>
21 #include <string>
22 #include <utility>
23 
24 #include "rclcpp/duration.hpp"
25 #include "rclcpp/node_interfaces/get_node_base_interface.hpp"
26 #include "rclcpp/node_interfaces/get_node_clock_interface.hpp"
27 #include "rclcpp/node_interfaces/get_node_timers_interface.hpp"
28 #include "rclcpp/node_interfaces/node_base_interface.hpp"
29 #include "rclcpp/node_interfaces/node_clock_interface.hpp"
30 #include "rclcpp/node_interfaces/node_timers_interface.hpp"
31 
32 namespace rclcpp
33 {
34 namespace detail
35 {
37 
45 template<typename DurationRepT, typename DurationT>
46 std::chrono::nanoseconds
47 safe_cast_to_period_in_ns(std::chrono::duration<DurationRepT, DurationT> period)
48 {
49  if (period < std::chrono::duration<DurationRepT, DurationT>::zero()) {
50  throw std::invalid_argument{"timer period cannot be negative"};
51  }
52 
53  // Casting to a double representation might lose precision and allow the check below to succeed
54  // but the actual cast to nanoseconds fail. Using 1 DurationT worth of nanoseconds less than max.
55  constexpr auto maximum_safe_cast_ns =
56  std::chrono::nanoseconds::max() - std::chrono::duration<DurationRepT, DurationT>(1);
57 
58  // If period is greater than nanoseconds::max(), the duration_cast to nanoseconds will overflow
59  // a signed integer, which is undefined behavior. Checking whether any std::chrono::duration is
60  // greater than nanoseconds::max() is a difficult general problem. This is a more conservative
61  // version of Howard Hinnant's (the <chrono> guy>) response here:
62  // https://stackoverflow.com/a/44637334/2089061
63  // However, this doesn't solve the issue for all possible duration types of period.
64  // Follow-up issue: https://github.com/ros2/rclcpp/issues/1177
65  constexpr auto ns_max_as_double =
66  std::chrono::duration_cast<std::chrono::duration<double, std::chrono::nanoseconds::period>>(
67  maximum_safe_cast_ns);
68  if (period > ns_max_as_double) {
69  throw std::invalid_argument{
70  "timer period must be less than std::chrono::nanoseconds::max()"};
71  }
72 
73  const auto period_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(period);
74  if (period_ns < std::chrono::nanoseconds::zero()) {
75  throw std::runtime_error{
76  "Casting timer period to nanoseconds resulted in integer overflow."};
77  }
78 
79  return period_ns;
80 }
81 } // namespace detail
82 
85 template<typename CallbackT>
86 typename rclcpp::TimerBase::SharedPtr
88  std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
89  std::shared_ptr<node_interfaces::NodeTimersInterface> node_timers,
90  rclcpp::Clock::SharedPtr clock,
91  rclcpp::Duration period,
92  CallbackT && callback,
93  rclcpp::CallbackGroup::SharedPtr group = nullptr,
94  bool autostart = true)
95 {
96  return create_timer(
97  clock,
98  period.to_chrono<std::chrono::nanoseconds>(),
99  std::forward<CallbackT>(callback),
100  group,
101  node_base.get(),
102  node_timers.get(),
103  autostart);
104 }
105 
107 template<typename NodeT, typename CallbackT>
108 typename rclcpp::TimerBase::SharedPtr
110  NodeT node,
111  rclcpp::Clock::SharedPtr clock,
112  rclcpp::Duration period,
113  CallbackT && callback,
114  rclcpp::CallbackGroup::SharedPtr group = nullptr,
115  bool autostart = true)
116 {
117  return create_timer(
118  clock,
119  period.to_chrono<std::chrono::nanoseconds>(),
120  std::forward<CallbackT>(callback),
121  group,
122  rclcpp::node_interfaces::get_node_base_interface(node).get(),
123  rclcpp::node_interfaces::get_node_timers_interface(node).get(),
124  autostart);
125 }
126 
128 
144 template<typename DurationRepT, typename DurationT, typename CallbackT>
147  rclcpp::Clock::SharedPtr clock,
148  std::chrono::duration<DurationRepT, DurationT> period,
149  CallbackT callback,
150  rclcpp::CallbackGroup::SharedPtr group,
153  bool autostart = true)
154 {
155  if (clock == nullptr) {
156  throw std::invalid_argument{"clock cannot be null"};
157  }
158  if (node_base == nullptr) {
159  throw std::invalid_argument{"input node_base cannot be null"};
160  }
161  if (node_timers == nullptr) {
162  throw std::invalid_argument{"input node_timers cannot be null"};
163  }
164 
165  const std::chrono::nanoseconds period_ns = detail::safe_cast_to_period_in_ns(period);
166 
167  // Add a new generic timer.
169  std::move(clock), period_ns, std::move(callback), node_base->get_context(), autostart);
170  node_timers->add_timer(timer, group);
171  return timer;
172 }
173 
175 
189 template<typename DurationRepT, typename DurationT, typename CallbackT>
192  std::chrono::duration<DurationRepT, DurationT> period,
193  CallbackT callback,
194  rclcpp::CallbackGroup::SharedPtr group,
197  bool autostart = true)
198 {
199  if (node_base == nullptr) {
200  throw std::invalid_argument{"input node_base cannot be null"};
201  }
202 
203  if (node_timers == nullptr) {
204  throw std::invalid_argument{"input node_timers cannot be null"};
205  }
206 
207  const std::chrono::nanoseconds period_ns = detail::safe_cast_to_period_in_ns(period);
208 
209  // Add a new wall timer.
211  period_ns, std::move(callback), node_base->get_context(), autostart);
212  node_timers->add_timer(timer, group);
213  return timer;
214 }
215 } // namespace rclcpp
216 
217 #endif // RCLCPP__CREATE_TIMER_HPP_
DurationT to_chrono() const
Convert Duration into a std::chrono::Duration.
Definition: duration.hpp:147
Generic timer. Periodically executes a user-specified callback.
Definition: timer.hpp:226
Pure virtual interface class for the NodeBase part of the Node API.
virtual RCLCPP_PUBLIC rclcpp::Context::SharedPtr get_context()=0
Return the context of the node.
Pure virtual interface class for the NodeTimers part of the Node API.
virtual RCLCPP_PUBLIC void add_timer(rclcpp::TimerBase::SharedPtr timer, rclcpp::CallbackGroup::SharedPtr callback_group)=0
Add a timer to the node.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
rclcpp::TimerBase::SharedPtr create_timer(std::shared_ptr< node_interfaces::NodeBaseInterface > node_base, std::shared_ptr< node_interfaces::NodeTimersInterface > node_timers, rclcpp::Clock::SharedPtr clock, rclcpp::Duration period, CallbackT &&callback, rclcpp::CallbackGroup::SharedPtr group=nullptr, bool autostart=true)
rclcpp::WallTimer< CallbackT >::SharedPtr create_wall_timer(std::chrono::duration< DurationRepT, DurationT > period, CallbackT callback, rclcpp::CallbackGroup::SharedPtr group, node_interfaces::NodeBaseInterface *node_base, node_interfaces::NodeTimersInterface *node_timers, bool autostart=true)
Convenience method to create a wall timer with node resources.