@@ -74,6 +74,7 @@ class RosServiceNode : public BT::ActionNodeBase
7474public:
7575 // Type definitions
7676 using ServiceClient = typename rclcpp::Client<ServiceT>;
77+ using ServiceClientPtr = std::shared_ptr<ServiceClient>;
7778 using Request = typename ServiceT::Request;
7879 using Response = typename ServiceT::Response;
7980
@@ -140,17 +141,44 @@ class RosServiceNode : public BT::ActionNodeBase
140141 }
141142
142143protected:
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+
143174 std::shared_ptr<rclcpp::Node> node_;
144- std::string prev_service_name_ ;
175+ std::string service_name_ ;
145176 bool service_name_may_change_ = false ;
146177 const std::chrono::milliseconds service_timeout_;
147178 const std::chrono::milliseconds wait_for_service_timeout_;
148179
149180private:
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_;
154182 std::shared_future<typename Response::SharedPtr> future_response_;
155183
156184 rclcpp::Time time_request_sent_;
@@ -165,6 +193,18 @@ class RosServiceNode : public BT::ActionNodeBase
165193// ---------------------- DEFINITIONS -----------------------------
166194// ----------------------------------------------------------------
167195
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+
168208template <class T >
169209inline RosServiceNode<T>::RosServiceNode(const std::string& instance_name,
170210 const NodeConfig& conf,
@@ -227,34 +267,52 @@ inline bool RosServiceNode<T>::createClient(const std::string& service_name)
227267 throw RuntimeError (" service_name is empty" );
228268 }
229269
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;
237288
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_);
239291 if (!found)
240292 {
241293 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 ());
243295 }
244296 return found;
245297}
246298
247299template <class T >
248300inline NodeStatus RosServiceNode<T>::tick()
249301{
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
251309 // service_name in the port didn't change.
252310 // 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_))
254312 {
255313 std::string service_name;
256314 getInput (" service_name" , service_name);
257- if (prev_service_name_ != service_name)
315+ if (service_name_ != service_name)
258316 {
259317 createClient (service_name);
260318 }
@@ -287,18 +345,20 @@ inline NodeStatus RosServiceNode<T>::tick()
287345 }
288346
289347 // Check if server is ready
290- if (!service_client_->service_is_ready ())
348+ if (!srv_instance_->service_client ->service_is_ready ())
349+ {
291350 return onFailure (SERVICE_UNREACHABLE);
351+ }
292352
293- future_response_ = service_client_ ->async_send_request (request).share ();
353+ future_response_ = srv_instance_-> service_client ->async_send_request (request).share ();
294354 time_request_sent_ = node_->now ();
295355
296356 return NodeStatus::RUNNING;
297357 }
298358
299359 if (status () == NodeStatus::RUNNING)
300360 {
301- callback_group_executor_ .spin_some ();
361+ srv_instance_-> callback_executor .spin_some ();
302362
303363 // FIRST case: check if the goal request has a timeout
304364 if (!response_received_)
@@ -307,8 +367,8 @@ inline NodeStatus RosServiceNode<T>::tick()
307367 auto const timeout =
308368 rclcpp::Duration::from_seconds (double (service_timeout_.count ()) / 1000 );
309369
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);
312372
313373 if (ret != rclcpp::FutureReturnCode::SUCCESS)
314374 {
0 commit comments