Nav2 Navigation Stack - humble  humble
ROS 2 Navigation Stack
bt_service_node.hpp
1 // Copyright (c) 2019 Samsung Research America
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef NAV2_BEHAVIOR_TREE__BT_SERVICE_NODE_HPP_
16 #define NAV2_BEHAVIOR_TREE__BT_SERVICE_NODE_HPP_
17 
18 #include <string>
19 #include <memory>
20 #include <chrono>
21 
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"
26 
27 namespace nav2_behavior_tree
28 {
29 
30 using namespace std::chrono_literals; // NOLINT
31 
36 template<class ServiceT>
37 class BtServiceNode : public BT::ActionNodeBase
38 {
39 public:
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_(
51  service_node_name)
52  {
53  node_ = config().blackboard->template get<rclcpp::Node::SharedPtr>("node");
54  callback_group_ = node_->create_callback_group(
55  rclcpp::CallbackGroupType::MutuallyExclusive,
56  false);
57  callback_group_executor_.add_callback_group(callback_group_, node_->get_node_base_interface());
58 
59  // Get the required items from the blackboard
60  auto bt_loop_duration =
61  config().blackboard->template get<std::chrono::milliseconds>("bt_loop_duration");
62  server_timeout_ =
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");
67 
68  // timeout should be less than bt_loop_duration to be able to finish the current tick
69  max_timeout_ = std::chrono::duration_cast<std::chrono::milliseconds>(bt_loop_duration * 0.5);
70 
71  // Now that we have node_ to use, create the service client for this BT service
72  getInput("service_name", service_name_);
73  service_client_ = node_->create_client<ServiceT>(
74  service_name_,
75  rclcpp::ServicesQoS().get_rmw_qos_profile(),
76  callback_group_);
77 
78  // Make a request for the service without parameter
79  request_ = std::make_shared<typename ServiceT::Request>();
80 
81  // Make sure the server is actually there before continuing
82  RCLCPP_DEBUG(
83  node_->get_logger(), "Waiting for \"%s\" service",
84  service_name_.c_str());
85  if (!service_client_->wait_for_service(wait_for_service_timeout_)) {
86  RCLCPP_ERROR(
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(
90  std::string(
91  "Service server %s not available",
92  service_node_name.c_str()));
93  }
94 
95  RCLCPP_DEBUG(
96  node_->get_logger(), "\"%s\" BtServiceNode initialized",
97  service_node_name_.c_str());
98  }
99 
100  BtServiceNode() = delete;
101 
102  virtual ~BtServiceNode()
103  {
104  }
105 
112  static BT::PortsList providedBasicPorts(BT::PortsList addition)
113  {
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")
117  };
118  basic.insert(addition.begin(), addition.end());
119 
120  return basic;
121  }
122 
127  static BT::PortsList providedPorts()
128  {
129  return providedBasicPorts({});
130  }
131 
136  BT::NodeStatus tick() override
137  {
138  if (!request_sent_) {
139  // reset the flag to send the request or not,
140  // allowing the user the option to set it in on_tick
141  should_send_request_ = true;
142 
143  // user defined callback, may modify "should_send_request_".
144  on_tick();
145 
146  if (!should_send_request_) {
147  return BT::NodeStatus::FAILURE;
148  }
149 
150  future_result_ = service_client_->async_send_request(request_).share();
151  sent_time_ = node_->now();
152  request_sent_ = true;
153  }
154  return check_future();
155  }
156 
160  void halt() override
161  {
162  request_sent_ = false;
163  setStatus(BT::NodeStatus::IDLE);
164  }
165 
170  virtual void on_tick()
171  {
172  }
173 
180  virtual BT::NodeStatus on_completion(std::shared_ptr<typename ServiceT::Response>/*response*/)
181  {
182  return BT::NodeStatus::SUCCESS;
183  }
184 
189  virtual BT::NodeStatus check_future()
190  {
191  auto elapsed = (node_->now() - sent_time_).template to_chrono<std::chrono::milliseconds>();
192  auto remaining = server_timeout_ - elapsed;
193 
194  if (remaining > std::chrono::milliseconds(0)) {
195  auto timeout = remaining > max_timeout_ ? max_timeout_ : remaining;
196 
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());
202  return status;
203  }
204 
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;
210  }
211  }
212  }
213 
214  RCLCPP_WARN(
215  node_->get_logger(),
216  "Node timed out while executing service call to %s.", service_name_.c_str());
217  request_sent_ = false;
218  return BT::NodeStatus::FAILURE;
219  }
220 
225  virtual void on_wait_for_result()
226  {
227  }
228 
229 protected:
234  {
235  int recovery_count = 0;
236  config().blackboard->template get<int>("number_recoveries", recovery_count); // NOLINT
237  recovery_count += 1;
238  config().blackboard->template set<int>("number_recoveries", recovery_count); // NOLINT
239  }
240 
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_;
244 
245  // The node that will be used for any ROS operations
246  rclcpp::Node::SharedPtr node_;
247  rclcpp::CallbackGroup::SharedPtr callback_group_;
248  rclcpp::executors::SingleThreadedExecutor callback_group_executor_;
249 
250  // The timeout value while to use in the tick loop while waiting for
251  // a result from the server
252  std::chrono::milliseconds server_timeout_;
253 
254  // The timeout value for BT loop execution
255  std::chrono::milliseconds max_timeout_;
256 
257  // The timeout value for waiting for a service to response
258  std::chrono::milliseconds wait_for_service_timeout_;
259 
260  // To track the server response when a new request is sent
261  std::shared_future<typename ServiceT::Response::SharedPtr> future_result_;
262  bool request_sent_{false};
263  rclcpp::Time sent_time_;
264 
265  // Can be set in on_tick or on_wait_for_result to indicate if a request should be sent.
266  bool should_send_request_;
267 };
268 
269 } // namespace nav2_behavior_tree
270 
271 #endif // NAV2_BEHAVIOR_TREE__BT_SERVICE_NODE_HPP_
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....