@@ -74,6 +74,7 @@ class RosServiceNode : public BT::ActionNodeBase
74
74
public:
75
75
// Type definitions
76
76
using ServiceClient = typename rclcpp::Client<ServiceT>;
77
+ using ServiceClientPtr = std::shared_ptr<ServiceClient>;
77
78
using Request = typename ServiceT::Request;
78
79
using Response = typename ServiceT::Response;
79
80
@@ -140,17 +141,44 @@ class RosServiceNode : public BT::ActionNodeBase
140
141
}
141
142
142
143
protected:
144
+ struct ServiceClientInstance
145
+ {
146
+ ServiceClientInstance (std::shared_ptr<rclcpp::Node> node,
147
+ const std::string& service_name);
148
+
149
+ ServiceClientPtr service_client;
150
+ rclcpp::CallbackGroup::SharedPtr callback_group;
151
+ rclcpp::executors::SingleThreadedExecutor callback_executor;
152
+ };
153
+
154
+ static std::mutex& getMutex ()
155
+ {
156
+ static std::mutex action_client_mutex;
157
+ return action_client_mutex;
158
+ }
159
+
160
+ rclcpp::Logger logger ()
161
+ {
162
+ return node_->get_logger ();
163
+ }
164
+
165
+ using ClientsRegistry =
166
+ std::unordered_map<std::string, std::weak_ptr<ServiceClientInstance>>;
167
+ // contains the fully-qualified name of the node and the name of the client
168
+ static ClientsRegistry& getRegistry ()
169
+ {
170
+ static ClientsRegistry clients_registry;
171
+ return clients_registry;
172
+ }
173
+
143
174
std::shared_ptr<rclcpp::Node> node_;
144
- std::string prev_service_name_ ;
175
+ std::string service_name_ ;
145
176
bool service_name_may_change_ = false ;
146
177
const std::chrono::milliseconds service_timeout_;
147
178
const std::chrono::milliseconds wait_for_service_timeout_;
148
179
149
180
private:
150
- typename std::shared_ptr<ServiceClient> service_client_;
151
- rclcpp::CallbackGroup::SharedPtr callback_group_;
152
- rclcpp::executors::SingleThreadedExecutor callback_group_executor_;
153
-
181
+ std::shared_ptr<ServiceClientInstance> srv_instance_;
154
182
std::shared_future<typename Response::SharedPtr> future_response_;
155
183
156
184
rclcpp::Time time_request_sent_;
@@ -165,6 +193,18 @@ class RosServiceNode : public BT::ActionNodeBase
165
193
// ---------------------- DEFINITIONS -----------------------------
166
194
// ----------------------------------------------------------------
167
195
196
+ template <class T >
197
+ inline RosServiceNode<T>::ServiceClientInstance::ServiceClientInstance(
198
+ std::shared_ptr<rclcpp::Node> node, const std::string& service_name)
199
+ {
200
+ callback_group =
201
+ node->create_callback_group (rclcpp::CallbackGroupType::MutuallyExclusive, false );
202
+ callback_executor.add_callback_group (callback_group, node->get_node_base_interface ());
203
+
204
+ service_client = node->create_client <T>(service_name, rmw_qos_profile_services_default,
205
+ callback_group);
206
+ }
207
+
168
208
template <class T >
169
209
inline RosServiceNode<T>::RosServiceNode(const std::string& instance_name,
170
210
const NodeConfig& conf,
@@ -227,34 +267,52 @@ inline bool RosServiceNode<T>::createClient(const std::string& service_name)
227
267
throw RuntimeError (" service_name is empty" );
228
268
}
229
269
230
- callback_group_ =
231
- node_->create_callback_group (rclcpp::CallbackGroupType::MutuallyExclusive);
232
- callback_group_executor_.add_callback_group (callback_group_,
233
- node_->get_node_base_interface ());
234
- service_client_ = node_->create_client <T>(
235
- service_name, rmw_qos_profile_services_default, callback_group_);
236
- prev_service_name_ = service_name;
270
+ std::unique_lock lk (getMutex ());
271
+ auto client_key = std::string (node_->get_fully_qualified_name ()) + " /" + service_name;
272
+
273
+ auto & registry = getRegistry ();
274
+ auto it = registry.find (client_key);
275
+ if (it == registry.end () || it->second .expired ()
276
+ {
277
+ srv_instance_ = std::make_shared<ServiceClientInstance>(node_, service_name);
278
+ registry.insert ({ client_key, srv_instance_ });
279
+
280
+ RCLCPP_INFO (logger (), " Node [%s] created service client [%s]" , name ().c_str (),
281
+ service_name.c_str ());
282
+ }
283
+ else
284
+ {
285
+ auto prev_instance srv_instance_ = it->second .lock ();
286
+ }
287
+ service_name_ = service_name;
237
288
238
- bool found = service_client_->wait_for_service (wait_for_service_timeout_);
289
+ bool found =
290
+ srv_instance_->service_client ->wait_for_service (wait_for_service_timeout_);
239
291
if (!found)
240
292
{
241
293
RCLCPP_ERROR (node_->get_logger (), " %s: Service with name '%s' is not reachable." ,
242
- name ().c_str (), prev_service_name_ .c_str ());
294
+ name ().c_str (), service_name_ .c_str ());
243
295
}
244
296
return found;
245
297
}
246
298
247
299
template <class T >
248
300
inline NodeStatus RosServiceNode<T>::tick()
249
301
{
250
- // First, check if the service_client_ is valid and that the name of the
302
+ if (!rclcpp::ok ())
303
+ {
304
+ halt ();
305
+ return NodeStatus::FAILURE;
306
+ }
307
+
308
+ // First, check if the service_client is valid and that the name of the
251
309
// service_name in the port didn't change.
252
310
// otherwise, create a new client
253
- if (!service_client_ || (status () == NodeStatus::IDLE && service_name_may_change_))
311
+ if (!srv_instance_ || (status () == NodeStatus::IDLE && service_name_may_change_))
254
312
{
255
313
std::string service_name;
256
314
getInput (" service_name" , service_name);
257
- if (prev_service_name_ != service_name)
315
+ if (service_name_ != service_name)
258
316
{
259
317
createClient (service_name);
260
318
}
@@ -287,18 +345,20 @@ inline NodeStatus RosServiceNode<T>::tick()
287
345
}
288
346
289
347
// Check if server is ready
290
- if (!service_client_->service_is_ready ())
348
+ if (!srv_instance_->service_client ->service_is_ready ())
349
+ {
291
350
return onFailure (SERVICE_UNREACHABLE);
351
+ }
292
352
293
- future_response_ = service_client_ ->async_send_request (request).share ();
353
+ future_response_ = srv_instance_-> service_client ->async_send_request (request).share ();
294
354
time_request_sent_ = node_->now ();
295
355
296
356
return NodeStatus::RUNNING;
297
357
}
298
358
299
359
if (status () == NodeStatus::RUNNING)
300
360
{
301
- callback_group_executor_ .spin_some ();
361
+ srv_instance_-> callback_executor .spin_some ();
302
362
303
363
// FIRST case: check if the goal request has a timeout
304
364
if (!response_received_)
@@ -307,8 +367,8 @@ inline NodeStatus RosServiceNode<T>::tick()
307
367
auto const timeout =
308
368
rclcpp::Duration::from_seconds (double (service_timeout_.count ()) / 1000 );
309
369
310
- auto ret =
311
- callback_group_executor_. spin_until_future_complete ( future_response_, nodelay);
370
+ auto ret = srv_instance_-> callback_executor . spin_until_future_complete (
371
+ future_response_, nodelay);
312
372
313
373
if (ret != rclcpp::FutureReturnCode::SUCCESS)
314
374
{
0 commit comments