ROS 2 rclcpp + rcl - rolling  rolling-20536064
ROS 2 C++ Client Library with ROS Client Library
generic_service.cpp
1 // Copyright 2024 Sony Group Corporation.
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 "rclcpp/generic_service.hpp"
16 
17 #include "rclcpp/exceptions.hpp"
18 #include "rclcpp/logging.hpp"
19 
20 namespace rclcpp
21 {
22 GenericService::GenericService(
23  const std::shared_ptr<rcl_node_t> & node_handle,
24  const std::string & service_name,
25  const std::string & service_type,
26  GenericServiceCallback any_callback,
27  rcl_service_options_t & service_options)
28 : ServiceBase(node_handle),
29  any_callback_(std::move(any_callback))
30 {
31  const rosidl_service_type_support_t * service_ts;
32  try {
33  ts_lib_ = get_typesupport_library(
34  service_type, "rosidl_typesupport_cpp");
35 
36  service_ts = get_service_typesupport_handle(
37  service_type, "rosidl_typesupport_cpp", *ts_lib_);
38 
39  auto request_type_support_intro = get_message_typesupport_handle(
40  service_ts->request_typesupport,
41  rosidl_typesupport_introspection_cpp::typesupport_identifier);
42  request_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
43  request_type_support_intro->data);
44 
45  auto response_type_support_intro = get_message_typesupport_handle(
46  service_ts->response_typesupport,
47  rosidl_typesupport_introspection_cpp::typesupport_identifier);
48  response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
49  response_type_support_intro->data);
50  } catch (std::runtime_error & err) {
51  RCLCPP_ERROR(
52  rclcpp::get_node_logger(node_handle_.get()).get_child("rclcpp"),
53  "Invalid service type: %s",
54  err.what());
56  }
57 
58  // rcl does the static memory allocation here
59  service_handle_ = std::shared_ptr<rcl_service_t>(
60  new rcl_service_t, [handle = node_handle_, service_name](rcl_service_t * service)
61  {
62  if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
63  RCLCPP_ERROR(
64  rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
65  "Error in destruction of rcl service handle: %s",
66  rcl_get_error_string().str);
67  rcl_reset_error();
68  }
69  delete service;
70  });
71  *service_handle_.get() = rcl_get_zero_initialized_service();
72 
74  service_handle_.get(),
75  node_handle.get(),
76  service_ts,
77  service_name.c_str(),
78  &service_options);
79  if (ret != RCL_RET_OK) {
80  if (ret == RCL_RET_SERVICE_NAME_INVALID) {
81  auto rcl_node_handle = get_rcl_node_handle();
82  // this will throw on any validation problem
83  rcl_reset_error();
85  service_name,
86  rcl_node_get_name(rcl_node_handle),
87  rcl_node_get_namespace(rcl_node_handle),
88  true);
89  }
90 
91  rclcpp::exceptions::throw_from_rcl_error(ret, "could not create service");
92  }
93  TRACETOOLS_TRACEPOINT(
94  rclcpp_service_callback_added,
95  static_cast<const void *>(get_service_handle().get()),
96  static_cast<const void *>(&any_callback_));
97 #ifndef TRACETOOLS_DISABLED
98  any_callback_.register_callback_for_tracing();
99 #endif
100 }
101 
102 bool
104  SharedRequest & request_out,
105  rmw_request_id_t & request_id_out)
106 {
107  request_out = create_request();
108  return this->take_type_erased_request(request_out.get(), request_id_out);
109 }
110 
111 std::shared_ptr<void>
112 GenericService::create_request()
113 {
114  Request request = new uint8_t[request_members_->size_of_];
115  request_members_->init_function(request, rosidl_runtime_cpp::MessageInitialization::ZERO);
116  return std::shared_ptr<void>(
117  request,
118  [this](void * p)
119  {
120  request_members_->fini_function(p);
121  delete[] reinterpret_cast<uint8_t *>(p);
122  });
123 }
124 
125 std::shared_ptr<void>
126 GenericService::create_response()
127 {
128  Response response = new uint8_t[response_members_->size_of_];
129  response_members_->init_function(response, rosidl_runtime_cpp::MessageInitialization::ZERO);
130  return std::shared_ptr<void>(
131  response,
132  [this](void * p)
133  {
134  response_members_->fini_function(p);
135  delete[] reinterpret_cast<uint8_t *>(p);
136  });
137 }
138 
139 std::shared_ptr<rmw_request_id_t>
140 GenericService::create_request_header()
141 {
142  return std::make_shared<rmw_request_id_t>();
143 }
144 
145 void
146 GenericService::handle_request(
147  const std::shared_ptr<rmw_request_id_t> & request_header,
148  const std::shared_ptr<void> & request)
149 {
150  auto response = any_callback_.dispatch(
151  this->shared_from_this(), request_header, request, create_response());
152  if (response) {
153  send_response(*request_header, response);
154  }
155 }
156 
157 void
158 GenericService::send_response(rmw_request_id_t & req_id, SharedResponse & response)
159 {
160  rcl_ret_t ret = rcl_send_response(get_service_handle().get(), &req_id, response.get());
161 
162  if (ret == RCL_RET_TIMEOUT) {
163  RCLCPP_WARN(
164  node_logger_.get_child("rclcpp"),
165  "failed to send response to %s (timeout): %s",
166  this->get_service_name(), rcl_get_error_string().str);
167  rcl_reset_error();
168  return;
169  }
170  if (ret != RCL_RET_OK) {
171  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send response");
172  }
173 }
174 
175 } // namespace rclcpp
RCLCPP_PUBLIC bool take_request(SharedRequest &request_out, rmw_request_id_t &request_id_out)
Take the next request from the service.
RCLCPP_PUBLIC Logger get_child(const std::string &suffix)
Return a logger that is a descendant of this logger.
Definition: logger.cpp:57
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
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.
RCLCPP_PUBLIC Logger get_node_logger(const rcl_node_t *node)
Return a named logger using an rcl_node_t.
Definition: logger.cpp:43
RCLCPP_PUBLIC std::shared_ptr< rcpputils::SharedLibrary > get_typesupport_library(const std::string &type, const std::string &typesupport_identifier)
Load the type support library for the given type.
RCLCPP_PUBLIC const rosidl_message_type_support_t * get_message_typesupport_handle(const std::string &type, const std::string &typesupport_identifier, rcpputils::SharedLibrary &library)
Extracts the message type support handle from the library.
RCLCPP_PUBLIC const rosidl_service_type_support_t * get_service_typesupport_handle(const std::string &type, const std::string &typesupport_identifier, rcpputils::SharedLibrary &library)
Extracts the service type support handle from the library.
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_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_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
Options available for a rcl service.
Definition: service.h:50
Structure which encapsulates a ROS Service.
Definition: service.h:43
#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