15 #ifndef NAV2_BEHAVIOR_TREE__BT_SERVICE_NODE_HPP_
16 #define NAV2_BEHAVIOR_TREE__BT_SERVICE_NODE_HPP_
22 #include "behaviortree_cpp/action_node.h"
23 #include "behaviortree_cpp/json_export.h"
24 #include "nav2_ros_common/node_utils.hpp"
25 #include "nav2_ros_common/lifecycle_node.hpp"
26 #include "nav2_behavior_tree/bt_utils.hpp"
27 #include "nav2_behavior_tree/json_utils.hpp"
28 #include "nav2_ros_common/service_client.hpp"
30 namespace nav2_behavior_tree
33 using namespace std::chrono_literals;
40 template<
class ServiceT>
51 const std::string & service_node_name,
52 const BT::NodeConfiguration & conf,
53 const std::string & service_name =
"")
54 : BT::ActionNodeBase(service_node_name, conf), service_name_(service_name), service_node_name_(
60 request_ = std::make_shared<typename ServiceT::Request>();
64 node_->get_logger(),
"Waiting for \"%s\" service",
65 service_name_.c_str());
66 if (!service_client_->wait_for_service(wait_for_service_timeout_)) {
68 node_->get_logger(),
"\"%s\" service server not available after waiting for %.2fs",
69 service_name_.c_str(), wait_for_service_timeout_.count() / 1000.0);
70 throw std::runtime_error(
71 std::string(
"Service server ") + service_name_ +
72 std::string(
" not available"));
76 node_->get_logger(),
"\"%s\" BtServiceNode initialized",
77 service_node_name_.c_str());
92 auto bt_loop_duration =
93 config().blackboard->template get<std::chrono::milliseconds>(
"bt_loop_duration");
94 getInputOrBlackboard(
"server_timeout", server_timeout_);
95 wait_for_service_timeout_ =
96 config().blackboard->template get<std::chrono::milliseconds>(
"wait_for_service_timeout");
99 max_timeout_ = std::chrono::duration_cast<std::chrono::milliseconds>(bt_loop_duration * 0.5);
102 createROSInterfaces();
110 std::string service_new;
111 getInput(
"service_name", service_new);
114 service_new = service_new.empty() ? service_name_ : service_new;
115 if (service_new.empty()) {
116 throw std::runtime_error(
117 std::string(
"Service name not provided for ") + service_node_name_ +
118 std::string(
" BT node"));
121 if (service_new != service_name_ || !service_client_) {
122 service_name_ = service_new;
123 node_ = config().blackboard->template get<nav2::LifecycleNode::SharedPtr>(
"node");
125 node_->create_client<ServiceT>(
126 service_name_,
true );
138 BT::PortsList basic = {
139 BT::InputPort<std::string>(
"service_name",
"please_set_service_name_in_BT_Node"),
140 BT::InputPort<std::chrono::milliseconds>(
"server_timeout")
142 basic.insert(addition.begin(), addition.end());
153 return providedBasicPorts({});
162 if (!BT::isStatusActive(status())) {
166 if (!request_sent_) {
169 should_send_request_ =
true;
172 request_ = std::make_shared<typename ServiceT::Request>();
177 if (!should_send_request_) {
178 return BT::NodeStatus::FAILURE;
181 future_result_ = service_client_->async_call(request_);
182 sent_time_ = node_->now();
183 request_sent_ =
true;
185 return check_future();
193 request_sent_ =
false;
211 virtual BT::NodeStatus
on_completion(std::shared_ptr<typename ServiceT::Response>)
213 return BT::NodeStatus::SUCCESS;
230 auto elapsed = (node_->now() - sent_time_).
template to_chrono<std::chrono::milliseconds>();
231 auto remaining = server_timeout_ - elapsed;
233 if (remaining > std::chrono::milliseconds(0)) {
234 auto timeout = remaining > max_timeout_ ? max_timeout_ : remaining;
236 rclcpp::FutureReturnCode rc;
237 rc = service_client_->spin_until_complete(future_result_, timeout);
238 if (rc == rclcpp::FutureReturnCode::SUCCESS) {
239 request_sent_ =
false;
240 BT::NodeStatus status = on_completion(future_result_.get());
244 if (rc == rclcpp::FutureReturnCode::TIMEOUT) {
245 on_wait_for_result();
246 elapsed = (node_->now() - sent_time_).
template to_chrono<std::chrono::milliseconds>();
247 if (elapsed < server_timeout_) {
248 return BT::NodeStatus::RUNNING;
255 "Node timed out while executing service call to %s.", service_name_.c_str());
257 request_sent_ =
false;
258 return BT::NodeStatus::FAILURE;
275 int recovery_count = 0;
276 [[maybe_unused]]
auto res = config().blackboard->get(
"number_recoveries", recovery_count);
278 config().blackboard->set(
"number_recoveries", recovery_count);
281 std::string service_name_, service_node_name_;
282 typename nav2::ServiceClient<ServiceT>::SharedPtr service_client_;
283 std::shared_ptr<typename ServiceT::Request> request_;
286 nav2::LifecycleNode::SharedPtr node_;
290 std::chrono::milliseconds server_timeout_;
293 std::chrono::milliseconds max_timeout_;
296 std::chrono::milliseconds wait_for_service_timeout_;
299 std::shared_future<typename ServiceT::Response::SharedPtr> future_result_;
300 bool request_sent_{
false};
301 rclcpp::Time sent_time_;
304 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.
void initialize()
Function to read parameters and initialize class variables.
void createROSInterfaces()
Function to create ROS interfaces.
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.
virtual void on_timeout()
Function to perform work in a BT Node when the service call times out.
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....