ROS 2 rclcpp + rcl - jazzy  jazzy
ROS 2 C++ Client Library with ROS Client Library
time_source.cpp
1 // Copyright 2017 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 #include <memory>
16 #include <string>
17 #include <unordered_set>
18 #include <utility>
19 #include <vector>
20 
21 #include "builtin_interfaces/msg/time.hpp"
22 
23 #include "rcl/time.h"
24 
25 #include "rclcpp/clock.hpp"
26 #include "rclcpp/exceptions.hpp"
27 #include "rclcpp/logging.hpp"
28 #include "rclcpp/node.hpp"
29 #include "rclcpp/parameter_client.hpp"
30 #include "rclcpp/parameter_events_filter.hpp"
31 #include "rclcpp/time.hpp"
32 #include "rclcpp/time_source.hpp"
33 
34 namespace rclcpp
35 {
36 
37 class ClocksState final
38 {
39 public:
40  ClocksState()
41  : logger_(rclcpp::get_logger("rclcpp")),
42  last_time_msg_(std::make_shared<builtin_interfaces::msg::Time>())
43  {
44  }
45 
46  // An internal method to use in the clock callback that iterates and enables all clocks
47  void enable_ros_time()
48  {
49  if (ros_time_active_) {
50  // already enabled no-op
51  return;
52  }
53 
54  // Local storage
55  ros_time_active_ = true;
56 
57  // Update all attached clocks to zero or last recorded time
58  set_all_clocks(last_time_msg_, true);
59  }
60 
61  // An internal method to use in the clock callback that iterates and disables all clocks
62  void disable_ros_time()
63  {
64  if (!ros_time_active_) {
65  // already disabled no-op
66  return;
67  }
68 
69  // Local storage
70  ros_time_active_ = false;
71 
72  // Update all attached clocks
73  auto msg = std::make_shared<builtin_interfaces::msg::Time>();
74  set_all_clocks(msg, false);
75  }
76 
77  // Check if ROS time is active
78  bool is_ros_time_active() const
79  {
80  return ros_time_active_;
81  }
82 
83  // Attach a clock
84  void attachClock(rclcpp::Clock::SharedPtr clock)
85  {
86  {
87  std::lock_guard<std::mutex> clock_guard(clock->get_clock_mutex());
88  if (clock->get_clock_type() != RCL_ROS_TIME && ros_time_active_) {
89  throw std::invalid_argument(
90  "ros_time_active_ can't be true while clock is not of RCL_ROS_TIME type");
91  }
92  }
93  std::lock_guard<std::mutex> guard(clock_list_lock_);
94  associated_clocks_.insert(clock);
95  // Set the clock to zero unless there's a recently received message
96  set_clock(last_time_msg_, ros_time_active_, clock);
97  }
98 
99  // Detach a clock
100  void detachClock(rclcpp::Clock::SharedPtr clock)
101  {
102  std::lock_guard<std::mutex> guard(clock_list_lock_);
103  auto removed = associated_clocks_.erase(clock);
104  if (removed == 0) {
105  RCLCPP_ERROR(logger_, "failed to remove clock");
106  }
107  }
108 
109  // Internal helper function used inside iterators
110  static void set_clock(
111  const builtin_interfaces::msg::Time::SharedPtr msg,
112  bool set_ros_time_enabled,
113  rclcpp::Clock::SharedPtr clock)
114  {
115  std::lock_guard<std::mutex> clock_guard(clock->get_clock_mutex());
116 
117  if (clock->get_clock_type() == RCL_ROS_TIME) {
118  // Do change
119  if (!set_ros_time_enabled && clock->ros_time_is_active()) {
120  auto ret = rcl_disable_ros_time_override(clock->get_clock_handle());
121  if (ret != RCL_RET_OK) {
122  rclcpp::exceptions::throw_from_rcl_error(
123  ret, "Failed to disable ros_time_override_status");
124  }
125  } else if (set_ros_time_enabled && !clock->ros_time_is_active()) {
126  auto ret = rcl_enable_ros_time_override(clock->get_clock_handle());
127  if (ret != RCL_RET_OK) {
128  rclcpp::exceptions::throw_from_rcl_error(
129  ret, "Failed to enable ros_time_override_status");
130  }
131  }
132 
133  auto ret = rcl_set_ros_time_override(
134  clock->get_clock_handle(),
135  rclcpp::Time(*msg).nanoseconds());
136  if (ret != RCL_RET_OK) {
137  rclcpp::exceptions::throw_from_rcl_error(
138  ret, "Failed to set ros_time_override_status");
139  }
140  } else if (set_ros_time_enabled) {
141  throw std::invalid_argument(
142  "set_ros_time_enabled can't be true while clock is not of RCL_ROS_TIME type");
143  }
144  }
145 
146  // Internal helper function
147  void set_all_clocks(
148  const builtin_interfaces::msg::Time::SharedPtr msg,
149  bool set_ros_time_enabled)
150  {
151  std::lock_guard<std::mutex> guard(clock_list_lock_);
152  for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
153  set_clock(msg, set_ros_time_enabled, *it);
154  }
155  }
156 
157  // Cache the last clock message received
158  void cache_last_msg(std::shared_ptr<const rosgraph_msgs::msg::Clock> msg)
159  {
160  last_time_msg_ = std::make_shared<builtin_interfaces::msg::Time>(msg->clock);
161  }
162 
163  bool are_all_clocks_rcl_ros_time()
164  {
165  std::lock_guard<std::mutex> guard(clock_list_lock_);
166  for (auto & clock : associated_clocks_) {
167  std::lock_guard<std::mutex> clock_guard(clock->get_clock_mutex());
168  if (clock->get_clock_type() != RCL_ROS_TIME) {
169  return false;
170  }
171  }
172  return true;
173  }
174 
175 private:
176  // Store (and update on node attach) logger for logging.
177  Logger logger_;
178 
179  // A lock to protect iterating the associated_clocks_ field.
180  std::mutex clock_list_lock_;
181  // An unordered_set to store references to associated clocks.
182  std::unordered_set<rclcpp::Clock::SharedPtr> associated_clocks_;
183 
184  // Local storage of validity of ROS time
185  // This is needed when new clocks are added.
186  bool ros_time_active_{false};
187  // Last set message to be passed to newly registered clocks
188  std::shared_ptr<builtin_interfaces::msg::Time> last_time_msg_{nullptr};
189 };
190 
192 {
193 public:
194  NodeState(const rclcpp::QoS & qos, bool use_clock_thread)
195  : use_clock_thread_(use_clock_thread),
196  logger_(rclcpp::get_logger("rclcpp")),
197  qos_(qos)
198  {
199  }
200 
201  ~NodeState()
202  {
203  if (
204  node_base_ || node_topics_ || node_graph_ || node_services_ ||
205  node_logging_ || node_clock_ || node_parameters_)
206  {
207  detachNode();
208  }
209  }
210 
211  // Check if a clock thread will be used
212  bool get_use_clock_thread()
213  {
214  return use_clock_thread_;
215  }
216 
217  // Set whether a clock thread will be used
218  void set_use_clock_thread(bool use_clock_thread)
219  {
220  use_clock_thread_ = use_clock_thread;
221  }
222 
223  // Check if the clock thread is joinable
224  bool clock_thread_is_joinable()
225  {
226  return clock_executor_thread_.joinable();
227  }
228 
229  // Attach a node to this time source
230  void attachNode(
231  rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
232  rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
233  rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
234  rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
235  rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_interface,
236  rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface,
237  rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface)
238  {
239  std::lock_guard<std::mutex> guard(node_base_lock_);
240  node_base_ = node_base_interface;
241  node_topics_ = node_topics_interface;
242  node_graph_ = node_graph_interface;
243  node_services_ = node_services_interface;
244  node_logging_ = node_logging_interface;
245  node_clock_ = node_clock_interface;
246  node_parameters_ = node_parameters_interface;
247  // TODO(tfoote): Update QOS
248 
249  logger_ = node_logging_->get_logger();
250 
251  // Though this defaults to false, it can be overridden by initial parameter values for the
252  // node, which may be given by the user at the node's construction or even by command-line
253  // arguments.
254  rclcpp::ParameterValue use_sim_time_param;
255  const std::string use_sim_time_name = "use_sim_time";
256  if (!node_parameters_->has_parameter(use_sim_time_name)) {
257  use_sim_time_param = node_parameters_->declare_parameter(
258  use_sim_time_name,
259  rclcpp::ParameterValue(false));
260  } else {
261  use_sim_time_param = node_parameters_->get_parameter(use_sim_time_name).get_parameter_value();
262  }
263  if (use_sim_time_param.get_type() == rclcpp::PARAMETER_BOOL) {
264  if (use_sim_time_param.get<bool>()) {
265  parameter_state_ = SET_TRUE;
266  clocks_state_.enable_ros_time();
267  create_clock_sub();
268  }
269  } else {
270  RCLCPP_ERROR(
271  logger_, "Invalid type '%s' for parameter 'use_sim_time', should be 'bool'",
272  rclcpp::to_string(use_sim_time_param.get_type()).c_str());
273  throw std::invalid_argument("Invalid type for parameter 'use_sim_time', should be 'bool'");
274  }
275 
276  on_set_parameters_callback_ = node_parameters_->add_on_set_parameters_callback(
277  std::bind(&TimeSource::NodeState::on_set_parameters, this, std::placeholders::_1));
278 
279 
280  // TODO(tfoote) use parameters interface not subscribe to events via topic ticketed #609
281  parameter_subscription_ = rclcpp::AsyncParametersClient::on_parameter_event(
282  node_topics_,
283  [this](std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event) {
284  this->on_parameter_event(event);
285  });
286  }
287 
288  // Detach the attached node
289  void detachNode()
290  {
291  // destroy_clock_sub() *must* be first here, to ensure that the executor
292  // can't possibly call any of the callbacks as we are cleaning up.
293  destroy_clock_sub();
294  std::lock_guard<std::mutex> guard(node_base_lock_);
295  clocks_state_.disable_ros_time();
296  if (on_set_parameters_callback_) {
297  node_parameters_->remove_on_set_parameters_callback(on_set_parameters_callback_.get());
298  }
299  on_set_parameters_callback_.reset();
300  parameter_subscription_.reset();
301  node_base_.reset();
302  node_topics_.reset();
303  node_graph_.reset();
304  node_services_.reset();
305  node_logging_.reset();
306  node_clock_.reset();
307  node_parameters_.reset();
308  }
309 
310  void attachClock(std::shared_ptr<rclcpp::Clock> clock)
311  {
312  clocks_state_.attachClock(std::move(clock));
313  }
314 
315  void detachClock(std::shared_ptr<rclcpp::Clock> clock)
316  {
317  clocks_state_.detachClock(std::move(clock));
318  }
319 
320 private:
321  ClocksState clocks_state_;
322 
323  // Dedicated thread for clock subscription.
324  bool use_clock_thread_;
325  std::thread clock_executor_thread_;
326 
327  // Preserve the node reference
328  std::mutex node_base_lock_;
329  rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_{nullptr};
330  rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_{nullptr};
331  rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_{nullptr};
332  rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_{nullptr};
333  rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_{nullptr};
334  rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_{nullptr};
335  rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_{nullptr};
336 
337  // Store (and update on node attach) logger for logging.
338  Logger logger_;
339 
340  // QoS of the clock subscription.
341  rclcpp::QoS qos_;
342 
343  // The subscription for the clock callback
345  std::shared_ptr<SubscriptionT> clock_subscription_{nullptr};
346  std::mutex clock_sub_lock_;
347  rclcpp::CallbackGroup::SharedPtr clock_callback_group_;
348  rclcpp::executors::SingleThreadedExecutor::SharedPtr clock_executor_;
349  std::promise<void> cancel_clock_executor_promise_;
350 
351  // The clock callback itself
352  void clock_cb(std::shared_ptr<const rosgraph_msgs::msg::Clock> msg)
353  {
354  if (!clocks_state_.is_ros_time_active() && SET_TRUE == this->parameter_state_) {
355  clocks_state_.enable_ros_time();
356  }
357  // Cache the last message in case a new clock is attached.
358  clocks_state_.cache_last_msg(msg);
359  auto time_msg = std::make_shared<builtin_interfaces::msg::Time>(msg->clock);
360 
361  if (SET_TRUE == this->parameter_state_) {
362  clocks_state_.set_all_clocks(time_msg, true);
363  }
364  }
365 
366  // Create the subscription for the clock topic
367  void create_clock_sub()
368  {
369  std::lock_guard<std::mutex> guard(clock_sub_lock_);
370  if (clock_subscription_) {
371  // Subscription already created.
372  return;
373  }
374 
376  options.qos_overriding_options = rclcpp::QosOverridingOptions(
377  {
378  rclcpp::QosPolicyKind::Depth,
379  rclcpp::QosPolicyKind::Durability,
380  rclcpp::QosPolicyKind::History,
381  rclcpp::QosPolicyKind::Reliability,
382  });
383 
384  if (use_clock_thread_) {
385  clock_callback_group_ = node_base_->create_callback_group(
386  rclcpp::CallbackGroupType::MutuallyExclusive,
387  false
388  );
389  options.callback_group = clock_callback_group_;
390  rclcpp::ExecutorOptions exec_options;
391  exec_options.context = node_base_->get_context();
392  clock_executor_ =
393  std::make_shared<rclcpp::executors::SingleThreadedExecutor>(exec_options);
394  if (!clock_executor_thread_.joinable()) {
395  cancel_clock_executor_promise_ = std::promise<void>{};
396  clock_executor_thread_ = std::thread(
397  [this]() {
398  auto future = cancel_clock_executor_promise_.get_future();
399  clock_executor_->add_callback_group(clock_callback_group_, node_base_);
400  clock_executor_->spin_until_future_complete(future);
401  }
402  );
403  }
404  }
405 
406  clock_subscription_ = rclcpp::create_subscription<rosgraph_msgs::msg::Clock>(
407  node_parameters_,
408  node_topics_,
409  "/clock",
410  qos_,
411  [this](std::shared_ptr<const rosgraph_msgs::msg::Clock> msg) {
412  bool execute_cb = false;
413  {
414  std::lock_guard<std::mutex> guard(node_base_lock_);
415  // We are using node_base_ as an indication if there is a node attached.
416  // Only call the clock_cb if that is the case.
417  execute_cb = node_base_ != nullptr;
418  }
419  if (execute_cb) {
420  clock_cb(msg);
421  }
422  },
423  options
424  );
425  }
426 
427  // Destroy the subscription for the clock topic
428  void destroy_clock_sub()
429  {
430  std::lock_guard<std::mutex> guard(clock_sub_lock_);
431  if (clock_executor_thread_.joinable()) {
432  cancel_clock_executor_promise_.set_value();
433  clock_executor_->cancel();
434  clock_executor_thread_.join();
435  clock_executor_->remove_callback_group(clock_callback_group_);
436  }
437  clock_subscription_.reset();
438  }
439 
440  // On set Parameters callback handle
441  node_interfaces::OnSetParametersCallbackHandle::SharedPtr on_set_parameters_callback_{nullptr};
442 
443  // Parameter Event subscription
445  std::shared_ptr<ParamSubscriptionT> parameter_subscription_;
446 
447  // Callback for parameter settings
448  rcl_interfaces::msg::SetParametersResult on_set_parameters(
449  const std::vector<rclcpp::Parameter> & parameters)
450  {
451  rcl_interfaces::msg::SetParametersResult result;
452  result.successful = true;
453  for (const auto & param : parameters) {
454  if (param.get_name() == "use_sim_time" && param.get_type() == rclcpp::PARAMETER_BOOL) {
455  if (param.as_bool() && !(clocks_state_.are_all_clocks_rcl_ros_time())) {
456  result.successful = false;
457  result.reason =
458  "use_sim_time parameter can't be true while clocks are not all of RCL_ROS_TIME type";
459  RCLCPP_ERROR(
460  logger_,
461  "use_sim_time parameter can't be true while clocks are not all of RCL_ROS_TIME type");
462  }
463  }
464  }
465  return result;
466  }
467 
468  // Callback for parameter updates
469  void on_parameter_event(std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event)
470  {
471  std::lock_guard<std::mutex> guard(node_base_lock_);
472 
473  if (node_base_ == nullptr) {
474  // Do nothing if node_base_ is nullptr because it means the TimeSource is now
475  // without an attached node
476  return;
477  }
478 
479  // Filter out events on 'use_sim_time' parameter instances in other nodes.
480  if (event->node != node_base_->get_fully_qualified_name()) {
481  return;
482  }
483  // Filter for only 'use_sim_time' being added or changed.
484  rclcpp::ParameterEventsFilter filter(event, {"use_sim_time"},
485  {rclcpp::ParameterEventsFilter::EventType::NEW,
486  rclcpp::ParameterEventsFilter::EventType::CHANGED});
487  for (auto & it : filter.get_events()) {
488  if (it.second->value.type != ParameterType::PARAMETER_BOOL) {
489  RCLCPP_ERROR(logger_, "use_sim_time parameter cannot be set to anything but a bool");
490  continue;
491  }
492  if (it.second->value.bool_value) {
493  parameter_state_ = SET_TRUE;
494  clocks_state_.enable_ros_time();
495  create_clock_sub();
496  } else {
497  parameter_state_ = SET_FALSE;
498  destroy_clock_sub();
499  clocks_state_.disable_ros_time();
500  }
501  }
502  // Handle the case that use_sim_time was deleted.
503  rclcpp::ParameterEventsFilter deleted(event, {"use_sim_time"},
504  {rclcpp::ParameterEventsFilter::EventType::DELETED});
505  for (auto & it : deleted.get_events()) {
506  (void) it; // if there is a match it's already matched, don't bother reading it.
507  // If the parameter is deleted mark it as unset but don't change state.
508  parameter_state_ = UNSET;
509  }
510  }
511 
512  // An enum to hold the parameter state
513  enum UseSimTimeParameterState {UNSET, SET_TRUE, SET_FALSE};
514  UseSimTimeParameterState parameter_state_;
515 };
516 
518  std::shared_ptr<rclcpp::Node> node,
519  const rclcpp::QoS & qos,
520  bool use_clock_thread)
521 : TimeSource(qos, use_clock_thread)
522 {
523  attachNode(node);
524 }
525 
527  const rclcpp::QoS & qos,
528  bool use_clock_thread)
529 : constructed_use_clock_thread_(use_clock_thread),
530  constructed_qos_(qos)
531 {
532  node_state_ = std::make_shared<NodeState>(qos, use_clock_thread);
533 }
534 
535 void TimeSource::attachNode(rclcpp::Node::SharedPtr node)
536 {
537  node_state_->set_use_clock_thread(node->get_node_options().use_clock_thread());
538  attachNode(
539  node->get_node_base_interface(),
540  node->get_node_topics_interface(),
541  node->get_node_graph_interface(),
542  node->get_node_services_interface(),
543  node->get_node_logging_interface(),
544  node->get_node_clock_interface(),
545  node->get_node_parameters_interface());
546 }
547 
549  rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
550  rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
551  rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
552  rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
553  rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_interface,
554  rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface,
555  rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface)
556 {
557  node_state_->attachNode(
558  std::move(node_base_interface),
559  std::move(node_topics_interface),
560  std::move(node_graph_interface),
561  std::move(node_services_interface),
562  std::move(node_logging_interface),
563  std::move(node_clock_interface),
564  std::move(node_parameters_interface));
565 }
566 
568 {
569  node_state_.reset();
570  node_state_ = std::make_shared<NodeState>(
571  constructed_qos_,
572  constructed_use_clock_thread_);
573 }
574 
575 void TimeSource::attachClock(std::shared_ptr<rclcpp::Clock> clock)
576 {
577  node_state_->attachClock(std::move(clock));
578 }
579 
580 void TimeSource::detachClock(std::shared_ptr<rclcpp::Clock> clock)
581 {
582  node_state_->detachClock(std::move(clock));
583 }
584 
586 {
587  return node_state_->get_use_clock_thread();
588 }
589 
590 void TimeSource::set_use_clock_thread(bool use_clock_thread)
591 {
592  node_state_->set_use_clock_thread(use_clock_thread);
593 }
594 
596 {
597  return node_state_->clock_thread_is_joinable();
598 }
599 
601 {
602 }
603 
604 } // namespace rclcpp
Store the type and value of a parameter.
RCLCPP_PUBLIC ParameterType get_type() const
Return an enum indicating the type of the set value.
Encapsulation of Quality of Service settings.
Definition: qos.hpp:116
Options that are passed in subscription/publisher constructor to specify QoSConfigurability.
Subscription implementation, templated on the type of message this subscription receives.
RCLCPP_PUBLIC bool get_use_clock_thread()
Get whether a separate clock thread is used or not.
RCLCPP_PUBLIC void attachClock(rclcpp::Clock::SharedPtr clock)
Attach a clock to the time source to be updated.
RCLCPP_PUBLIC void detachClock(rclcpp::Clock::SharedPtr clock)
Detach a clock from the time source.
RCLCPP_PUBLIC void set_use_clock_thread(bool use_clock_thread)
Set whether to use a separate clock thread or not.
RCLCPP_PUBLIC TimeSource(rclcpp::Node::SharedPtr node, const rclcpp::QoS &qos=rclcpp::ClockQoS(), bool use_clock_thread=true)
Constructor.
RCLCPP_PUBLIC bool clock_thread_is_joinable()
Check if the clock thread is joinable.
RCLCPP_PUBLIC ~TimeSource()
TimeSource Destructor.
RCLCPP_PUBLIC void detachNode()
Detach the node from the time source.
RCLCPP_PUBLIC void attachNode(rclcpp::Node::SharedPtr node)
Attach node to the time source.
RCLCPP_PUBLIC rcl_time_point_value_t nanoseconds() const
Get the nanoseconds since epoch.
Definition: time.cpp:212
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC std::string to_string(const FutureReturnCode &future_return_code)
String conversion function for FutureReturnCode.
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Definition: logger.cpp:33
Options to be passed to the executor constructor.
rclcpp::CallbackGroup::SharedPtr callback_group
The callback group for this subscription. NULL to use the default callback group.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_enable_ros_time_override(rcl_clock_t *clock)
Enable the ROS time abstraction override.
Definition: time.c:299
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_disable_ros_time_override(rcl_clock_t *clock)
Disable the ROS time abstraction override.
Definition: time.c:321
@ RCL_ROS_TIME
Use ROS time.
Definition: time.h:66
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_set_ros_time_override(rcl_clock_t *clock, rcl_time_point_value_t time_value)
Set the current time for this RCL_ROS_TIME time source.
Definition: time.c:361
#define RCL_RET_OK
Success return code.
Definition: types.h:27