194 const Context context_;
195 const rclcpp::Logger logger_;
198 BT::NodeStatus tick()
override;
200 void halt()
override;
202 static std::mutex & getMutex();
204 static ClientsRegistry & getRegistry();
207 bool dynamic_client_instance_ =
false;
208 std::shared_ptr<ServiceClientInstance> client_instance_;
209 typename ServiceClient::SharedFuture future_;
211 rclcpp::Time time_request_sent_;
212 BT::NodeStatus on_feedback_state_change_;
213 typename Response::SharedPtr response_;
220template <
class ServiceT>
221inline RosServiceNode<ServiceT>::ServiceClientInstance::ServiceClientInstance(
222 rclcpp::Node::SharedPtr node, rclcpp::CallbackGroup::SharedPtr group,
const std::string & service_name)
224 service_client = node->create_client<ServiceT>(service_name, rmw_qos_profile_services_default, group);
228template <
class ServiceT>
230 const std::string & instance_name,
const Config & config, Context context)
231: BT::ActionNodeBase(instance_name, config),
233 logger_(context.getChildLogger(
auto_apms_util::toSnakeCase(instance_name)))
235 if (
const BT::Expected<std::string> expected_name = context_.getCommunicationPortName(
this)) {
236 createClient(expected_name.value());
241 dynamic_client_instance_ = true;
245template <
class ServiceT>
246inline BT::NodeStatus RosServiceNode<ServiceT>::tick()
250 return BT::NodeStatus::FAILURE;
255 if (dynamic_client_instance_ && client_instance_) {
256 dynamic_client_instance_ =
false;
261 if (status() == BT::NodeStatus::IDLE && dynamic_client_instance_) {
262 const BT::Expected<std::string> expected_name = context_.getCommunicationPortName(
this);
264 createClient(expected_name.value());
266 throw exceptions::RosNodeError(
267 context_.getFullyQualifiedTreeNodeName(
this) +
268 " - Cannot create the service client because the service name couldn't be resolved using "
269 "the communication port expression specified by the node's "
270 "registration parameters (" +
271 NodeRegistrationOptions::PARAM_NAME_PORT +
": " + context_.registration_options_.port +
272 "). Error message: " + expected_name.error());
276 if (!client_instance_) {
277 throw exceptions::RosNodeError(context_.getFullyQualifiedTreeNodeName(
this) +
" - client_instance_ is nullptr.");
280 auto & service_client = client_instance_->service_client;
282 auto check_status = [
this](BT::NodeStatus status) {
283 if (!isStatusCompleted(status)) {
284 throw exceptions::RosNodeError(
285 context_.getFullyQualifiedTreeNodeName(
this) +
" - The callback must return either SUCCESS or FAILURE.");
291 if (status() == BT::NodeStatus::IDLE) {
292 setStatus(BT::NodeStatus::RUNNING);
294 on_feedback_state_change_ = BT::NodeStatus::RUNNING;
297 typename Request::SharedPtr request = std::make_shared<Request>();
299 if (!setRequest(request)) {
300 return check_status(onFailure(INVALID_REQUEST));
304 if (!service_client->service_is_ready()) {
305 return onFailure(SERVICE_UNREACHABLE);
308 const auto future_and_request_id =
309 service_client->async_send_request(request, [
this](
typename ServiceClient::SharedFuture response) {
310 if (response.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
311 throw exceptions::RosNodeError(
312 this->context_.getFullyQualifiedTreeNodeName(
this) +
" - Response not ready in response callback.");
314 this->response_ = response.get();
316 future_ = future_and_request_id.future;
317 request_id_ = future_and_request_id.request_id;
318 time_request_sent_ = context_.getCurrentTime();
320 RCLCPP_DEBUG(logger_,
"%s - Service request sent.", context_.getFullyQualifiedTreeNodeName(
this).c_str());
321 return BT::NodeStatus::RUNNING;
324 if (status() == BT::NodeStatus::RUNNING) {
328 if ((context_.getCurrentTime() - time_request_sent_) > context_.registration_options_.request_timeout) {
330 client_instance_->service_client->remove_pending_request(request_id_);
331 return check_status(onFailure(SERVICE_TIMEOUT));
333 return BT::NodeStatus::RUNNING;
334 }
else if (future_.valid()) {
338 RCLCPP_DEBUG(logger_,
"%s - Service response received.", context_.getFullyQualifiedTreeNodeName(
this).c_str());
342 return check_status(onResponseReceived(response_));
344 return BT::NodeStatus::RUNNING;
347template <
class ServiceT>
348inline void RosServiceNode<ServiceT>::halt()
350 if (status() == BT::NodeStatus::RUNNING) {
355template <
class ServiceT>
361template <
class ServiceT>
364 return BT::NodeStatus::SUCCESS;
367template <
class ServiceT>
370 const std::string msg = context_.getFullyQualifiedTreeNodeName(
this) +
" - Unexpected error " +
371 std::to_string(error) +
": " +
toStr(error) +
".";
372 RCLCPP_ERROR_STREAM(logger_, msg);
373 throw exceptions::RosNodeError(msg);
376template <
class ServiceT>
379 if (service_name.empty()) {
380 throw exceptions::RosNodeError(
381 context_.getFullyQualifiedTreeNodeName(
this) +
382 " - Argument service_name is empty when trying to create the client.");
387 client_instance_ && service_name == client_instance_->name &&
388 client_instance_->service_client->service_is_ready()) {
392 std::unique_lock lk(getMutex());
394 rclcpp::Node::SharedPtr node = context_.nh_.lock();
396 throw exceptions::RosNodeError(
397 context_.getFullyQualifiedTreeNodeName(
this) +
398 " - The weak pointer to the ROS 2 node expired. The tree node doesn't "
399 "take ownership of it.");
401 rclcpp::CallbackGroup::SharedPtr group = context_.cb_group_.lock();
403 throw exceptions::RosNodeError(
404 context_.getFullyQualifiedTreeNodeName(
this) +
405 " - The weak pointer to the ROS 2 callback group expired. The tree node doesn't "
406 "take ownership of it.");
408 auto client_key = std::string(node->get_fully_qualified_name()) +
"/" + service_name;
410 auto & registry = getRegistry();
411 auto it = registry.find(client_key);
412 if (it == registry.end() || it->second.expired()) {
413 client_instance_ = std::make_shared<ServiceClientInstance>(node, group, service_name);
414 registry.insert({client_key, client_instance_});
416 logger_,
"%s - Created client for service '%s'.", context_.getFullyQualifiedTreeNodeName(
this).c_str(),
417 service_name.c_str());
419 client_instance_ = it->second.lock();
422 bool found = client_instance_->service_client->wait_for_service(context_.registration_options_.wait_timeout);
424 std::string msg = context_.getFullyQualifiedTreeNodeName(
this) +
" - Service with name '" + client_instance_->name +
425 "' is not reachable.";
426 if (context_.registration_options_.allow_unreachable) {
427 RCLCPP_WARN_STREAM(logger_, msg);
429 RCLCPP_ERROR_STREAM(logger_, msg);
430 throw exceptions::RosNodeError(msg);
436template <
class ServiceT>
439 if (client_instance_)
return client_instance_->name;