15 #ifndef NAV2_BEHAVIOR_TREE__BT_SERVICE_NODE_HPP_
16 #define NAV2_BEHAVIOR_TREE__BT_SERVICE_NODE_HPP_
22 #include "behaviortree_cpp_v3/action_node.h"
23 #include "nav2_util/node_utils.hpp"
24 #include "rclcpp/rclcpp.hpp"
25 #include "nav2_behavior_tree/bt_conversions.hpp"
27 namespace nav2_behavior_tree
30 using namespace std::chrono_literals;
36 template<
class ServiceT>
47 const std::string & service_node_name,
48 const BT::NodeConfiguration & conf,
49 const std::string & service_name =
"")
50 : BT::ActionNodeBase(service_node_name, conf), service_name_(service_name), service_node_name_(
53 node_ = config().blackboard->template get<rclcpp::Node::SharedPtr>(
"node");
54 callback_group_ = node_->create_callback_group(
55 rclcpp::CallbackGroupType::MutuallyExclusive,
57 callback_group_executor_.add_callback_group(callback_group_, node_->get_node_base_interface());
60 auto bt_loop_duration =
61 config().blackboard->template get<std::chrono::milliseconds>(
"bt_loop_duration");
63 config().blackboard->template get<std::chrono::milliseconds>(
"server_timeout");
64 getInput<std::chrono::milliseconds>(
"server_timeout", server_timeout_);
65 wait_for_service_timeout_ =
66 config().blackboard->template get<std::chrono::milliseconds>(
"wait_for_service_timeout");
69 max_timeout_ = std::chrono::duration_cast<std::chrono::milliseconds>(bt_loop_duration * 0.5);
72 getInput(
"service_name", service_name_);
73 service_client_ = node_->create_client<ServiceT>(
75 rclcpp::ServicesQoS().get_rmw_qos_profile(),
79 request_ = std::make_shared<typename ServiceT::Request>();
83 node_->get_logger(),
"Waiting for \"%s\" service",
84 service_name_.c_str());
85 if (!service_client_->wait_for_service(wait_for_service_timeout_)) {
87 node_->get_logger(),
"\"%s\" service server not available after waiting for %.2fs",
88 service_name_.c_str(), wait_for_service_timeout_.count() / 1000.0);
89 throw std::runtime_error(
91 "Service server %s not available",
92 service_node_name.c_str()));
96 node_->get_logger(),
"\"%s\" BtServiceNode initialized",
97 service_node_name_.c_str());
114 BT::PortsList basic = {
115 BT::InputPort<std::string>(
"service_name",
"please_set_service_name_in_BT_Node"),
116 BT::InputPort<std::chrono::milliseconds>(
"server_timeout")
118 basic.insert(addition.begin(), addition.end());
129 return providedBasicPorts({});
138 if (!request_sent_) {
141 should_send_request_ =
true;
146 if (!should_send_request_) {
147 return BT::NodeStatus::FAILURE;
150 future_result_ = service_client_->async_send_request(request_).share();
151 sent_time_ = node_->now();
152 request_sent_ =
true;
154 return check_future();
162 request_sent_ =
false;
163 setStatus(BT::NodeStatus::IDLE);
180 virtual BT::NodeStatus
on_completion(std::shared_ptr<typename ServiceT::Response>)
182 return BT::NodeStatus::SUCCESS;
191 auto elapsed = (node_->now() - sent_time_).
template to_chrono<std::chrono::milliseconds>();
192 auto remaining = server_timeout_ - elapsed;
194 if (remaining > std::chrono::milliseconds(0)) {
195 auto timeout = remaining > max_timeout_ ? max_timeout_ : remaining;
197 rclcpp::FutureReturnCode rc;
198 rc = callback_group_executor_.spin_until_future_complete(future_result_, timeout);
199 if (rc == rclcpp::FutureReturnCode::SUCCESS) {
200 request_sent_ =
false;
201 BT::NodeStatus status = on_completion(future_result_.get());
205 if (rc == rclcpp::FutureReturnCode::TIMEOUT) {
206 on_wait_for_result();
207 elapsed = (node_->now() - sent_time_).
template to_chrono<std::chrono::milliseconds>();
208 if (elapsed < server_timeout_) {
209 return BT::NodeStatus::RUNNING;
216 "Node timed out while executing service call to %s.", service_name_.c_str());
217 request_sent_ =
false;
218 return BT::NodeStatus::FAILURE;
235 int recovery_count = 0;
236 config().blackboard->template get<int>(
"number_recoveries", recovery_count);
238 config().blackboard->template set<int>(
"number_recoveries", recovery_count);
241 std::string service_name_, service_node_name_;
242 typename std::shared_ptr<rclcpp::Client<ServiceT>> service_client_;
243 std::shared_ptr<typename ServiceT::Request> request_;
246 rclcpp::Node::SharedPtr node_;
247 rclcpp::CallbackGroup::SharedPtr callback_group_;
248 rclcpp::executors::SingleThreadedExecutor callback_group_executor_;
252 std::chrono::milliseconds server_timeout_;
255 std::chrono::milliseconds max_timeout_;
258 std::chrono::milliseconds wait_for_service_timeout_;
261 std::shared_future<typename ServiceT::Response::SharedPtr> future_result_;
262 bool request_sent_{
false};
263 rclcpp::Time sent_time_;
266 bool should_send_request_;
Abstract class representing a service based BT node.
BT::NodeStatus tick() override
The main override required by a BT service.
virtual void on_tick()
Function to perform some user-defined operation on tick Fill in service request with information if n...
static BT::PortsList providedPorts()
Creates list of BT ports.
static BT::PortsList providedBasicPorts(BT::PortsList addition)
Any subclass of BtServiceNode that accepts parameters must provide a providedPorts method and call pr...
virtual BT::NodeStatus check_future()
Check the future and decide the status of BT.
void halt() override
The other (optional) override required by a BT service.
void increment_recovery_count()
Function to increment recovery count on blackboard if this node wraps a recovery.
virtual void on_wait_for_result()
Function to perform some user-defined operation after a timeout waiting for a result that hasn't been...
BtServiceNode(const std::string &service_node_name, const BT::NodeConfiguration &conf, const std::string &service_name="")
A nav2_behavior_tree::BtServiceNode constructor.
virtual BT::NodeStatus on_completion(std::shared_ptr< typename ServiceT::Response >)
Function to perform some user-defined operation upon successful completion of the service....