generic_client.cpp
Go to the documentation of this file.
1 #include <future>
2 #include <iostream>
3 
4 #include <rclcpp/client.hpp>
5 #include <rclcpp/serialized_message.hpp>
6 #include <rclcpp/typesupport_helpers.hpp>
7 #include <rosidl_typesupport_introspection_cpp/field_types.hpp>
8 #include <rosidl_typesupport_introspection_cpp/service_introspection.hpp>
9 
11 
12 namespace {
13 
14 // Copy of github.com/ros2/rclcpp/blob/33dae5d67/rclcpp/src/rclcpp/typesupport_helpers.cpp#L69-L92
15 static std::tuple<std::string, std::string, std::string> extract_type_identifier(
16  const std::string& full_type) {
17  char type_separator = '/';
18  auto sep_position_back = full_type.find_last_of(type_separator);
19  auto sep_position_front = full_type.find_first_of(type_separator);
20  if (sep_position_back == std::string::npos || sep_position_back == 0 ||
21  sep_position_back == full_type.length() - 1) {
22  throw std::runtime_error(
23  "Message type is not of the form package/type and cannot be processed");
24  }
25 
26  std::string package_name = full_type.substr(0, sep_position_front);
27  std::string middle_module = "";
28  if (sep_position_back - sep_position_front > 0) {
29  middle_module =
30  full_type.substr(sep_position_front + 1, sep_position_back - sep_position_front - 1);
31  }
32  std::string type_name = full_type.substr(sep_position_back + 1);
33 
34  return std::make_tuple(package_name, middle_module, type_name);
35 }
36 } // namespace
37 
38 namespace foxglove_bridge {
39 
40 constexpr char TYPESUPPORT_INTROSPECTION_LIB_NAME[] = "rosidl_typesupport_introspection_cpp";
41 constexpr char TYPESUPPORT_LIB_NAME[] = "rosidl_typesupport_cpp";
42 using rosidl_typesupport_introspection_cpp::MessageMembers;
43 using rosidl_typesupport_introspection_cpp::ServiceMembers;
44 
45 std::shared_ptr<void> allocate_message(const MessageMembers* members) {
46  void* buffer = malloc(members->size_of_);
47  if (buffer == nullptr) {
48  throw std::runtime_error("Failed to allocate memory");
49  }
50  memset(buffer, 0, members->size_of_);
51  members->init_function(buffer, rosidl_runtime_cpp::MessageInitialization::ALL);
52  return std::shared_ptr<void>(buffer, free);
53 }
54 
55 std::string getTypeIntrospectionSymbolName(const std::string& serviceType) {
56  const auto [pkgName, middleModule, typeName] = extract_type_identifier(serviceType);
57 
58  return std::string(TYPESUPPORT_INTROSPECTION_LIB_NAME) + "__get_service_type_support_handle__" +
59  pkgName + "__" + (middleModule.empty() ? "srv" : middleModule) + "__" + typeName;
60 }
61 
80 std::string getServiceTypeSupportHandleSymbolName(const std::string& serviceType) {
81  const auto [pkgName, middleModule, typeName] = extract_type_identifier(serviceType);
82  const auto lengthPrefixedString = [](const std::string& s) {
83  return std::to_string(s.size()) + s;
84  };
85 
86  return "_ZN" + lengthPrefixedString(TYPESUPPORT_LIB_NAME) +
87  lengthPrefixedString("get_service_type_support_handle") + "IN" +
88  lengthPrefixedString(pkgName) +
89  lengthPrefixedString(middleModule.empty() ? "srv" : middleModule) +
90  lengthPrefixedString(typeName) + "EEEPK" +
91  lengthPrefixedString("rosidl_service_type_support_t") + "v";
92 }
93 
94 GenericClient::GenericClient(rclcpp::node_interfaces::NodeBaseInterface* nodeBase,
95  rclcpp::node_interfaces::NodeGraphInterface::SharedPtr nodeGraph,
96  std::string serviceName, std::string serviceType,
97  rcl_client_options_t& client_options)
98  : rclcpp::ClientBase(nodeBase, nodeGraph) {
99  const auto [pkgName, middleModule, typeName] = extract_type_identifier(serviceType);
100  const auto requestTypeName = serviceType + "_Request";
101  const auto responseTypeName = serviceType + "_Response";
102 
103  _typeSupportLib = rclcpp::get_typesupport_library(serviceType, TYPESUPPORT_LIB_NAME);
105  rclcpp::get_typesupport_library(serviceType, TYPESUPPORT_INTROSPECTION_LIB_NAME);
107  throw std::runtime_error("Failed to load shared library for service type " + serviceType);
108  }
109 
110  const auto typesupportSymbolName = getServiceTypeSupportHandleSymbolName(serviceType);
111  if (!_typeSupportLib->has_symbol(typesupportSymbolName)) {
112  throw std::runtime_error("Failed to find symbol '" + typesupportSymbolName + "' in " +
113  _typeSupportLib->get_library_path());
114  }
115 
116  const rosidl_service_type_support_t* (*get_ts)() = nullptr;
118  (reinterpret_cast<decltype(get_ts)>(_typeSupportLib->get_symbol(typesupportSymbolName)))();
119 
120  const auto typeinstrospection_symbol_name = getTypeIntrospectionSymbolName(serviceType);
121 
122  // This will throw runtime_error if the symbol was not found.
123  _typeIntrospectionHdl = (reinterpret_cast<decltype(get_ts)>(
124  _typeIntrospectionLib->get_symbol(typeinstrospection_symbol_name)))();
125 
127  rclcpp::get_typesupport_handle(requestTypeName, TYPESUPPORT_LIB_NAME, *_typeSupportLib);
129  rclcpp::get_typesupport_handle(responseTypeName, TYPESUPPORT_LIB_NAME, *_typeSupportLib);
130 
131  rcl_ret_t ret = rcl_client_init(this->get_client_handle().get(), this->get_rcl_node_handle(),
132  _serviceTypeSupportHdl, serviceName.c_str(), &client_options);
133  if (ret != RCL_RET_OK) {
134  if (ret == RCL_RET_SERVICE_NAME_INVALID) {
135  auto rcl_node_handle = this->get_rcl_node_handle();
136  // this will throw on any validation problem
137  rcl_reset_error();
138  rclcpp::expand_topic_or_service_name(serviceName, rcl_node_get_name(rcl_node_handle),
139  rcl_node_get_namespace(rcl_node_handle), true);
140  }
141  rclcpp::exceptions::throw_from_rcl_error(ret, "could not create client");
142  }
143 }
144 
145 std::shared_ptr<void> GenericClient::create_response() {
146  auto srv_members = static_cast<const ServiceMembers*>(_typeIntrospectionHdl->data);
147  return allocate_message(srv_members->response_members_);
148 }
149 
150 std::shared_ptr<rmw_request_id_t> GenericClient::create_request_header() {
151  return std::shared_ptr<rmw_request_id_t>(new rmw_request_id_t);
152 }
153 
154 void GenericClient::handle_response(std::shared_ptr<rmw_request_id_t> request_header,
155  std::shared_ptr<void> response) {
156  std::unique_lock<std::mutex> lock(pending_requests_mutex_);
157  int64_t sequence_number = request_header->sequence_number;
158 
159  auto ser_response = std::make_shared<rclcpp::SerializedMessage>();
160  rmw_ret_t r = rmw_serialize(response.get(), _responseTypeSupportHdl,
161  &ser_response->get_rcl_serialized_message());
162  if (r != RMW_RET_OK) {
163  RCUTILS_LOG_ERROR_NAMED("foxglove_bridge", "Failed to serialize service response. Ignoring...");
164  return;
165  }
166 
167  // TODO(esteve) this should throw instead since it is not expected to happen in the first place
168  if (this->pending_requests_.count(sequence_number) == 0) {
169  RCUTILS_LOG_ERROR_NAMED("foxglove_bridge", "Received invalid sequence number. Ignoring...");
170  return;
171  }
172  auto tuple = this->pending_requests_[sequence_number];
173  auto call_promise = std::get<0>(tuple);
174  auto callback = std::get<1>(tuple);
175  auto future = std::get<2>(tuple);
176  this->pending_requests_.erase(sequence_number);
177  // Unlock here to allow the service to be called recursively from one of its callbacks.
178  lock.unlock();
179 
180  call_promise->set_value(ser_response);
181  callback(future);
182 }
183 
185  return async_send_request(request, [](SharedFuture) {});
186 }
187 
189  CallbackType&& cb) {
190  std::lock_guard<std::mutex> lock(pending_requests_mutex_);
191  int64_t sequence_number;
192 
193  auto srv_members = static_cast<const ServiceMembers*>(_typeIntrospectionHdl->data);
194  auto buf = allocate_message(srv_members->request_members_);
195 
196  const rmw_serialized_message_t* sm = &request->get_rcl_serialized_message();
197  if (const auto ret = rmw_deserialize(sm, _requestTypeSupportHdl, buf.get()) != RCL_RET_OK) {
198  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to desirialize request");
199  }
200  rcl_ret_t ret = rcl_send_request(get_client_handle().get(), buf.get(), &sequence_number);
201  if (RCL_RET_OK != ret) {
202  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
203  }
204 
205  SharedPromise call_promise = std::make_shared<Promise>();
206  SharedFuture f(call_promise->get_future());
207  pending_requests_[sequence_number] =
208  std::make_tuple(call_promise, std::forward<CallbackType>(cb), f);
209  return f;
210 }
211 
212 } // namespace foxglove_bridge
std::string getServiceTypeSupportHandleSymbolName(const std::string &serviceType)
std::string getTypeIntrospectionSymbolName(const std::string &serviceType)
const rosidl_service_type_support_t * _serviceTypeSupportHdl
f
std::shared_future< SharedResponse > SharedFuture
XmlRpcServer s
const rosidl_message_type_support_t * _responseTypeSupportHdl
std::function< void(SharedFuture)> CallbackType
void handle_response(std::shared_ptr< rmw_request_id_t > request_header, std::shared_ptr< void > response) override
std::shared_ptr< void > create_response() override
constexpr char TYPESUPPORT_LIB_NAME[]
const rosidl_service_type_support_t * _typeIntrospectionHdl
std::shared_ptr< rclcpp::SerializedMessage > SharedRequest
SharedFuture async_send_request(SharedRequest request)
std::shared_ptr< void > allocate_message(const MessageMembers *members)
GenericClient(rclcpp::node_interfaces::NodeBaseInterface *node_base, rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph, std::string service_name, std::string service_type, rcl_client_options_t &client_options)
std::shared_ptr< rcpputils::SharedLibrary > _typeSupportLib
std::map< int64_t, std::tuple< SharedPromise, CallbackType, SharedFuture > > pending_requests_
std::shared_ptr< rmw_request_id_t > create_request_header() override
const rosidl_message_type_support_t * _requestTypeSupportHdl
std::shared_ptr< rcpputils::SharedLibrary > _typeIntrospectionLib
std::shared_ptr< Promise > SharedPromise
constexpr char TYPESUPPORT_INTROSPECTION_LIB_NAME[]


foxglove_bridge
Author(s): Foxglove
autogenerated on Mon Jul 3 2023 02:12:22