ROS 2 rclcpp + rcl - kilted  kilted
ROS 2 C++ Client Library with ROS Client Library
generic_client.cpp
1 // Copyright 2023 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 <future>
16 
17 #include "rclcpp/generic_client.hpp"
18 #include "rclcpp/typesupport_helpers.hpp"
19 
20 #include "rosidl_runtime_c/service_type_support_struct.h"
21 #include "rosidl_typesupport_introspection_cpp/identifier.hpp"
22 #include "rosidl_typesupport_introspection_cpp/service_introspection.hpp"
23 
24 namespace rclcpp
25 {
26 GenericClient::GenericClient(
28  rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
29  const std::string & service_name,
30  const std::string & service_type,
31  rcl_client_options_t & client_options)
32 : ClientBase(node_base, node_graph)
33 {
34  const rosidl_service_type_support_t * service_ts;
35  try {
36  ts_lib_ = get_typesupport_library(
37  service_type, "rosidl_typesupport_cpp");
38 
39  service_ts = get_service_typesupport_handle(
40  service_type, "rosidl_typesupport_cpp", *ts_lib_);
41 
42  auto response_type_support_intro = get_message_typesupport_handle(
43  service_ts->response_typesupport,
44  rosidl_typesupport_introspection_cpp::typesupport_identifier);
45  response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
46  response_type_support_intro->data);
47  } catch (std::runtime_error & err) {
48  RCLCPP_ERROR(
49  rclcpp::get_node_logger(node_handle_.get()).get_child("rclcpp"),
50  "Invalid service type: %s",
51  err.what());
53  }
54 
56  this->get_client_handle().get(),
57  this->get_rcl_node_handle(),
58  service_ts,
59  service_name.c_str(),
60  &client_options);
61  if (ret != RCL_RET_OK) {
62  if (ret == RCL_RET_SERVICE_NAME_INVALID) {
63  auto rcl_node_handle = this->get_rcl_node_handle();
64  // this will throw on any validation problem
65  rcl_reset_error();
67  service_name,
68  rcl_node_get_name(rcl_node_handle),
69  rcl_node_get_namespace(rcl_node_handle),
70  true);
71  }
72  rclcpp::exceptions::throw_from_rcl_error(ret, "could not create generic client");
73  }
74 }
75 
76 std::shared_ptr<void>
77 GenericClient::create_response()
78 {
79  void * response = new uint8_t[response_members_->size_of_];
80  response_members_->init_function(response, rosidl_runtime_cpp::MessageInitialization::ZERO);
81  return std::shared_ptr<void>(
82  response,
83  [this](void * p)
84  {
85  response_members_->fini_function(p);
86  delete[] reinterpret_cast<uint8_t *>(p);
87  });
88 }
89 
90 std::shared_ptr<rmw_request_id_t>
91 GenericClient::create_request_header()
92 {
93  // TODO(wjwwood): This should probably use rmw_request_id's allocator.
94  // (since it is a C type)
95  return std::shared_ptr<rmw_request_id_t>(new rmw_request_id_t);
96 }
97 
98 void
99 GenericClient::handle_response(
100  std::shared_ptr<rmw_request_id_t> request_header,
101  std::shared_ptr<void> response)
102 {
103  auto optional_pending_request =
104  this->get_and_erase_pending_request(request_header->sequence_number);
105  if (!optional_pending_request) {
106  return;
107  }
108  auto & value = *optional_pending_request;
109  if (std::holds_alternative<Promise>(value)) {
110  auto & promise = std::get<Promise>(value);
111  promise.set_value(std::move(response));
112  } else if (std::holds_alternative<CallbackTypeValueVariant>(value)) {
113  auto & inner = std::get<CallbackTypeValueVariant>(value);
114  const auto & callback = std::get<CallbackType>(inner);
115  auto & promise = std::get<Promise>(inner);
116  auto & future = std::get<SharedFuture>(inner);
117  promise.set_value(std::move(response));
118  callback(std::move(future));
119  }
120 }
121 
122 size_t
123 GenericClient::prune_pending_requests()
124 {
125  std::lock_guard guard(pending_requests_mutex_);
126  auto ret = pending_requests_.size();
127  pending_requests_.clear();
128  return ret;
129 }
130 
131 bool
132 GenericClient::remove_pending_request(int64_t request_id)
133 {
134  std::lock_guard guard(pending_requests_mutex_);
135  return pending_requests_.erase(request_id) != 0u;
136 }
137 
138 bool
139 GenericClient::remove_pending_request(const FutureAndRequestId & future)
140 {
141  return this->remove_pending_request(future.request_id);
142 }
143 
144 bool
145 GenericClient::remove_pending_request(const SharedFutureAndRequestId & future)
146 {
147  return this->remove_pending_request(future.request_id);
148 }
149 
150 std::optional<GenericClient::CallbackInfoVariant>
151 GenericClient::get_and_erase_pending_request(int64_t request_number)
152 {
153  std::unique_lock<std::mutex> lock(pending_requests_mutex_);
154  auto it = pending_requests_.find(request_number);
155  if (it == pending_requests_.end()) {
156  RCUTILS_LOG_DEBUG_NAMED(
157  "rclcpp",
158  "Received invalid sequence number. Ignoring...");
159  return std::nullopt;
160  }
161  auto value = std::move(it->second.second);
162  pending_requests_.erase(request_number);
163  return value;
164 }
165 
166 GenericClient::FutureAndRequestId
167 GenericClient::async_send_request(const Request request)
168 {
169  Promise promise;
170  auto future = promise.get_future();
171  auto req_id = async_send_request_impl(
172  request,
173  std::move(promise));
174  return FutureAndRequestId(std::move(future), req_id);
175 }
176 
177 int64_t
178 GenericClient::async_send_request_impl(const Request request, CallbackInfoVariant value)
179 {
180  int64_t sequence_number;
181  std::lock_guard<std::mutex> lock(pending_requests_mutex_);
182  rcl_ret_t ret = rcl_send_request(get_client_handle().get(), request, &sequence_number);
183  if (RCL_RET_OK != ret) {
184  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
185  }
186  pending_requests_.try_emplace(
187  sequence_number,
188  std::make_pair(std::chrono::system_clock::now(), std::move(value)));
189  return sequence_number;
190 }
191 
192 } // namespace rclcpp
RCLCPP_PUBLIC Logger get_child(const std::string &suffix)
Return a logger that is a descendant of this logger.
Definition: logger.cpp:101
Pure virtual interface class for the NodeBase part of the Node API.
RCL_PUBLIC RCL_WARN_UNUSED 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)
Initialize a rcl client.
Definition: client.c:87
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_send_request(const rcl_client_t *client, const void *ros_request, int64_t *sequence_number)
Send a ROS request using a client.
Definition: client.c:318
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:45
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:408
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:417
Options available for a rcl_client_t.
Definition: client.h:50
A convenient GenericClient::Future and request id pair.
A convenient GenericClient::SharedFuture and request id pair.
#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
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24