From 3ed934a16719734a8377a6e230699c995975f320 Mon Sep 17 00:00:00 2001
From: matt-chen <chenbo@yunji.ai>
Date: Tue, 22 Oct 2019 21:19:26 +0800
Subject: [PATCH] Add an input port to control the action/service servers'
 timeout

---
 .../nav2_behavior_tree/bt_action_node.hpp     | 23 +++++++++++-----
 .../nav2_behavior_tree/bt_conversions.hpp     |  6 +++++
 .../nav2_behavior_tree/bt_service_node.hpp    | 27 +++++++++++++------
 .../nav2_behavior_tree/follow_path_action.hpp |  2 +-
 nav2_bt_navigator/src/bt_navigator.cpp        |  2 +-
 5 files changed, 43 insertions(+), 17 deletions(-)

diff --git a/nav2_behavior_tree/include/nav2_behavior_tree/bt_action_node.hpp b/nav2_behavior_tree/include/nav2_behavior_tree/bt_action_node.hpp
index 9208cd22..af4b70b5 100644
--- a/nav2_behavior_tree/include/nav2_behavior_tree/bt_action_node.hpp
+++ b/nav2_behavior_tree/include/nav2_behavior_tree/bt_action_node.hpp
@@ -41,8 +41,9 @@ public:
     result_ = typename rclcpp_action::ClientGoalHandle<ActionT>::WrappedResult();
 
     // Get the required items from the blackboard
-    node_loop_timeout_ =
-      config().blackboard->get<std::chrono::milliseconds>("node_loop_timeout");
+    server_timeout_ =
+      config().blackboard->get<std::chrono::milliseconds>("server_timeout");
+    getInput<std::chrono::milliseconds>("server_timeout", server_timeout_);
 
     // Now that we have the ROS node to use, create the action client for this BT action
     action_client_ = rclcpp_action::create_client<ActionT>(node_, action_name_);
@@ -61,8 +62,16 @@ public:
   {
   }
 
+  // Any BT node that accepts parameters must provide a requiredNodeParameters method
+  static BT::PortsList providedPorts()
+  {
+    return {
+      BT::InputPort<std::chrono::milliseconds>("server_timeout")
+    };
+  }
+
   // Derived classes can override any of the following methods to hook into the
-  // processing for the action: on_tick, on_loop_timeout, and on_success
+  // processing for the action: on_tick, on_server_timeout, and on_success
 
   // Could do dynamic checks, such as getting updates to values on the blackboard
   virtual void on_tick()
@@ -71,7 +80,7 @@ public:
 
   // There can be many loop iterations per tick. Any opportunity to do something after
   // a timeout waiting for a result that hasn't been received yet
-  virtual void on_loop_timeout()
+  virtual void on_server_timeout()
   {
   }
 
@@ -106,9 +115,9 @@ new_goal_received:
     auto future_result = goal_handle_->async_result();
     rclcpp::executor::FutureReturnCode rc;
     do {
-      rc = rclcpp::spin_until_future_complete(node_, future_result, node_loop_timeout_);
+      rc = rclcpp::spin_until_future_complete(node_, future_result, server_timeout_);
       if (rc == rclcpp::executor::FutureReturnCode::TIMEOUT) {
-        on_loop_timeout();
+        on_server_timeout();
 
         // We can handle a new goal if we're still executing
         auto status = goal_handle_->get_status();
@@ -197,7 +206,7 @@ protected:
 
   // The timeout value while to use in the tick loop while waiting for
   // a result from the server
-  std::chrono::milliseconds node_loop_timeout_;
+  std::chrono::milliseconds server_timeout_;
 };
 
 }  // namespace nav2_behavior_tree
diff --git a/nav2_behavior_tree/include/nav2_behavior_tree/bt_conversions.hpp b/nav2_behavior_tree/include/nav2_behavior_tree/bt_conversions.hpp
index ead7ed68..ed3a4cc7 100644
--- a/nav2_behavior_tree/include/nav2_behavior_tree/bt_conversions.hpp
+++ b/nav2_behavior_tree/include/nav2_behavior_tree/bt_conversions.hpp
@@ -59,6 +59,12 @@ inline geometry_msgs::msg::Quaternion convertFromString(const StringView key)
   }
 }
 
+template <>
+inline std::chrono::milliseconds convertFromString<std::chrono::milliseconds>(StringView str)
+{
+  return std::chrono::milliseconds(std::stoul(str.data()));
+}
+
 }  // namespace BT
 
 #endif  // NAV2_BEHAVIOR_TREE__BT_CONVERSIONS_HPP_
