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


foxglove_bridge
Author(s): Foxglove
autogenerated on Tue May 20 2025 02:34:26