ROS 2 rclcpp + rcl - humble  humble
ROS 2 C++ Client Library with ROS Client Library
service.c
1 // Copyright 2016 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 #ifdef __cplusplus
16 extern "C"
17 {
18 #endif
19 
20 #include "rcl/service.h"
21 
22 #include <stdio.h>
23 #include <string.h>
24 
25 #include "rcl/error_handling.h"
26 #include "rcl/node.h"
27 #include "rcutils/logging_macros.h"
28 #include "rcutils/macros.h"
29 #include "rmw/error_handling.h"
30 #include "rmw/rmw.h"
31 #include "tracetools/tracetools.h"
32 
34 {
35  rcl_service_options_t options;
36  rmw_qos_profile_t actual_request_subscription_qos;
37  rmw_qos_profile_t actual_response_publisher_qos;
38  rmw_service_t * rmw_handle;
39 };
40 
43 {
44  static rcl_service_t null_service = {0};
45  return null_service;
46 }
47 
50  rcl_service_t * service,
51  const rcl_node_t * node,
52  const rosidl_service_type_support_t * type_support,
53  const char * service_name,
54  const rcl_service_options_t * options)
55 {
56  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
57  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ALREADY_INIT);
58  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
59  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
60  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
61  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_SERVICE_NAME_INVALID);
62 
63  rcl_ret_t fail_ret = RCL_RET_ERROR;
64 
65  // Check options and allocator first, so the allocator can be used in errors.
66  RCL_CHECK_ARGUMENT_FOR_NULL(options, RCL_RET_INVALID_ARGUMENT);
67  rcl_allocator_t * allocator = (rcl_allocator_t *)&options->allocator;
68  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
69 
70  RCL_CHECK_ARGUMENT_FOR_NULL(service, RCL_RET_INVALID_ARGUMENT);
71  if (!rcl_node_is_valid(node)) {
72  return RCL_RET_NODE_INVALID; // error already set
73  }
74  RCL_CHECK_ARGUMENT_FOR_NULL(type_support, RCL_RET_INVALID_ARGUMENT);
75  RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
76  RCUTILS_LOG_DEBUG_NAMED(
77  ROS_PACKAGE_NAME, "Initializing service for service name '%s'", service_name);
78  if (service->impl) {
79  RCL_SET_ERROR_MSG("service already initialized, or memory was unintialized");
80  return RCL_RET_ALREADY_INIT;
81  }
82 
83  // Expand and remap the given service name.
84  char * remapped_service_name = NULL;
86  node,
87  service_name,
88  *allocator,
89  true,
90  false,
91  &remapped_service_name);
92  if (ret != RCL_RET_OK) {
95  } else if (ret != RCL_RET_BAD_ALLOC) {
96  ret = RCL_RET_ERROR;
97  }
98  goto cleanup;
99  }
100  RCUTILS_LOG_DEBUG_NAMED(
101  ROS_PACKAGE_NAME, "Expanded and remapped service name '%s'", remapped_service_name);
102 
103  // Allocate space for the implementation struct.
104  service->impl = (rcl_service_impl_t *)allocator->allocate(
105  sizeof(rcl_service_impl_t), allocator->state);
106  RCL_CHECK_FOR_NULL_WITH_MSG(
107  service->impl, "allocating memory failed", ret = RCL_RET_BAD_ALLOC; goto cleanup);
108 
109  if (RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL == options->qos.durability) {
110  RCUTILS_LOG_WARN_NAMED(
111  ROS_PACKAGE_NAME,
112  "Warning: Setting QoS durability to 'transient local' for service servers "
113  "can cause them to receive requests from clients that have since terminated.");
114  }
115  // Fill out implementation struct.
116  // rmw handle (create rmw service)
117  // TODO(wjwwood): pass along the allocator to rmw when it supports it
118  service->impl->rmw_handle = rmw_create_service(
120  type_support,
121  remapped_service_name,
122  &options->qos);
123  if (!service->impl->rmw_handle) {
124  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
125  goto fail;
126  }
127  // get actual qos, and store it
128  rmw_ret_t rmw_ret = rmw_service_request_subscription_get_actual_qos(
129  service->impl->rmw_handle,
130  &service->impl->actual_request_subscription_qos);
131 
132  if (RMW_RET_OK != rmw_ret) {
133  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
134  goto fail;
135  }
136 
137  rmw_ret = rmw_service_response_publisher_get_actual_qos(
138  service->impl->rmw_handle,
139  &service->impl->actual_response_publisher_qos);
140 
141  if (RMW_RET_OK != rmw_ret) {
142  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
143  goto fail;
144  }
145 
146  // ROS specific namespacing conventions is not retrieved by get_actual_qos
147  service->impl->actual_request_subscription_qos.avoid_ros_namespace_conventions =
148  options->qos.avoid_ros_namespace_conventions;
149  service->impl->actual_response_publisher_qos.avoid_ros_namespace_conventions =
150  options->qos.avoid_ros_namespace_conventions;
151 
152  // options
153  service->impl->options = *options;
154  RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Service initialized");
155  ret = RCL_RET_OK;
156  TRACEPOINT(
158  (const void *)service,
159  (const void *)node,
160  (const void *)service->impl->rmw_handle,
161  remapped_service_name);
162  goto cleanup;
163 fail:
164  if (service->impl) {
165  allocator->deallocate(service->impl, allocator->state);
166  service->impl = NULL;
167  }
168  ret = fail_ret;
169  // Fall through to clean up
170 cleanup:
171  allocator->deallocate(remapped_service_name, allocator->state);
172  return ret;
173 }
174 
175 rcl_ret_t
177 {
178  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_SERVICE_INVALID);
179  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
180  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
181  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
182 
183  RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Finalizing service");
184  RCL_CHECK_ARGUMENT_FOR_NULL(service, RCL_RET_SERVICE_INVALID);
186  return RCL_RET_NODE_INVALID; // error already set
187  }
188 
189  rcl_ret_t result = RCL_RET_OK;
190  if (service->impl) {
191  rcl_allocator_t allocator = service->impl->options.allocator;
192  rmw_node_t * rmw_node = rcl_node_get_rmw_handle(node);
193  if (!rmw_node) {
195  }
196  rmw_ret_t ret = rmw_destroy_service(rmw_node, service->impl->rmw_handle);
197  if (ret != RMW_RET_OK) {
198  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
199  result = RCL_RET_ERROR;
200  }
201  allocator.deallocate(service->impl, allocator.state);
202  service->impl = NULL;
203  }
204  RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Service finalized");
205  return result;
206 }
207 
210 {
211  // !!! MAKE SURE THAT CHANGES TO THESE DEFAULTS ARE REFLECTED IN THE HEADER DOC STRING
212  static rcl_service_options_t default_options;
213  // Must set the allocator and qos after because they are not a compile time constant.
214  default_options.qos = rmw_qos_profile_services_default;
215  default_options.allocator = rcl_get_default_allocator();
216  return default_options;
217 }
218 
219 const char *
221 {
222  const rcl_service_options_t * options = rcl_service_get_options(service);
223  if (!options) {
224  return NULL;
225  }
226  RCL_CHECK_FOR_NULL_WITH_MSG(service->impl->rmw_handle, "service is invalid", return NULL);
227  return service->impl->rmw_handle->service_name;
228 }
229 
230 #define _service_get_options(service) & service->impl->options
231 
232 const rcl_service_options_t *
234 {
235  if (!rcl_service_is_valid(service)) {
236  return NULL; // error already set
237  }
238  return _service_get_options(service);
239 }
240 
241 rmw_service_t *
243 {
244  if (!rcl_service_is_valid(service)) {
245  return NULL; // error already set
246  }
247  return service->impl->rmw_handle;
248 }
249 
250 rcl_ret_t
252  const rcl_service_t * service,
253  rmw_service_info_t * request_header,
254  void * ros_request)
255 {
256  RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Service server taking service request");
257  if (!rcl_service_is_valid(service)) {
258  return RCL_RET_SERVICE_INVALID; // error already set
259  }
260  RCL_CHECK_ARGUMENT_FOR_NULL(request_header, RCL_RET_INVALID_ARGUMENT);
261  RCL_CHECK_ARGUMENT_FOR_NULL(ros_request, RCL_RET_INVALID_ARGUMENT);
262  const rcl_service_options_t * options = rcl_service_get_options(service);
263  RCL_CHECK_FOR_NULL_WITH_MSG(options, "Failed to get service options", return RCL_RET_ERROR);
264 
265  bool taken = false;
266  rmw_ret_t ret = rmw_take_request(
267  service->impl->rmw_handle, request_header, ros_request, &taken);
268  if (RMW_RET_OK != ret) {
269  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
270  if (RMW_RET_BAD_ALLOC == ret) {
271  return RCL_RET_BAD_ALLOC;
272  }
273  return RCL_RET_ERROR;
274  }
275  RCUTILS_LOG_DEBUG_NAMED(
276  ROS_PACKAGE_NAME, "Service take request succeeded: %s", taken ? "true" : "false");
277  if (!taken) {
279  }
280  return RCL_RET_OK;
281 }
282 
283 rcl_ret_t
285  const rcl_service_t * service,
286  rmw_request_id_t * request_header,
287  void * ros_request)
288 {
289  rmw_service_info_t header;
290  header.request_id = *request_header;
291  rcl_ret_t ret = rcl_take_request_with_info(service, &header, ros_request);
292  *request_header = header.request_id;
293  return ret;
294 }
295 
296 rcl_ret_t
298  const rcl_service_t * service,
299  rmw_request_id_t * request_header,
300  void * ros_response)
301 {
302  rcl_ret_t ret;
303  RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Sending service response");
304  if (!rcl_service_is_valid(service)) {
305  return RCL_RET_SERVICE_INVALID; // error already set
306  }
307  RCL_CHECK_ARGUMENT_FOR_NULL(request_header, RCL_RET_INVALID_ARGUMENT);
308  RCL_CHECK_ARGUMENT_FOR_NULL(ros_response, RCL_RET_INVALID_ARGUMENT);
309  const rcl_service_options_t * options = rcl_service_get_options(service);
310  RCL_CHECK_FOR_NULL_WITH_MSG(options, "Failed to get service options", return RCL_RET_ERROR);
311 
312  ret = rmw_send_response(service->impl->rmw_handle, request_header, ros_response);
313  if (ret != RMW_RET_OK) {
314  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
315  if (ret == RMW_RET_TIMEOUT) {
316  return RCL_RET_TIMEOUT;
317  }
318  return RCL_RET_ERROR;
319  }
320  return RCL_RET_OK;
321 }
322 
323 bool
325 {
326  RCL_CHECK_FOR_NULL_WITH_MSG(service, "service pointer is invalid", return false);
327  RCL_CHECK_FOR_NULL_WITH_MSG(
328  service->impl, "service's implementation is invalid", return false);
329  RCL_CHECK_FOR_NULL_WITH_MSG(
330  service->impl->rmw_handle, "service's rmw handle is invalid", return false);
331  return true;
332 }
333 
334 const rmw_qos_profile_t *
336 {
337  if (!rcl_service_is_valid(service)) {
338  return NULL;
339  }
340  return &service->impl->actual_request_subscription_qos;
341 }
342 
343 const rmw_qos_profile_t *
345 {
346  if (!rcl_service_is_valid(service)) {
347  return NULL;
348  }
349  return &service->impl->actual_response_publisher_qos;
350 }
351 
352 rcl_ret_t
354  const rcl_service_t * service,
355  rcl_event_callback_t callback,
356  const void * user_data)
357 {
358  if (!rcl_service_is_valid(service)) {
359  // error state already set
361  }
362 
363  return rmw_service_set_on_new_request_callback(
364  service->impl->rmw_handle,
365  callback,
366  user_data);
367 }
368 
369 #ifdef __cplusplus
370 }
371 #endif
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Definition: allocator.h:37
#define RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, msg, fail_statement)
Check that the given allocator is initialized, or fail with a message.
Definition: allocator.h:56
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
Definition: allocator.h:31
RCL_PUBLIC bool rcl_node_is_valid(const rcl_node_t *node)
Return true if the node is valid, else false.
Definition: node.c:421
RCL_PUBLIC RCL_WARN_UNUSED rmw_node_t * rcl_node_get_rmw_handle(const rcl_node_t *node)
Return the rmw node handle.
Definition: node.c:485
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_node_resolve_name(const rcl_node_t *node, const char *input_name, rcl_allocator_t allocator, bool is_service, bool only_expand, char **output_name)
Expand a given name into a fully-qualified topic name and apply remapping rules.
RCL_PUBLIC bool rcl_node_is_valid_except_context(const rcl_node_t *node)
Return true if node is valid, except for the context being valid.
Definition: node.c:411
RCL_PUBLIC RCL_WARN_UNUSED const rmw_qos_profile_t * rcl_service_request_subscription_get_actual_qos(const rcl_service_t *service)
Get the actual qos settings of the service's request subscription.
Definition: service.c:335
RCL_PUBLIC RCL_WARN_UNUSED rmw_service_t * rcl_service_get_rmw_handle(const rcl_service_t *service)
Return the rmw service handle.
Definition: service.c:242
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_service_get_service_name(const rcl_service_t *service)
Get the topic name for the service.
Definition: service.c:220
RCL_PUBLIC RCL_WARN_UNUSED const rmw_qos_profile_t * rcl_service_response_publisher_get_actual_qos(const rcl_service_t *service)
Get the actual qos settings of the service's response publisher.
Definition: service.c:344
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:49
RCL_PUBLIC RCL_WARN_UNUSED const rcl_service_options_t * rcl_service_get_options(const rcl_service_t *service)
Return the rcl service options.
Definition: service.c:233
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_take_request_with_info(const rcl_service_t *service, rmw_service_info_t *request_header, void *ros_request)
Take a pending ROS request using a rcl service.
Definition: service.c:251
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_service_set_on_new_request_callback(const rcl_service_t *service, rcl_event_callback_t callback, const void *user_data)
Set the on new request callback function for the service.
Definition: service.c:353
RCL_PUBLIC bool rcl_service_is_valid(const rcl_service_t *service)
Check that the service is valid.
Definition: service.c:324
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:176
RCL_PUBLIC RCL_WARN_UNUSED rcl_service_options_t rcl_service_get_default_options(void)
Return the default service options in a rcl_service_options_t.
Definition: service.c:209
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:42
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_take_request(const rcl_service_t *service, rmw_request_id_t *request_header, void *ros_request)
Backwards compatibility function to take a pending ROS request using a rcl service.
Definition: service.c:284
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:297
Structure which encapsulates a ROS Node.
Definition: node.h:42
Options available for a rcl service.
Definition: service.h:44
rmw_qos_profile_t qos
Middleware quality of service settings for the service.
Definition: service.h:46
rcl_allocator_t allocator
Custom allocator for the service, used for incidental allocations.
Definition: service.h:49
Structure which encapsulates a ROS Service.
Definition: service.h:37
rcl_service_impl_t * impl
Pointer to the service implementation.
Definition: service.h:39
#define RCL_RET_SERVICE_INVALID
Invalid rcl_service_t given return code.
Definition: types.h:82
#define RCL_RET_UNKNOWN_SUBSTITUTION
Topic name substitution is unknown.
Definition: types.h:50
#define RCL_RET_SERVICE_NAME_INVALID
Service name (same as topic name) does not pass validation.
Definition: types.h:48
#define RCL_RET_ALREADY_INIT
rcl_init() already called return code.
Definition: types.h:40
#define RCL_RET_OK
Success return code.
Definition: types.h:26
#define RCL_RET_BAD_ALLOC
Failed to allocate memory return code.
Definition: types.h:32
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
Definition: types.h:34
#define RCL_RET_ERROR
Unspecified error return code.
Definition: types.h:28
#define RCL_RET_SERVICE_TAKE_FAILED
Failed to take a request from the service return code.
Definition: types.h:84
#define RCL_RET_NODE_INVALID
Invalid rcl_node_t given return code.
Definition: types.h:56
#define RCL_RET_TIMEOUT
Timeout occurred return code.
Definition: types.h:30
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:23