ROS 2 rclcpp + rcl - rolling  rolling-e7363f10
ROS 2 C++ Client Library with ROS Client Library
service_event_publisher.c
1 // Copyright 2022 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 #include "rcl/service_event_publisher.h"
16 
17 #include <stdbool.h>
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <string.h>
21 
22 #include "rcl/allocator.h"
23 #include "rcl/error_handling.h"
24 #include "rcl/publisher.h"
25 #include "rcl/node.h"
26 #include "rcl/service_introspection.h"
27 #include "rcl/time.h"
28 #include "rcl/types.h"
29 #include "rcutils/logging_macros.h"
30 #include "rcutils/error_handling.h"
31 #include "rcutils/macros.h"
32 #include "rmw/error_handling.h"
33 #include "rmw/ret_types.h"
34 #include "service_msgs/msg/service_event_info.h"
35 
36 rcl_service_event_publisher_t rcl_get_zero_initialized_service_event_publisher(void)
37 {
38  // All members are initialized to 0 or NULL by C99 6.7.8/10.
39  static rcl_service_event_publisher_t zero_service_event_publisher;
40  return zero_service_event_publisher;
41 }
42 
43 bool
44 rcl_service_event_publisher_is_valid(const rcl_service_event_publisher_t * service_event_publisher)
45 {
46  RCL_CHECK_FOR_NULL_WITH_MSG(
47  service_event_publisher, "service_event_publisher is invalid", return false);
48  RCL_CHECK_FOR_NULL_WITH_MSG(
49  service_event_publisher->service_type_support,
50  "service_event_publisher's service type support is invalid", return false);
51  if (!rcl_clock_valid(service_event_publisher->clock)) {
52  RCL_SET_ERROR_MSG("service_event_publisher's clock is invalid");
53  return false;
54  }
55  return true;
56 }
57 
58 static rcl_ret_t introspection_create_publisher(
59  rcl_service_event_publisher_t * service_event_publisher,
60  const rcl_node_t * node)
61 {
62  rcl_allocator_t allocator = service_event_publisher->publisher_options.allocator;
63  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_ERROR);
64 
65  service_event_publisher->publisher = allocator.allocate(
66  sizeof(rcl_publisher_t), allocator.state);
67  RCL_CHECK_FOR_NULL_WITH_MSG(
68  service_event_publisher->publisher,
69  "allocate service_event_publisher failed in enable", return RCL_RET_BAD_ALLOC);
70  *service_event_publisher->publisher = rcl_get_zero_initialized_publisher();
72  service_event_publisher->publisher, node,
73  service_event_publisher->service_type_support->event_typesupport,
74  service_event_publisher->service_event_topic_name,
75  &service_event_publisher->publisher_options);
76  if (RCL_RET_OK != ret) {
77  allocator.deallocate(service_event_publisher->publisher, allocator.state);
78  service_event_publisher->publisher = NULL;
79  rcutils_reset_error();
80  RCL_SET_ERROR_MSG(rcl_get_error_string().str);
81  return ret;
82  }
83 
84  return RCL_RET_OK;
85 }
86 
87 rcl_ret_t rcl_service_event_publisher_init(
88  rcl_service_event_publisher_t * service_event_publisher,
89  const rcl_node_t * node,
90  rcl_clock_t * clock,
91  const rcl_publisher_options_t publisher_options,
92  const char * service_name,
93  const rosidl_service_type_support_t * service_type_support)
94 {
95  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
96  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ALREADY_INIT);
97  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
98  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
99  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
100  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_TOPIC_NAME_INVALID);
101  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_PUBLISHER_INVALID);
102 
103  RCL_CHECK_ARGUMENT_FOR_NULL(service_event_publisher, RCL_RET_INVALID_ARGUMENT);
104  RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT);
105  RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
106  RCL_CHECK_ARGUMENT_FOR_NULL(service_type_support, RCL_RET_INVALID_ARGUMENT);
107 
109  &publisher_options.allocator,
110  "allocator is invalid", return RCL_RET_ERROR);
111 
112  rcl_allocator_t allocator = publisher_options.allocator;
113 
114  rcl_ret_t ret = RCL_RET_OK;
115 
116  if (!rcl_node_is_valid(node)) {
117  return RCL_RET_NODE_INVALID;
118  }
119 
120  if (!rcl_clock_valid(clock)) {
121  rcutils_reset_error();
122  RCL_SET_ERROR_MSG("clock is invalid");
123  return RCL_RET_ERROR;
124  }
125 
126  RCUTILS_LOG_DEBUG_NAMED(
127  ROS_PACKAGE_NAME, "Initializing service introspection for service name '%s'", service_name);
128 
129  // Typesupports have static lifetimes
130  service_event_publisher->service_type_support = service_type_support;
131  service_event_publisher->clock = clock;
132  service_event_publisher->publisher_options = publisher_options;
133 
134  size_t topic_length = strlen(service_name) + strlen(RCL_SERVICE_INTROSPECTION_TOPIC_POSTFIX) + 1;
135  service_event_publisher->service_event_topic_name = (char *) allocator.allocate(
136  topic_length, allocator.state);
137  RCL_CHECK_FOR_NULL_WITH_MSG(
138  service_event_publisher->service_event_topic_name,
139  "allocating memory for service introspection topic name failed",
140  return RCL_RET_BAD_ALLOC;);
141 
142  snprintf(
143  service_event_publisher->service_event_topic_name,
144  topic_length,
145  "%s%s", service_name, RCL_SERVICE_INTROSPECTION_TOPIC_POSTFIX);
146 
147  ret = introspection_create_publisher(service_event_publisher, node);
148  if (ret != RCL_RET_OK) {
149  goto free_topic_name;
150  }
151 
152  RCUTILS_LOG_DEBUG_NAMED(
153  ROS_PACKAGE_NAME, "Service introspection for service '%s' initialized", service_name);
154 
155  return RCL_RET_OK;
156 
157 free_topic_name:
158  allocator.deallocate(service_event_publisher->service_event_topic_name, allocator.state);
159 
160  return ret;
161 }
162 
163 rcl_ret_t rcl_service_event_publisher_fini(
164  rcl_service_event_publisher_t * service_event_publisher,
165  rcl_node_t * node)
166 {
167  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
168  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ALREADY_SHUTDOWN);
169  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
170  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_PUBLISHER_INVALID);
171  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
172  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
173 
174  if (!rcl_service_event_publisher_is_valid(service_event_publisher)) {
175  return RCL_RET_ERROR;
176  }
177 
179  return RCL_RET_NODE_INVALID;
180  }
181 
182  rcl_allocator_t allocator = service_event_publisher->publisher_options.allocator;
183  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_ERROR);
184 
185  if (service_event_publisher->publisher) {
186  rcl_ret_t ret = rcl_publisher_fini(service_event_publisher->publisher, node);
187  allocator.deallocate(service_event_publisher->publisher, allocator.state);
188  service_event_publisher->publisher = NULL;
189  if (RCL_RET_OK != ret) {
190  return ret;
191  }
192  }
193 
194  allocator.deallocate(service_event_publisher->service_event_topic_name, allocator.state);
195  service_event_publisher->service_event_topic_name = NULL;
196 
197  return RCL_RET_OK;
198 }
199 
200 rcl_ret_t rcl_send_service_event_message(
201  const rcl_service_event_publisher_t * service_event_publisher,
202  const uint8_t event_type,
203  const void * ros_response_request,
204  const int64_t sequence_number,
205  const uint8_t guid[16])
206 {
207  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
208  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_PUBLISHER_INVALID);
209  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
210  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
211 
212  RCL_CHECK_ARGUMENT_FOR_NULL(ros_response_request, RCL_RET_INVALID_ARGUMENT);
213  RCL_CHECK_FOR_NULL_WITH_MSG(guid, "guid is NULL", return RCL_RET_INVALID_ARGUMENT);
214 
215  if (!rcl_service_event_publisher_is_valid(service_event_publisher)) {
216  return RCL_RET_ERROR;
217  }
218 
219  if (service_event_publisher->introspection_state == RCL_SERVICE_INTROSPECTION_OFF) {
220  return RCL_RET_ERROR;
221  }
222 
223  rcl_allocator_t allocator = service_event_publisher->publisher_options.allocator;
224  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
225 
226  if (!rcl_publisher_is_valid(service_event_publisher->publisher)) {
228  }
229 
230  rcl_ret_t ret;
231 
233  ret = rcl_clock_get_now(service_event_publisher->clock, &now);
234  if (RMW_RET_OK != ret) {
235  rcutils_reset_error();
236  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
237  return RCL_RET_ERROR;
238  }
239 
240  rosidl_service_introspection_info_t info = {
241  .event_type = event_type,
242  .stamp_sec = (int32_t)RCL_NS_TO_S(now),
243  .stamp_nanosec = now % (1000LL * 1000LL * 1000LL),
244  .sequence_number = sequence_number,
245  };
246 
247  memcpy(info.client_gid, guid, 16);
248 
249  void * service_introspection_message;
250  if (service_event_publisher->introspection_state == RCL_SERVICE_INTROSPECTION_METADATA) {
251  ros_response_request = NULL;
252  }
253  switch (event_type) {
254  case service_msgs__msg__ServiceEventInfo__REQUEST_RECEIVED:
255  case service_msgs__msg__ServiceEventInfo__REQUEST_SENT:
256  service_introspection_message =
257  service_event_publisher->service_type_support->event_message_create_handle_function(
258  &info, &allocator, ros_response_request, NULL);
259  break;
260  case service_msgs__msg__ServiceEventInfo__RESPONSE_RECEIVED:
261  case service_msgs__msg__ServiceEventInfo__RESPONSE_SENT:
262  service_introspection_message =
263  service_event_publisher->service_type_support->event_message_create_handle_function(
264  &info, &allocator, NULL, ros_response_request);
265  break;
266  default:
267  rcutils_reset_error();
268  RCL_SET_ERROR_MSG("unsupported event type");
269  return RCL_RET_ERROR;
270  }
271  RCL_CHECK_FOR_NULL_WITH_MSG(
272  service_introspection_message, "service_introspection_message is NULL", return RCL_RET_ERROR);
273 
274  // and publish it out!
275  ret = rcl_publish(service_event_publisher->publisher, service_introspection_message, NULL);
276  // clean up before error checking
277  service_event_publisher->service_type_support->event_message_destroy_handle_function(
278  service_introspection_message, &allocator);
279  if (RCL_RET_OK != ret) {
280  rcutils_reset_error();
281  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
282  }
283 
284  return ret;
285 }
286 
287 rcl_ret_t
288 rcl_service_event_publisher_change_state(
289  rcl_service_event_publisher_t * service_event_publisher,
290  rcl_service_introspection_state_t introspection_state)
291 {
292  if (!rcl_service_event_publisher_is_valid(service_event_publisher)) {
293  return RCL_RET_ERROR;
294  }
295 
296  service_event_publisher->introspection_state = introspection_state;
297 
298  return RCL_RET_OK;
299 }
#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:406
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:396
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_publisher_init(rcl_publisher_t *publisher, const rcl_node_t *node, const rosidl_message_type_support_t *type_support, const char *topic_name, const rcl_publisher_options_t *options)
Initialize a rcl publisher.
Definition: publisher.c:56
RCL_PUBLIC bool rcl_publisher_is_valid(const rcl_publisher_t *publisher)
Return true if the publisher is valid, otherwise false.
Definition: publisher.c:426
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_publish(const rcl_publisher_t *publisher, const void *ros_message, rmw_publisher_allocation_t *allocation)
Publish a ROS message on a topic using a publisher.
Definition: publisher.c:279
RCL_PUBLIC RCL_WARN_UNUSED rcl_publisher_t rcl_get_zero_initialized_publisher(void)
Return a rcl_publisher_t struct with members set to NULL.
Definition: publisher.c:48
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_publisher_fini(rcl_publisher_t *publisher, rcl_node_t *node)
Finalize a rcl_publisher_t.
Definition: publisher.c:187
Encapsulation of a time source.
Definition: time.h:141
Structure which encapsulates a ROS Node.
Definition: node.h:49
Options available for a rcl publisher.
Definition: publisher.h:51
rcl_allocator_t allocator
Custom allocator for the publisher, used for incidental allocations.
Definition: publisher.h:56
Structure which encapsulates a ROS Publisher.
Definition: publisher.h:44
char * service_event_topic_name
Name of service introspection topic: <service_name>/<RCL_SERVICE_INTROSPECTION_TOPIC_POSTFIX>
const rosidl_service_type_support_t * service_type_support
Handle to service typesupport.
rcl_publisher_options_t publisher_options
Publisher options for service event publisher.
rcl_service_introspection_state_t introspection_state
Current state of introspection; off, metadata, or contents.
rcl_publisher_t * publisher
Handle to publisher for publishing service events.
rcl_clock_t * clock
Handle to clock for timestamping service events.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_clock_get_now(rcl_clock_t *clock, rcl_time_point_value_t *time_point_value)
Fill the time point value with the current value of the associated clock.
Definition: time.c:263
#define RCL_NS_TO_S
Convenience macro to convert nanoseconds to seconds.
Definition: time.h:42
rcutils_time_point_value_t rcl_time_point_value_t
A single point in time, measured in nanoseconds since the Unix epoch.
Definition: time.h:49
RCL_PUBLIC RCL_WARN_UNUSED bool rcl_clock_valid(rcl_clock_t *clock)
Check if the clock has valid values.
Definition: time.c:88
#define RCL_RET_ALREADY_INIT
rcl_init() already called return code.
Definition: types.h:42
#define RCL_RET_OK
Success return code.
Definition: types.h:28
#define RCL_RET_BAD_ALLOC
Failed to allocate memory return code.
Definition: types.h:34
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
Definition: types.h:36
#define RCL_RET_ERROR
Unspecified error return code.
Definition: types.h:30
#define RCL_RET_NODE_INVALID
Invalid rcl_node_t given return code.
Definition: types.h:60
#define RCL_RET_TOPIC_NAME_INVALID
Topic name does not pass validation.
Definition: types.h:48
#define RCL_RET_ALREADY_SHUTDOWN
rcl_shutdown() already called return code.
Definition: types.h:54
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:25
#define RCL_RET_PUBLISHER_INVALID
Invalid rcl_publisher_t given return code.
Definition: types.h:70