ROS 2 rclcpp + rcl - rolling  rolling-29de98cf
ROS 2 C++ Client Library with ROS Client Library
service.hpp
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__SERVICE_HPP_
16 #define RCLCPP__SERVICE_HPP_
17 
18 #include <atomic>
19 #include <functional>
20 #include <memory>
21 #include <mutex>
22 #include <string>
23 #include <utility>
24 
25 #include "rcl/error_handling.h"
26 #include "rcl/event_callback.h"
27 #include "rcl/service.h"
28 #include "rcl/service_introspection.h"
29 
30 #include "rmw/error_handling.h"
31 #include "rmw/rmw.h"
32 
33 #include "tracetools/tracetools.h"
34 
35 #include "rclcpp/any_service_callback.hpp"
36 #include "rclcpp/clock.hpp"
37 #include "rclcpp/exceptions.hpp"
38 #include "rclcpp/expand_topic_or_service_name.hpp"
39 #include "rclcpp/logging.hpp"
40 #include "rclcpp/macros.hpp"
41 #include "rclcpp/qos.hpp"
42 #include "rclcpp/type_support_decl.hpp"
43 #include "rclcpp/visibility_control.hpp"
44 
45 namespace rclcpp
46 {
47 
49 {
50 public:
51  RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ServiceBase)
52 
53  RCLCPP_PUBLIC
54  explicit ServiceBase(const std::shared_ptr<rcl_node_t> & node_handle);
55 
56  RCLCPP_PUBLIC
57  virtual ~ServiceBase() = default;
58 
60 
61  RCLCPP_PUBLIC
62  const char *
63  get_service_name() const;
64 
66 
70  RCLCPP_PUBLIC
71  std::shared_ptr<rcl_service_t>
73 
75 
79  RCLCPP_PUBLIC
80  std::shared_ptr<const rcl_service_t>
81  get_service_handle() const;
82 
84 
98  RCLCPP_PUBLIC
99  bool
100  take_type_erased_request(void * request_out, rmw_request_id_t & request_id_out);
101 
102  virtual
103  std::shared_ptr<void>
104  create_request() = 0;
105 
106  virtual
107  std::shared_ptr<rmw_request_id_t>
108  create_request_header() = 0;
109 
110  virtual
111  void
112  handle_request(
113  const std::shared_ptr<rmw_request_id_t> & request_header,
114  const std::shared_ptr<void> & request) = 0;
115 
117 
126  RCLCPP_PUBLIC
127  bool
128  exchange_in_use_by_wait_set_state(bool in_use_state);
129 
131 
142  RCLCPP_PUBLIC
145 
147 
158  RCLCPP_PUBLIC
161 
163 
189  RCLCPP_PUBLIC
190  void
191  set_on_new_request_callback(const std::function<void(size_t)> & callback);
192 
194  RCLCPP_PUBLIC
195  void
197 
198 protected:
199  RCLCPP_DISABLE_COPY(ServiceBase)
200 
201  RCLCPP_PUBLIC
202  rcl_node_t *
203  get_rcl_node_handle();
204 
205  RCLCPP_PUBLIC
206  const rcl_node_t *
207  get_rcl_node_handle() const;
208 
209  RCLCPP_PUBLIC
210  void
211  set_on_new_request_callback(rcl_event_callback_t callback, const void * user_data);
212 
213  std::shared_ptr<rcl_node_t> node_handle_;
214 
215  std::recursive_mutex callback_mutex_;
216  // It is important to declare on_new_request_callback_ before
217  // service_handle_, so on destruction the service is
218  // destroyed first. Otherwise, the rmw service callback
219  // would point briefly to a destroyed function.
220  std::function<void(size_t)> on_new_request_callback_{nullptr};
221  // Declare service_handle_ after callback
222  std::shared_ptr<rcl_service_t> service_handle_;
223  bool owns_rcl_handle_ = true;
224 
225  rclcpp::Logger node_logger_;
226 
227  std::atomic<bool> in_use_by_wait_set_{false};
228 };
229 
230 template<typename ServiceT>
231 class Service
232  : public ServiceBase,
233  public std::enable_shared_from_this<Service<ServiceT>>
234 {
235 public:
236  using ServiceType = ServiceT;
237  using CallbackType = std::function<
238  void (
239  const std::shared_ptr<typename ServiceT::Request>,
240  std::shared_ptr<typename ServiceT::Response>)>;
241 
242  using CallbackWithHeaderType = std::function<
243  void (
244  const std::shared_ptr<rmw_request_id_t>,
245  const std::shared_ptr<typename ServiceT::Request>,
246  std::shared_ptr<typename ServiceT::Response>)>;
247  RCLCPP_SMART_PTR_DEFINITIONS(Service)
248 
249 
261  std::shared_ptr<rcl_node_t> node_handle,
262  const std::string & service_name,
263  AnyServiceCallback<ServiceT> any_callback,
264  rcl_service_options_t & service_options)
265  : ServiceBase(node_handle), any_callback_(any_callback),
266  srv_type_support_handle_(rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
267  {
268  // rcl does the static memory allocation here
269  service_handle_ = std::shared_ptr<rcl_service_t>(
270  new rcl_service_t, [handle = node_handle_, service_name](rcl_service_t * service)
271  {
272  if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
273  RCLCPP_ERROR(
274  rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
275  "Error in destruction of rcl service handle: %s",
276  rcl_get_error_string().str);
277  rcl_reset_error();
278  }
279  delete service;
280  });
281  *service_handle_.get() = rcl_get_zero_initialized_service();
282 
284  service_handle_.get(),
285  node_handle.get(),
286  srv_type_support_handle_,
287  service_name.c_str(),
288  &service_options);
289  if (ret != RCL_RET_OK) {
290  if (ret == RCL_RET_SERVICE_NAME_INVALID) {
291  auto rcl_node_handle = get_rcl_node_handle();
292  // this will throw on any validation problem
293  rcl_reset_error();
295  service_name,
296  rcl_node_get_name(rcl_node_handle),
297  rcl_node_get_namespace(rcl_node_handle),
298  true);
299  }
300 
301  rclcpp::exceptions::throw_from_rcl_error(ret, "could not create service");
302  }
303  TRACETOOLS_TRACEPOINT(
304  rclcpp_service_callback_added,
305  static_cast<const void *>(get_service_handle().get()),
306  static_cast<const void *>(&any_callback_));
307 #ifndef TRACETOOLS_DISABLED
308  any_callback_.register_callback_for_tracing();
309 #endif
310  }
311 
313 
323  const std::shared_ptr<rcl_node_t> & node_handle,
324  const std::shared_ptr<rcl_service_t> & service_handle,
325  AnyServiceCallback<ServiceT> any_callback)
326  : ServiceBase(node_handle), any_callback_(std::move(any_callback)),
327  srv_type_support_handle_(rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
328  {
329  // check if service handle was initialized
330  if (!rcl_service_is_valid(service_handle.get())) {
331  // *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
332  throw std::runtime_error(
333  std::string("rcl_service_t in constructor argument must be initialized beforehand."));
334  // *INDENT-ON*
335  }
336 
337  service_handle_ = service_handle;
338  TRACETOOLS_TRACEPOINT(
339  rclcpp_service_callback_added,
340  static_cast<const void *>(get_service_handle().get()),
341  static_cast<const void *>(&any_callback_));
342 #ifndef TRACETOOLS_DISABLED
343  any_callback_.register_callback_for_tracing();
344 #endif
345  }
346 
348 
358  const std::shared_ptr<rcl_node_t> & node_handle,
359  rcl_service_t * service_handle,
360  AnyServiceCallback<ServiceT> any_callback)
361  : ServiceBase(node_handle), any_callback_(std::move(any_callback)),
362  srv_type_support_handle_(rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
363  {
364  // check if service handle was initialized
365  if (!rcl_service_is_valid(service_handle)) {
366  // *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
367  throw std::runtime_error(
368  std::string("rcl_service_t in constructor argument must be initialized beforehand."));
369  // *INDENT-ON*
370  }
371 
372  // In this case, rcl owns the service handle memory
373  service_handle_ = std::shared_ptr<rcl_service_t>(new rcl_service_t);
374  service_handle_->impl = service_handle->impl;
375  TRACETOOLS_TRACEPOINT(
376  rclcpp_service_callback_added,
377  static_cast<const void *>(get_service_handle().get()),
378  static_cast<const void *>(&any_callback_));
379 #ifndef TRACETOOLS_DISABLED
380  any_callback_.register_callback_for_tracing();
381 #endif
382  }
383 
384  Service() = delete;
385 
386  virtual ~Service()
387  {
388  }
389 
391 
402  bool
403  take_request(typename ServiceT::Request & request_out, rmw_request_id_t & request_id_out)
404  {
405  return this->take_type_erased_request(&request_out, request_id_out);
406  }
407 
408  std::shared_ptr<void>
409  create_request() override
410  {
411  return std::make_shared<typename ServiceT::Request>();
412  }
413 
414  std::shared_ptr<rmw_request_id_t>
415  create_request_header() override
416  {
417  return std::make_shared<rmw_request_id_t>();
418  }
419 
420  void
421  handle_request(
422  const std::shared_ptr<rmw_request_id_t> & request_header,
423  const std::shared_ptr<void> & request) override
424  {
425  auto typed_request = std::static_pointer_cast<typename ServiceT::Request>(request);
426  auto response = any_callback_.dispatch(this->shared_from_this(), request_header, typed_request);
427  if (response) {
428  send_response(*request_header, *response);
429  }
430  }
431 
432  void
433  send_response(rmw_request_id_t & req_id, typename ServiceT::Response & response)
434  {
435  rcl_ret_t ret = rcl_send_response(get_service_handle().get(), &req_id, &response);
436 
437  if (ret == RCL_RET_TIMEOUT) {
438  RCLCPP_WARN(
439  node_logger_.get_child("rclcpp"),
440  "failed to send response to %s (timeout): %s",
441  this->get_service_name(), rcl_get_error_string().str);
442  rcl_reset_error();
443  return;
444  }
445  if (ret != RCL_RET_OK) {
446  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send response");
447  }
448  }
449 
451 
459  void
461  const Clock::SharedPtr & clock, const QoS & qos_service_event_pub,
462  rcl_service_introspection_state_t introspection_state)
463  {
465  pub_opts.qos = qos_service_event_pub.get_rmw_qos_profile();
466 
468  service_handle_.get(),
469  node_handle_.get(),
470  clock->get_clock_handle(),
471  srv_type_support_handle_,
472  pub_opts,
473  introspection_state);
474 
475  if (RCL_RET_OK != ret) {
476  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure service introspection");
477  }
478  }
479 
480 private:
481  RCLCPP_DISABLE_COPY(Service)
482 
483  AnyServiceCallback<ServiceT> any_callback_;
484 
485  const rosidl_service_type_support_t * srv_type_support_handle_;
486 };
487 
488 } // namespace rclcpp
489 
490 #endif // RCLCPP__SERVICE_HPP_
RCLCPP_PUBLIC Logger get_child(const std::string &suffix)
Return a logger that is a descendant of this logger.
Definition: logger.cpp:57
Encapsulation of Quality of Service settings.
Definition: qos.hpp:114
rmw_qos_profile_t & get_rmw_qos_profile()
Return the rmw qos profile.
Definition: qos.cpp:109
RCLCPP_PUBLIC bool exchange_in_use_by_wait_set_state(bool in_use_state)
Exchange the "in use by wait set" state for this service.
Definition: service.cpp:87
RCLCPP_PUBLIC rclcpp::QoS get_request_subscription_actual_qos() const
Get the actual request subscription QoS settings, after the defaults have been determined.
Definition: service.cpp:112
RCLCPP_PUBLIC rclcpp::QoS get_response_publisher_actual_qos() const
Get the actual response publisher QoS settings, after the defaults have been determined.
Definition: service.cpp:93
RCLCPP_PUBLIC const char * get_service_name() const
Return the name of the service.
Definition: service.cpp:57
RCLCPP_PUBLIC void clear_on_new_request_callback()
Unset the callback registered for new requests, if any.
Definition: service.cpp:193
RCLCPP_PUBLIC std::shared_ptr< rcl_service_t > get_service_handle()
Return the rcl_service_t service handle in a std::shared_ptr.
Definition: service.cpp:63
RCLCPP_PUBLIC bool take_type_erased_request(void *request_out, rmw_request_id_t &request_id_out)
Take the next request from the service as a type erased pointer.
Definition: service.cpp:42
RCLCPP_PUBLIC void set_on_new_request_callback(const std::function< void(size_t)> &callback)
Set a callback to be called when each new request is received.
Definition: service.cpp:145
bool take_request(typename ServiceT::Request &request_out, rmw_request_id_t &request_id_out)
Take the next request from the service.
Definition: service.hpp:403
Service(const std::shared_ptr< rcl_node_t > &node_handle, rcl_service_t *service_handle, AnyServiceCallback< ServiceT > any_callback)
Default constructor.
Definition: service.hpp:357
Service(const std::shared_ptr< rcl_node_t > &node_handle, const std::shared_ptr< rcl_service_t > &service_handle, AnyServiceCallback< ServiceT > any_callback)
Default constructor.
Definition: service.hpp:322
void configure_introspection(const Clock::SharedPtr &clock, const QoS &qos_service_event_pub, rcl_service_introspection_state_t introspection_state)
Configure service introspection.
Definition: service.hpp:460
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC std::string expand_topic_or_service_name(const std::string &name, const std::string &node_name, const std::string &namespace_, bool is_service=false)
Expand a topic or service name and throw if it is not valid.
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_node_get_name(const rcl_node_t *node)
Return the name of the node.
Definition: node.c:416
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_node_get_namespace(const rcl_node_t *node)
Return the namespace of the node.
Definition: node.c:425
RCL_PUBLIC RCL_WARN_UNUSED rcl_publisher_options_t rcl_publisher_get_default_options(void)
Return the default publisher options in a rcl_publisher_options_t.
Definition: publisher.c:217
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_service_init(rcl_service_t *service, const rcl_node_t *node, const rosidl_service_type_support_t *type_support, const char *service_name, const rcl_service_options_t *options)
Initialize a rcl service.
Definition: service.c:76
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_service_configure_service_introspection(rcl_service_t *service, rcl_node_t *node, rcl_clock_t *clock, const rosidl_service_type_support_t *type_support, const rcl_publisher_options_t publisher_options, rcl_service_introspection_state_t introspection_state)
Configure service introspection features for the service.
Definition: service.c:462
RCL_PUBLIC bool rcl_service_is_valid(const rcl_service_t *service)
Check that the service is valid.
Definition: service.c:416
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_service_fini(rcl_service_t *service, rcl_node_t *node)
Finalize a rcl_service_t.
Definition: service.c:225
RCL_PUBLIC RCL_WARN_UNUSED rcl_service_t rcl_get_zero_initialized_service(void)
Return a rcl_service_t struct with members set to NULL.
Definition: service.c:45
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_send_response(const rcl_service_t *service, rmw_request_id_t *response_header, void *ros_response)
Send a ROS response to a client using a service.
Definition: service.c:375
Structure which encapsulates a ROS Node.
Definition: node.h:45
Options available for a rcl publisher.
Definition: publisher.h:44
rmw_qos_profile_t qos
Middleware quality of service settings for the publisher.
Definition: publisher.h:46
Options available for a rcl service.
Definition: service.h:50
Structure which encapsulates a ROS Service.
Definition: service.h:43
rcl_service_impl_t * impl
Pointer to the service implementation.
Definition: service.h:45
#define RCL_RET_SERVICE_NAME_INVALID
Service name (same as topic name) does not pass validation.
Definition: types.h:49
#define RCL_RET_OK
Success return code.
Definition: types.h:27
#define RCL_RET_TIMEOUT
Timeout occurred return code.
Definition: types.h:31
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24