Skip to content

Commit fa151bc

Browse files
committed
merging changes from PR BehaviorTree#46: registry for service clients
1 parent 3b399da commit fa151bc

File tree

3 files changed

+84
-24
lines changed

3 files changed

+84
-24
lines changed

behaviortree_ros2/include/behaviortree_ros2/bt_action_node.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ inline bool RosActionNode<T>::createClient(const std::string& action_name)
306306

307307
auto& registry = getRegistry();
308308
auto it = registry.find(action_client_key_);
309-
if(it == registry.end())
309+
if(it == registry.end() || it->second.expired())
310310
{
311311
client_instance_ = std::make_shared<ActionClientInstance>(node_, action_name);
312312
registry.insert({ action_client_key_, client_instance_ });

behaviortree_ros2/include/behaviortree_ros2/bt_service_node.hpp

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class RosServiceNode : public BT::ActionNodeBase
7474
public:
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

142143
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+
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

149180
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_;
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+
168208
template <class T>
169209
inline 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

247299
template <class T>
248300
inline 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
{

behaviortree_ros2/include/behaviortree_ros2/bt_topic_sub_node.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ inline bool RosTopicSubNode<T>::createSubscriber(const std::string& topic_name)
248248

249249
auto& registry = getRegistry();
250250
auto it = registry.find(subscriber_key_);
251-
if(it == registry.end())
251+
if(it == registry.end() || it->second.expired())
252252
{
253253
sub_instance_ = std::make_shared<SubscriberInstance>(node_, topic_name);
254254
registry.insert({ subscriber_key_, sub_instance_ });

0 commit comments

Comments
 (0)