rclcpp  beta1
C++ ROS Client Library API
client.hpp
Go to the documentation of this file.
1 // Copyright 2014 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__CLIENT_HPP_
16 #define RCLCPP__CLIENT_HPP_
17 
18 #include <future>
19 #include <map>
20 #include <memory>
21 #include <sstream>
22 #include <string>
23 #include <tuple>
24 #include <utility>
25 
26 #include "rcl/client.h"
27 #include "rcl/error_handling.h"
28 #include "rcl/wait.h"
29 
31 #include "rclcpp/macros.hpp"
34 #include "rclcpp/utilities.hpp"
36 
37 #include "rmw/error_handling.h"
38 #include "rmw/rmw.h"
39 
40 namespace rclcpp
41 {
42 
43 namespace node_interfaces
44 {
45 class NodeBaseInterface;
46 } // namespace node_interfaces
47 
48 namespace client
49 {
50 
52 {
53 public:
55 
57  ClientBase(
59  rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
60  const std::string & service_name);
61 
63  virtual ~ClientBase();
64 
66  const std::string &
67  get_service_name() const;
68 
70  const rcl_client_t *
71  get_client_handle() const;
72 
74  bool
75  service_is_ready() const;
76 
77  template<typename RatioT = std::milli>
78  bool
81  {
82  return wait_for_service_nanoseconds(
83  std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
84  );
85  }
86 
87  virtual std::shared_ptr<void> create_response() = 0;
88  virtual std::shared_ptr<rmw_request_id_t> create_request_header() = 0;
89  virtual void handle_response(
90  std::shared_ptr<rmw_request_id_t> request_header, std::shared_ptr<void> response) = 0;
91 
92 protected:
93  RCLCPP_DISABLE_COPY(ClientBase)
94 
96  bool
97  wait_for_service_nanoseconds(std::chrono::nanoseconds timeout);
98 
100  rcl_node_t *
101  get_rcl_node_handle() const;
102 
103  rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
105 
108 };
109 
110 template<typename ServiceT>
111 class Client : public ClientBase
112 {
113 public:
114  using SharedRequest = typename ServiceT::Request::SharedPtr;
115  using SharedResponse = typename ServiceT::Response::SharedPtr;
116 
119 
122 
125 
128 
130 
132  rclcpp::node_interfaces::NodeBaseInterface * node_base,
133  rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
134  const std::string & service_name,
135  rcl_client_options_t & client_options)
136  : ClientBase(node_base, node_graph, service_name)
137  {
138  using rosidl_typesupport_cpp::get_service_type_support_handle;
139  auto service_type_support_handle =
140  get_service_type_support_handle<ServiceT>();
141  if (rcl_client_init(&client_handle_, this->get_rcl_node_handle(),
142  service_type_support_handle, service_name.c_str(), &client_options) != RCL_RET_OK)
143  {
144  // *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
145  throw std::runtime_error(
146  std::string("could not create client: ") +
148  // *INDENT-ON*
149  }
150  }
151 
152  virtual ~Client()
153  {
154  if (rcl_client_fini(&client_handle_, this->get_rcl_node_handle()) != RCL_RET_OK) {
155  fprintf(stderr,
156  "Error in destruction of rmw client handle: %s\n", rmw_get_error_string_safe());
157  }
158  }
159 
162  {
163  return std::shared_ptr<void>(new typename ServiceT::Response());
164  }
165 
168  {
169  // TODO(wjwwood): This should probably use rmw_request_id's allocator.
170  // (since it is a C type)
172  }
173 
174  void
176  std::shared_ptr<void> response)
177  {
178  std::lock_guard<std::mutex> lock(pending_requests_mutex_);
179  auto typed_response = std::static_pointer_cast<typename ServiceT::Response>(response);
180  int64_t sequence_number = request_header->sequence_number;
181  // TODO(esteve) this should throw instead since it is not expected to happen in the first place
182  if (this->pending_requests_.count(sequence_number) == 0) {
183  fprintf(stderr, "Received invalid sequence number. Ignoring...\n");
184  return;
185  }
186  auto tuple = this->pending_requests_[sequence_number];
187  auto call_promise = std::get<0>(tuple);
188  auto callback = std::get<1>(tuple);
189  auto future = std::get<2>(tuple);
190  this->pending_requests_.erase(sequence_number);
191  call_promise->set_value(typed_response);
192  callback(future);
193  }
194 
197  {
198  return async_send_request(request, [](SharedFuture) {});
199  }
200 
201  template<
202  typename CallbackT,
203  typename std::enable_if<
205  CallbackT,
207  >::value
208  >::type * = nullptr
209  >
211  async_send_request(SharedRequest request, CallbackT && cb)
212  {
213  std::lock_guard<std::mutex> lock(pending_requests_mutex_);
214  int64_t sequence_number;
215  if (RCL_RET_OK != rcl_send_request(get_client_handle(), request.get(), &sequence_number)) {
216  // *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
217  throw std::runtime_error(
218  std::string("failed to send request: ") + rcl_get_error_string_safe());
219  // *INDENT-ON*
220  }
221 
222  SharedPromise call_promise = std::make_shared<Promise>();
223  SharedFuture f(call_promise->get_future());
224  pending_requests_[sequence_number] =
225  std::make_tuple(call_promise, std::forward<CallbackType>(cb), f);
226  return f;
227  }
228 
229  template<
230  typename CallbackT,
231  typename std::enable_if<
233  CallbackT,
235  >::value
236  >::type * = nullptr
237  >
239  async_send_request(SharedRequest request, CallbackT && cb)
240  {
241  SharedPromiseWithRequest promise = std::make_shared<PromiseWithRequest>();
242  SharedFutureWithRequest future_with_request(promise->get_future());
243 
244  auto wrapping_cb = [future_with_request, promise, request, &cb](SharedFuture future) {
245  auto response = future.get();
246  promise->set_value(std::make_pair(request, response));
247  cb(future_with_request);
248  };
249 
250  async_send_request(request, wrapping_cb);
251 
252  return future_with_request;
253  }
254 
255 private:
257 
259  std::mutex pending_requests_mutex_;
260 };
261 
262 } // namespace client
263 } // namespace rclcpp
264 
265 #endif // RCLCPP__CLIENT_HPP_
SharedFuture async_send_request(SharedRequest request)
Definition: client.hpp:196
typename rcl_interfaces::srv::GetParameters ::Response::SharedPtr SharedResponse
Definition: client.hpp:115
SharedFutureWithRequest async_send_request(SharedRequest request, CallbackT &&cb)
Definition: client.hpp:239
virtual ~Client()
Definition: client.hpp:152
Definition: client.hpp:111
#define RCLCPP_DISABLE_COPY(...)
Definition: macros.hpp:26
std::shared_ptr< void > create_response()
Definition: client.hpp:161
Definition: allocator_common.hpp:24
T make_tuple(T... args)
rcl_ret_t rcl_send_request(const rcl_client_t *client, const void *ros_request, int64_t *sequence_number)
const char * rmw_get_error_string_safe(void)
#define RCL_RET_OK
SharedFuture async_send_request(SharedRequest request, CallbackT &&cb)
Definition: client.hpp:211
void handle_response(std::shared_ptr< rmw_request_id_t > request_header, std::shared_ptr< void > response)
Definition: client.hpp:175
rcl_ret_t rcl_client_init(rcl_client_t *client, const rcl_node_t *node, const rosidl_service_type_support_t *type_support, const char *service_name, const rcl_client_options_t *options)
#define RCLCPP_SMART_PTR_DEFINITIONS(...)
Definition: macros.hpp:36
Definition: function_traits.hpp:141
Definition: client.hpp:51
T make_pair(T... args)
T static_pointer_cast(T... args)
#define rcl_get_error_string_safe
#define RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(...)
Definition: macros.hpp:51
T get(T... args)
#define RCLCPP_PUBLIC
Definition: visibility_control.hpp:50
std::shared_ptr< rcl_node_t > node_handle_
Definition: client.hpp:104
bool wait_for_service(std::chrono::duration< int64_t, RatioT > timeout=std::chrono::duration< int64_t, RatioT >(-1))
Definition: client.hpp:79
Pure virtual interface class for the NodeBase part of the Node API.
Definition: node_base_interface.hpp:36
rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_
Definition: client.hpp:103
rcl_client_t rcl_get_zero_initialized_client(void)
rcl_ret_t rcl_client_fini(rcl_client_t *client, rcl_node_t *node)
std::string service_name_
Definition: client.hpp:107
std::shared_ptr< rmw_request_id_t > create_request_header()
Definition: client.hpp:167
typename rcl_interfaces::srv::GetParameters ::Request::SharedPtr SharedRequest
Definition: client.hpp:114