diff --git a/nav2_behavior_tree/include/nav2_behavior_tree/bt_service_node.hpp b/nav2_behavior_tree/include/nav2_behavior_tree/bt_service_node.hpp
index 8f40866b..1a7eb037 100644
--- a/nav2_behavior_tree/include/nav2_behavior_tree/bt_service_node.hpp
+++ b/nav2_behavior_tree/include/nav2_behavior_tree/bt_service_node.hpp
@@ -37,8 +37,9 @@ public:
     node_ = config().blackboard->get<rclcpp::Node::SharedPtr>("node");
 
     // Get the required items from the blackboard
-    node_loop_timeout_ =
-      config().blackboard->get<std::chrono::milliseconds>("node_loop_timeout");
+    server_timeout_ =
+      config().blackboard->get<std::chrono::milliseconds>("server_timeout");
+    getInput<std::chrono::milliseconds>("server_timeout", server_timeout_);
 
     // Now that we have node_ to use, create the service client for this BT service
     getInput("service_name", service_name_);
@@ -63,7 +64,8 @@ public:
   static BT::PortsList providedPorts()
   {
     return {
-      BT::InputPort<std::string>("service_name", "please_set_service_name_in_BT_Node")
+      BT::InputPort<std::string>("service_name", "please_set_service_name_in_BT_Node"),
+      BT::InputPort<std::chrono::milliseconds>("server_timeout")
     };
   }
 
@@ -75,12 +77,15 @@ public:
 
     rclcpp::executor::FutureReturnCode rc;
     rc = rclcpp::spin_until_future_complete(node_,
-        future_result, node_loop_timeout_);
-    if (rc != rclcpp::executor::FutureReturnCode::SUCCESS) {
-      return BT::NodeStatus::FAILURE;
-    } else {
+        future_result, server_timeout_);
+    if (rc == rclcpp::executor::FutureReturnCode::SUCCESS) {
       return BT::NodeStatus::SUCCESS;
+    } else if(rc == rclcpp::executor::FutureReturnCode::TIMEOUT) {
+      RCLCPP_WARN(node_->get_logger(),
+                  "Node timed out while executing service call to %s.", service_name_.c_str());
+      on_server_timeout();
     }
+    return BT::NodeStatus::FAILURE;
   }
 
   // Fill in service request with information if necessary
@@ -89,6 +94,12 @@ public:
     request_ = std::make_shared<typename ServiceT::Request>();
   }
 
+  // An opportunity to do something after
+  // a timeout waiting for a result that hasn't been received yet
+  virtual void on_server_timeout()
+  {
+  }
+
 protected:
   std::string service_name_, service_node_name_;
   typename std::shared_ptr<rclcpp::Client<ServiceT>> service_client_;
@@ -99,7 +110,7 @@ protected:
 
   // The timeout value while to use in the tick loop while waiting for
   // a result from the server
-  std::chrono::milliseconds node_loop_timeout_;
+  std::chrono::milliseconds server_timeout_;
 };
 
 }  // namespace nav2_behavior_tree
diff --git a/nav2_behavior_tree/include/nav2_behavior_tree/follow_path_action.hpp b/nav2_behavior_tree/include/nav2_behavior_tree/follow_path_action.hpp
index ecbe918b..0ddcb3bb 100644
--- a/nav2_behavior_tree/include/nav2_behavior_tree/follow_path_action.hpp
+++ b/nav2_behavior_tree/include/nav2_behavior_tree/follow_path_action.hpp
@@ -40,7 +40,7 @@ public:
     getInput("path", goal_.path);
   }
 
-  void on_loop_timeout() override
+  void on_server_timeout() override
   {
     // Check if the goal has been updated
     if (config().blackboard->get<bool>("path_updated")) {
diff --git a/nav2_bt_navigator/src/bt_navigator.cpp b/nav2_bt_navigator/src/bt_navigator.cpp
index c06b2ea2..9a9a0ad1 100644
--- a/nav2_bt_navigator/src/bt_navigator.cpp
+++ b/nav2_bt_navigator/src/bt_navigator.cpp
@@ -82,7 +82,7 @@ BtNavigator::on_configure(const rclcpp_lifecycle::State & /*state*/)
   // Put items on the blackboard
   blackboard_->set<rclcpp::Node::SharedPtr>("node", client_node_);  // NOLINT
   blackboard_->set<std::shared_ptr<tf2_ros::Buffer>>("tf_buffer", tf_);  // NOLINT
-  blackboard_->set<std::chrono::milliseconds>("node_loop_timeout", std::chrono::milliseconds(10));  // NOLINT
+  blackboard_->set<std::chrono::milliseconds>("server_timeout", std::chrono::milliseconds(10));  // NOLINT
   blackboard_->set<bool>("path_updated", false);  // NOLINT
   blackboard_->set<bool>("initial_pose_received", false);  // NOLINT
 
-- 
GitLab