rclcpp_action  master
C++ ROS Action Client Library
client.hpp
Go to the documentation of this file.
1 // Copyright 2018 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_ACTION__CLIENT_HPP_
16 #define RCLCPP_ACTION__CLIENT_HPP_
17 
18 #include <rclcpp/macros.hpp>
22 #include <rclcpp/logger.hpp>
23 #include <rclcpp/time.hpp>
24 #include <rclcpp/waitable.hpp>
25 
26 #include <rosidl_generator_c/action_type_support_struct.h>
27 #include <rosidl_typesupport_cpp/action_type_support.hpp>
28 
29 #include <algorithm>
30 #include <chrono>
31 #include <functional>
32 #include <future>
33 #include <map>
34 #include <memory>
35 #include <mutex>
36 #include <string>
37 #include <utility>
38 
40 #include "rclcpp_action/types.hpp"
42 
43 
44 namespace rclcpp_action
45 {
46 // Forward declaration
47 class ClientBaseImpl;
48 
51 
58 {
59 public:
61  virtual ~ClientBase();
62 
65  bool
66  action_server_is_ready() const;
67 
69  template<typename RatioT = std::milli>
70  bool
73  {
75  std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
76  );
77  }
78 
79  // -------------
80  // Waitables API
81 
84  size_t
86 
89  size_t
90  get_number_of_ready_timers() override;
91 
94  size_t
95  get_number_of_ready_clients() override;
96 
99  size_t
100  get_number_of_ready_services() override;
101 
104  size_t
106 
109  bool
110  add_to_wait_set(rcl_wait_set_t * wait_set) override;
111 
114  bool
115  is_ready(rcl_wait_set_t * wait_set) override;
116 
119  void
120  execute() override;
121 
122  // End Waitables API
123  // -----------------
124 
125 protected:
127  ClientBase(
128  rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
129  rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
130  rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
131  const std::string & action_name,
132  const rosidl_action_type_support_t * type_support,
133  const rcl_action_client_options_t & options);
134 
137  bool
139 
140  // -----------------------------------------------------
141  // API for communication between ClientBase and Client<>
143 
147 
150  virtual
151  GoalID
153 
156  virtual
157  void
159  std::shared_ptr<void> request,
160  ResponseCallback callback);
161 
164  virtual
165  void
167  std::shared_ptr<void> request,
168  ResponseCallback callback);
169 
172  virtual
173  void
175  std::shared_ptr<void> request,
176  ResponseCallback callback);
177 
179  virtual
181  create_goal_response() const = 0;
182 
185  virtual
186  void
188  const rmw_request_id_t & response_header,
189  std::shared_ptr<void> goal_response);
190 
192  virtual
194  create_result_response() const = 0;
195 
198  virtual
199  void
201  const rmw_request_id_t & response_header,
202  std::shared_ptr<void> result_response);
203 
205  virtual
207  create_cancel_response() const = 0;
208 
211  virtual
212  void
214  const rmw_request_id_t & response_header,
215  std::shared_ptr<void> cancel_response);
216 
218  virtual
220  create_feedback_message() const = 0;
221 
223  virtual
224  void
226 
228  virtual
230  create_status_message() const = 0;
231 
233  virtual
234  void
236 
237  // End API for communication between ClientBase and Client<>
238  // ---------------------------------------------------------
239 
240 private:
242 };
243 
245 
254 template<typename ActionT>
255 class Client : public ClientBase
256 {
257 public:
259 
260  using Goal = typename ActionT::Goal;
261  using Feedback = typename ActionT::Feedback;
262  using GoalHandle = ClientGoalHandle<ActionT>;
263  using Result = typename GoalHandle::Result;
265  using CancelRequest = typename ActionT::CancelGoalService::Request;
266  using CancelResponse = typename ActionT::CancelGoalService::Response;
267 
269  rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
270  rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
271  rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
272  const std::string & action_name,
273  const rcl_action_client_options_t client_options = rcl_action_client_get_default_options()
274  )
275  : ClientBase(
276  node_base, node_graph, node_logging, action_name,
277  rosidl_typesupport_cpp::get_action_type_support_handle<ActionT>(),
278  client_options)
279  {
280  }
281 
284  const Goal & goal, FeedbackCallback callback = nullptr, bool ignore_result = false)
285  {
286  // Put promise in the heap to move it around.
287  auto promise = std::make_shared<std::promise<typename GoalHandle::SharedPtr>>();
288  std::shared_future<typename GoalHandle::SharedPtr> future(promise->get_future());
289  using GoalRequest = typename ActionT::GoalRequestService::Request;
290  // auto goal_request = std::make_shared<GoalRequest>();
291  // goal_request->goal_id = this->generate_goal_id();
292  // goal_request->goal = goal;
293  auto goal_request = std::make_shared<GoalRequest>(goal);
294  goal_request->action_goal_id.uuid = this->generate_goal_id();
295  this->send_goal_request(
296  std::static_pointer_cast<void>(goal_request),
297  [this, goal_request, callback, ignore_result, promise](
298  std::shared_ptr<void> response) mutable
299  {
300  using GoalResponse = typename ActionT::GoalRequestService::Response;
301  auto goal_response = std::static_pointer_cast<GoalResponse>(response);
302  if (!goal_response->accepted) {
303  promise->set_value(nullptr);
304  return;
305  }
306  GoalInfo goal_info;
307  // goal_info.goal_id = goal_request->goal_id;
308  goal_info.goal_id.uuid = goal_request->action_goal_id.uuid;
309  goal_info.stamp = goal_response->stamp;
310  // Do not use std::make_shared as friendship cannot be forwarded.
311  std::shared_ptr<GoalHandle> goal_handle(new GoalHandle(goal_info, callback));
312  if (!ignore_result) {
313  try {
314  this->make_result_aware(goal_handle);
315  } catch (...) {
316  promise->set_exception(std::current_exception());
317  return;
318  }
319  }
320  std::lock_guard<std::mutex> guard(goal_handles_mutex_);
321  goal_handles_[goal_handle->get_goal_id()] = goal_handle;
322  promise->set_value(goal_handle);
323  });
324  return future;
325  }
326 
328  async_get_result(typename GoalHandle::SharedPtr goal_handle)
329  {
330  std::lock_guard<std::mutex> lock(goal_handles_mutex_);
331  if (goal_handles_.count(goal_handle->get_goal_id()) == 0) {
333  }
334  // If the user chose to ignore the result before, then ask the server for the result now.
335  if (!goal_handle->is_result_aware()) {
336  this->make_result_aware(goal_handle);
337  }
338  return goal_handle->async_result();
339  }
340 
342  async_cancel_goal(typename GoalHandle::SharedPtr goal_handle)
343  {
344  std::lock_guard<std::mutex> lock(goal_handles_mutex_);
345  if (goal_handles_.count(goal_handle->get_goal_id()) == 0) {
347  }
348  // Put promise in the heap to move it around.
349  auto promise = std::make_shared<std::promise<bool>>();
350  std::shared_future<bool> future(promise->get_future());
351  auto cancel_request = std::make_shared<CancelRequest>();
352  // cancel_request->goal_info.goal_id = goal_handle->get_goal_id();
353  cancel_request->goal_info.goal_id.uuid = goal_handle->get_goal_id();
354  this->send_cancel_request(
355  std::static_pointer_cast<void>(cancel_request),
356  [goal_handle, promise](std::shared_ptr<void> response) mutable
357  {
358  auto cancel_response = std::static_pointer_cast<CancelResponse>(response);
359  bool goal_canceled = false;
360  if (!cancel_response->goals_canceling.empty()) {
361  const GoalInfo & canceled_goal_info = cancel_response->goals_canceling[0];
362  // goal_canceled = (canceled_goal_info.goal_id == goal_handle->get_goal_id());
363  goal_canceled = (canceled_goal_info.goal_id.uuid == goal_handle->get_goal_id());
364  }
365  promise->set_value(goal_canceled);
366  });
367  return future;
368  }
369 
372  {
373  auto cancel_request = std::make_shared<CancelRequest>();
374  // std::fill(cancel_request->goal_info.goal_id.uuid, 0u);
375  std::fill(
376  cancel_request->goal_info.goal_id.uuid.begin(),
377  cancel_request->goal_info.goal_id.uuid.end(), 0u);
378  return async_cancel(cancel_request);
379  }
380 
383  {
384  auto cancel_request = std::make_shared<CancelRequest>();
385  // std::fill(cancel_request->goal_info.goal_id.uuid, 0u);
386  std::fill(
387  cancel_request->goal_info.goal_id.uuid.begin(),
388  cancel_request->goal_info.goal_id.uuid.end(), 0u);
389  cancel_request->goal_info.stamp = stamp;
390  return async_cancel(cancel_request);
391  }
392 
393  virtual
395  {
396  std::lock_guard<std::mutex> guard(goal_handles_mutex_);
397  auto it = goal_handles_.begin();
398  while (it != goal_handles_.end()) {
399  it->second->invalidate();
400  it = goal_handles_.erase(it);
401  }
402  }
403 
404 private:
407  create_goal_response() const override
408  {
409  using GoalResponse = typename ActionT::GoalRequestService::Response;
410  return std::shared_ptr<void>(new GoalResponse());
411  }
412 
415  create_result_response() const override
416  {
417  using GoalResultResponse = typename ActionT::GoalResultService::Response;
418  return std::shared_ptr<void>(new GoalResultResponse());
419  }
420 
423  create_cancel_response() const override
424  {
426  }
427 
430  create_feedback_message() const override
431  {
432  // using FeedbackMessage = typename ActionT::FeedbackMessage;
433  // return std::shared_ptr<void>(new FeedbackMessage());
434  return std::shared_ptr<void>(new Feedback());
435  }
436 
438  void
440  {
441  std::lock_guard<std::mutex> guard(goal_handles_mutex_);
442  // using FeedbackMessage = typename ActionT::FeedbackMessage;
443  // typename FeedbackMessage::SharedPtr feedback_message =
444  // std::static_pointer_cast<FeedbackMessage>(message);
445  typename Feedback::SharedPtr feedback_message =
447  // const GoalID & goal_id = feedback_message->goal_id;
448  const GoalID & goal_id = feedback_message->action_goal_id.uuid;
449  if (goal_handles_.count(goal_id) == 0) {
450  RCLCPP_DEBUG(
451  this->get_logger(),
452  "Received feedback for unknown goal. Ignoring...");
453  return;
454  }
455  typename GoalHandle::SharedPtr goal_handle = goal_handles_[goal_id];
456  // goal_handle->call_feedback_callback(goal_handle, feedback_message->feedback);
457  goal_handle->call_feedback_callback(goal_handle, feedback_message);
458  }
459 
462  create_status_message() const override
463  {
464  using GoalStatusMessage = typename ActionT::GoalStatusMessage;
465  return std::shared_ptr<void>(new GoalStatusMessage());
466  }
467 
469  void
471  {
472  std::lock_guard<std::mutex> guard(goal_handles_mutex_);
473  using GoalStatusMessage = typename ActionT::GoalStatusMessage;
474  auto status_message = std::static_pointer_cast<GoalStatusMessage>(message);
475  for (const GoalStatus & status : status_message->status_list) {
476  // const GoalID & goal_id = status.goal_info.goal_id;
477  const GoalID & goal_id = status.goal_info.goal_id.uuid;
478  if (goal_handles_.count(goal_id) == 0) {
479  RCLCPP_DEBUG(
480  this->get_logger(),
481  "Received status for unknown goal. Ignoring...");
482  continue;
483  }
484  typename GoalHandle::SharedPtr goal_handle = goal_handles_[goal_id];
485  goal_handle->set_status(status.status);
486  const int8_t goal_status = goal_handle->get_status();
487  if (
488  goal_status == GoalStatus::STATUS_SUCCEEDED ||
489  goal_status == GoalStatus::STATUS_CANCELED ||
490  goal_status == GoalStatus::STATUS_ABORTED)
491  {
492  goal_handles_.erase(goal_id);
493  }
494  }
495  }
496 
498  void
499  make_result_aware(typename GoalHandle::SharedPtr goal_handle)
500  {
501  using GoalResultRequest = typename ActionT::GoalResultService::Request;
502  auto goal_result_request = std::make_shared<GoalResultRequest>();
503  // goal_result_request.goal_id = goal_handle->get_goal_id();
504  goal_result_request->action_goal_id.uuid = goal_handle->get_goal_id();
505  this->send_result_request(
506  std::static_pointer_cast<void>(goal_result_request),
507  [goal_handle, this](std::shared_ptr<void> response) mutable
508  {
509  // Wrap the response in a struct with the fields a user cares about
510  Result result;
511  using GoalResultResponse = typename ActionT::GoalResultService::Response;
512  result.response = std::static_pointer_cast<GoalResultResponse>(response);
513  result.goal_id = goal_handle->get_goal_id();
514  result.code = static_cast<ResultCode>(result.response->action_status);
515  goal_handle->set_result(result);
516  std::lock_guard<std::mutex> lock(goal_handles_mutex_);
517  goal_handles_.erase(goal_handle->get_goal_id());
518  });
519  goal_handle->set_result_awareness(true);
520  }
521 
524  async_cancel(typename CancelRequest::SharedPtr cancel_request)
525  {
526  // Put promise in the heap to move it around.
527  auto promise = std::make_shared<std::promise<typename CancelResponse::SharedPtr>>();
528  std::shared_future<typename CancelResponse::SharedPtr> future(promise->get_future());
529  this->send_cancel_request(
530  std::static_pointer_cast<void>(cancel_request),
531  [promise](std::shared_ptr<void> response) mutable
532  {
533  auto cancel_response = std::static_pointer_cast<CancelResponse>(response);
534  promise->set_value(cancel_response);
535  });
536  return future;
537  }
538 
540  std::mutex goal_handles_mutex_;
541 };
542 } // namespace rclcpp_action
543 
544 #endif // RCLCPP_ACTION__CLIENT_HPP_
virtual RCLCPP_ACTION_PUBLIC void send_result_request(std::shared_ptr< void > request, ResponseCallback callback)
Definition: client.hpp:57
typename ActionT::Goal Goal
Definition: client.hpp:260
virtual ~Client()
Definition: client.hpp:394
RCLCPP_ACTION_PUBLIC size_t get_number_of_ready_guard_conditions() override
typename GoalHandle::Result Result
Definition: client.hpp:263
Action Client.
Definition: client.hpp:255
typename ActionT::CancelGoalService::Request CancelRequest
Definition: client.hpp:265
GoalResponse
A response returned by an action server callback when a goal is requested.
Definition: server.hpp:43
RCLCPP_ACTION_PUBLIC size_t get_number_of_ready_timers() override
typename ActionT::Feedback Feedback
Definition: client.hpp:261
ResultCode
The possible statuses that an action goal can finish with.
Definition: client_goal_handle.hpp:35
CancelResponse
A response returned by an action server callback when a goal has been asked to be canceled...
Definition: server.hpp:54
T current_exception(T... args)
RCLCPP_ACTION_PUBLIC ClientBase(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base, rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph, rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging, const std::string &action_name, const rosidl_action_type_support_t *type_support, const rcl_action_client_options_t &options)
typename ClientGoalHandle< ActionT >::FeedbackCallback FeedbackCallback
Definition: client.hpp:264
virtual std::shared_ptr< void > create_cancel_response() const =0
virtual RCLCPP_ACTION_PUBLIC void handle_result_response(const rmw_request_id_t &response_header, std::shared_ptr< void > result_response)
RCLCPP_ACTION_PUBLIC size_t get_number_of_ready_services() override
virtual std::shared_ptr< void > create_result_response() const =0
bool wait_for_action_server(std::chrono::duration< int64_t, RatioT > timeout=std::chrono::duration< int64_t, RatioT >(-1))
Wait for action_server_is_ready() to become true, or until the given timeout is reached.
Definition: client.hpp:71
virtual RCLCPP_ACTION_PUBLIC ~ClientBase()
virtual std::shared_ptr< void > create_status_message() const =0
#define RCLCPP_DEBUG(logger,...)
std::shared_future< Result > async_get_result(typename GoalHandle::SharedPtr goal_handle)
Definition: client.hpp:328
std::shared_future< typename CancelResponse::SharedPtr > async_cancel_all_goals()
Definition: client.hpp:371
RCLCPP_ACTION_PUBLIC size_t get_number_of_ready_clients() override
virtual std::shared_ptr< void > create_goal_response() const =0
std::shared_future< typename CancelResponse::SharedPtr > async_cancel_goals_before(const rclcpp::Time &stamp)
Definition: client.hpp:382
T static_pointer_cast(T... args)
#define RCLCPP_ACTION_PUBLIC
Definition: visibility_control.hpp:50
#define RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(...)
virtual RCLCPP_ACTION_PUBLIC void handle_goal_response(const rmw_request_id_t &response_header, std::shared_ptr< void > goal_response)
action_msgs::msg::GoalStatus GoalStatus
Definition: types.hpp:33
std::shared_future< typename GoalHandle::SharedPtr > async_send_goal(const Goal &goal, FeedbackCallback callback=nullptr, bool ignore_result=false)
Definition: client.hpp:283
action_msgs::msg::GoalInfo GoalInfo
Definition: types.hpp:34
RCLCPP_ACTION_PUBLIC bool wait_for_action_server_nanoseconds(std::chrono::nanoseconds timeout)
Wait for action_server_is_ready() to become true, or until the given timeout is reached.
Definition: client.hpp:44
virtual RCLCPP_ACTION_PUBLIC void send_goal_request(std::shared_ptr< void > request, ResponseCallback callback)
virtual void handle_status_message(std::shared_ptr< void > message)=0
virtual RCLCPP_ACTION_PUBLIC void send_cancel_request(std::shared_ptr< void > request, ResponseCallback callback)
RCLCPP_ACTION_PUBLIC bool is_ready(rcl_wait_set_t *wait_set) override
virtual std::shared_ptr< void > create_feedback_message() const =0
T fill(T... args)
std::shared_future< bool > async_cancel_goal(typename GoalHandle::SharedPtr goal_handle)
Definition: client.hpp:342
Definition: client_goal_handle.hpp:49
RCLCPP_ACTION_PUBLIC size_t get_number_of_ready_subscriptions() override
virtual RCLCPP_ACTION_PUBLIC GoalID generate_goal_id()
virtual RCLCPP_ACTION_PUBLIC void handle_cancel_response(const rmw_request_id_t &response_header, std::shared_ptr< void > cancel_response)
virtual void handle_feedback_message(std::shared_ptr< void > message)=0
RCLCPP_ACTION_PUBLIC rclcpp::Logger get_logger()
typename ActionT::CancelGoalService::Response CancelResponse
Definition: client.hpp:266
RCLCPP_ACTION_PUBLIC void execute() override
RCLCPP_ACTION_PUBLIC bool add_to_wait_set(rcl_wait_set_t *wait_set) override
RCLCPP_ACTION_PUBLIC bool action_server_is_ready() const
Return true if there is an action server that is ready to take goal requests.