From d0a4f01a5d046aeb1bb26c159ef05cd5b050f2ad Mon Sep 17 00:00:00 2001
From: Alejandro Lopez Gestoso <alopez@iri.upc.edu>
Date: Mon, 18 Dec 2023 13:12:11 +0100
Subject: [PATCH] Added timeout functions

---
 CMakeLists.txt               |   3 +-
 include/iri_bt_basic_nodes.h |  72 ++++++++++++++++++++++
 package.xml                  |   2 +-
 src/iri_bt_basic_nodes.cpp   | 113 +++++++++++++++++++++++++++++++++++
 src/xml/bt_definitions.xml   |  19 ++++++
 5 files changed, 207 insertions(+), 2 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index dc74094..c4fb859 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,6 +20,7 @@ find_package(catkin REQUIRED COMPONENTS
   tf2_ros
   geometry_msgs
   sensor_msgs
+  iri_ros_tools
 )
 
 ## System dependencies are found with CMake's conventions
@@ -98,7 +99,7 @@ set(head_helpers_name head_helpers)
 catkin_package(
   INCLUDE_DIRS include
   LIBRARIES ${PROJECT_NAME} ${head_helpers_name}
-  CATKIN_DEPENDS roscpp dynamic_reconfigure diagnostic_updater behaviortree_cpp_v3 iri_behaviortree tf2_ros geometry_msgs sensor_msgs
+  CATKIN_DEPENDS roscpp dynamic_reconfigure diagnostic_updater behaviortree_cpp_v3 iri_behaviortree tf2_ros geometry_msgs sensor_msgs iri_ros_tools
 #  DEPENDS system_lib
   DEPENDS Boost
 )
diff --git a/include/iri_bt_basic_nodes.h b/include/iri_bt_basic_nodes.h
index 820043f..9968ef5 100644
--- a/include/iri_bt_basic_nodes.h
+++ b/include/iri_bt_basic_nodes.h
@@ -4,6 +4,7 @@
 #include "iri_bt_factory.h"
 #include <tf2_geometry_msgs/tf2_geometry_msgs.h>
 #include <tf2_ros/transform_listener.h>
+#include <iri_ros_tools/timeout.h>
 
 class IriBTBasicNodes
 {
@@ -25,6 +26,7 @@ class IriBTBasicNodes
     // variables to know whether start or stop have been demanded by the user
     tf2_ros::Buffer tf2_buffer;
     tf2_ros::TransformListener tf2_listener;
+    std::map<std::string, CROSTimeout> timeouts;
 
   protected:
     /**
@@ -151,6 +153,76 @@ class IriBTBasicNodes
       *
       */
     BT::NodeStatus print_msg(BT::TreeNode& _self);
+
+    /**
+      * \brief Action to create (if necessary) and to start a timeout
+      *
+      * It has the following input ports:
+      *
+      *   tout_name (std::string): Timeout name.
+      * 
+      *   duration (double): Duration time in seconds.
+      *
+      * \param _self node with the required ports:
+      *
+      * \return a BT:NodeStatus indicating whether the request has been
+      * successfull (BT::NodeStatus::SUCCESS) or not (BT::NodeStatus::FAILURE).
+      * If inputs are missing or incorrect it also returns BT::NodeStatus::FAILURE.
+      *
+      */
+    BT::NodeStatus start_timeout(BT::TreeNode& _self);
+
+    /**
+      * \brief Action to stop a timeout
+      *
+      * It has the following input ports:
+      *
+      *   tout_name (std::string): Timeout name.
+      *
+      * \param _self node with the required ports:
+      *
+      * \return a BT:NodeStatus indicating whether the request has been
+      * successfull (BT::NodeStatus::SUCCESS) or not (BT::NodeStatus::FAILURE).
+      * If inputs are missing or incorrect it also returns BT::NodeStatus::FAILURE.
+      *
+      */
+    BT::NodeStatus stop_timeout(BT::TreeNode& _self);
+
+    /**
+      * \brief Action to stop a timeout
+      *
+      * It has the following input ports:
+      *
+      *   tout_name (std::string): Timeout name.
+      *
+      * It also has the following output ports:
+      * 
+      *   timed_out (bool): If is timed out.
+      * 
+      * \param _self node with the required ports:
+      *
+      * \return a BT:NodeStatus indicating whether the request has been
+      * successfull (BT::NodeStatus::SUCCESS) or not (BT::NodeStatus::FAILURE).
+      * If inputs are missing or incorrect it also returns BT::NodeStatus::FAILURE.
+      *
+      */
+    BT::NodeStatus get_timed_out(BT::TreeNode& _self);
+
+    /**
+      * \brief Action to erase a timeout
+      *
+      * It has the following input ports:
+      *
+      *   tout_name (std::string): Timeout name.
+      * 
+      * \param _self node with the required ports:
+      *
+      * \return a BT:NodeStatus indicating whether the request has been
+      * successfull (BT::NodeStatus::SUCCESS) or not (BT::NodeStatus::FAILURE).
+      * If inputs are missing or incorrect it also returns BT::NodeStatus::FAILURE.
+      *
+      */
+    BT::NodeStatus erase_timeout(BT::TreeNode& _self);
     };
 
 #endif
diff --git a/package.xml b/package.xml
index b45eb3e..e74fd57 100644
--- a/package.xml
+++ b/package.xml
@@ -21,7 +21,7 @@
   <depend>tf2_ros</depend>
   <depend>sensor_msgs</depend>
   <depend>geometry_msgs</depend>
-  
+  <depend>iri_ros_tools</depend>
 
   <!-- The export tag contains other, unspecified, tags -->
   <export>
diff --git a/src/iri_bt_basic_nodes.cpp b/src/iri_bt_basic_nodes.cpp
index 5955797..58780d4 100644
--- a/src/iri_bt_basic_nodes.cpp
+++ b/src/iri_bt_basic_nodes.cpp
@@ -13,6 +13,9 @@ void IriBTBasicNodes::init(IriBehaviorTreeFactory &factory)
   BT::PortsList compute_distance_ports = {BT::InputPort<geometry_msgs::PoseStamped>("pose1"),BT::InputPort<geometry_msgs::PoseStamped>("pose2"),BT::OutputPort<double>("distance")};
   BT::PortsList compare_ports = {BT::InputPort<double>("value1"),BT::InputPort<double>("value2")};
   BT::PortsList print_msg_ports = {BT::InputPort<std::string>("msg"), BT::InputPort<std::string>("type")};
+  BT::PortsList start_timeout_ports = {BT::InputPort<std::string>("tout_name"), BT::InputPort<double>("duration")};
+  BT::PortsList stop_timeout_ports = {BT::InputPort<std::string>("tout_name")};
+  BT::PortsList get_timed_out_ports = {BT::InputPort<std::string>("tout_name"), BT::OutputPort<bool>("timed_out")};
 
   //Registration of conditions
   factory.registerSimpleCondition("is_variable_true", std::bind(&IriBTBasicNodes::is_variable_true, this, std::placeholders::_1),check_variable_ports);
@@ -25,6 +28,10 @@ void IriBTBasicNodes::init(IriBehaviorTreeFactory &factory)
   factory.registerSimpleAction("compare_bigger", std::bind(&IriBTBasicNodes::compare_bigger, this, std::placeholders::_1),compare_ports);
   factory.registerSimpleAction("compare_smaller", std::bind(&IriBTBasicNodes::compare_smaller, this, std::placeholders::_1),compare_ports);
   factory.registerSimpleAction("print_msg",  std::bind(&IriBTBasicNodes::print_msg, this, std::placeholders::_1), print_msg_ports);
+  factory.registerSimpleAction("start_timeout",  std::bind(&IriBTBasicNodes::start_timeout, this, std::placeholders::_1), start_timeout_ports);
+  factory.registerSimpleAction("stop_timeout",  std::bind(&IriBTBasicNodes::stop_timeout, this, std::placeholders::_1), stop_timeout_ports);
+  factory.registerSimpleAction("get_timed_out",  std::bind(&IriBTBasicNodes::get_timed_out, this, std::placeholders::_1), get_timed_out_ports);
+  factory.registerSimpleAction("erase_timeout",  std::bind(&IriBTBasicNodes::erase_timeout, this, std::placeholders::_1), stop_timeout_ports);
 }
 
 BT::NodeStatus IriBTBasicNodes::is_variable_true(BT::TreeNode& self)
@@ -237,3 +244,109 @@ BT::NodeStatus IriBTBasicNodes::print_msg(BT::TreeNode& _self)
     ROS_INFO_STREAM(msg);
   return BT::NodeStatus::SUCCESS;
 }
+
+BT::NodeStatus IriBTBasicNodes::start_timeout(BT::TreeNode& _self)
+{
+  BT::Optional<std::string> name_p = _self.getInput<std::string>("tout_name");
+  BT::Optional<double> duration_p = _self.getInput<double>("duration");
+  std::string name;
+  double duration;
+  std::map<std::string, CROSTimeout>::iterator it;
+
+  if (!name_p || !duration_p)
+  {
+    ROS_ERROR_STREAM("IriBTBasicNodes::start_timeout-> Incorrect or missing input. It needs the following input ports:"
+                      << " tout_name (sdt::string) and duration (double)");
+    return BT::NodeStatus::FAILURE;
+  }
+  name = name_p.value();
+  duration = duration_p.value();
+
+  it = this->timeouts.find(name);
+  if (it == this->timeouts.end())
+  {
+    CROSTimeout tout;
+    this->timeouts[name] = tout;
+    it = this->timeouts.find(name);
+  }
+  it->second.start(ros::Duration(duration));
+
+  return BT::NodeStatus::SUCCESS;
+}
+
+BT::NodeStatus IriBTBasicNodes::stop_timeout(BT::TreeNode& _self)
+{
+  BT::Optional<std::string> name_p = _self.getInput<std::string>("tout_name");
+  std::string name;
+  std::map<std::string, CROSTimeout>::iterator it;
+
+  if (!name_p)
+  {
+    ROS_ERROR_STREAM("IriBTBasicNodes::stop_timeout-> Incorrect or missing input. It needs the following input ports:"
+                      << " tout_name (sdt::string)");
+    return BT::NodeStatus::FAILURE;
+  }
+  name = name_p.value();
+
+  it = this->timeouts.find(name);
+  if (it == this->timeouts.end())
+  {
+    ROS_ERROR_STREAM("IriBTBasicNodes::stop_timeout-> No timeout found with name " << name);
+    return BT::NodeStatus::FAILURE;
+  }
+  it->second.stop();
+
+  return BT::NodeStatus::SUCCESS;
+}
+
+BT::NodeStatus IriBTBasicNodes::get_timed_out(BT::TreeNode& _self)
+{
+  BT::Optional<std::string> name_p = _self.getInput<std::string>("tout_name");
+  std::string name;
+  std::map<std::string, CROSTimeout>::iterator it;
+  bool timed_out = false;
+
+  _self.setOutput("timed_out", timed_out);
+  if (!name_p)
+  {
+    ROS_ERROR_STREAM("IriBTBasicNodes::get_timed_out-> Incorrect or missing input. It needs the following input ports:"
+                      << " tout_name (sdt::string)");
+    return BT::NodeStatus::FAILURE;
+  }
+  name = name_p.value();
+
+  it = this->timeouts.find(name);
+  if (it == this->timeouts.end())
+  {
+    ROS_ERROR_STREAM("IriBTBasicNodes::get_timed_out-> No timeout found with name " << name);
+    return BT::NodeStatus::FAILURE;
+  }
+  timed_out = it->second.timed_out();
+  _self.setOutput("timed_out", timed_out);
+  return BT::NodeStatus::SUCCESS;
+}
+
+BT::NodeStatus IriBTBasicNodes::erase_timeout(BT::TreeNode& _self)
+{
+  BT::Optional<std::string> name_p = _self.getInput<std::string>("tout_name");
+  std::string name;
+  std::map<std::string, CROSTimeout>::iterator it;
+
+  if (!name_p)
+  {
+    ROS_ERROR_STREAM("IriBTBasicNodes::erase_timeout-> Incorrect or missing input. It needs the following input ports:"
+                      << " tout_name (sdt::string)");
+    return BT::NodeStatus::FAILURE;
+  }
+  name = name_p.value();
+
+  it = this->timeouts.find(name);
+  if (it == this->timeouts.end())
+  {
+    ROS_WARN_STREAM("IriBTBasicNodes::erase_timeout-> No timeout found with name " << name);
+    return BT::NodeStatus::SUCCESS;
+  }
+  this->timeouts.erase(it);
+
+  return BT::NodeStatus::SUCCESS;
+}
\ No newline at end of file
diff --git a/src/xml/bt_definitions.xml b/src/xml/bt_definitions.xml
index c96154b..8471bf2 100644
--- a/src/xml/bt_definitions.xml
+++ b/src/xml/bt_definitions.xml
@@ -24,6 +24,11 @@
             <input_port name="pose2"> (geometry_msgs::PoseStamped): Pose 2.</input_port>
             <output_port name="distance"> (double): Distance.</output_port>
         </Action>
+        <Action ID="compute_y_distance">
+            <input_port name="pose1"> (geometry_msgs::PoseStamped): Pose 1.</input_port>
+            <input_port name="pose2"> (geometry_msgs::PoseStamped): Pose 2.</input_port>
+            <output_port name="distance"> (double): Distance.</output_port>
+        </Action>
         <Action ID="compare_bigger">
             <input_port name="value1"> (double): Value 1.</input_port>
             <input_port name="value2"> (double): Value 2.</input_port>
@@ -69,6 +74,20 @@
             <input_port name="param_name"> (std::string): Parameter name.</input_port>
             <output_port name="param_value"> (bool): Parameter value.</output_port>
         </Action>
+        <Action ID="start_timeout">
+            <input_port name="tout_name"> (std::string): Timeout name.</input_port>
+            <input_port name="duration"> (double): Duration time in second.</input_port>
+        </Action>
+        <Action ID="stop_timeout">
+            <input_port name="tout_name"> (std::string): Timeout name.</input_port>
+        </Action>
+        <Action ID="get_timed_out">
+            <input_port name="tout_name"> (std::string): Timeout name.</input_port>
+            <output_port name="timed_out"> (bool): If is timed out.</output_port>
+        </Action>
+        <Action ID="erase_timeout">
+            <input_port name="tout_name"> (std::string): Timeout name.</input_port>
+        </Action>
         <!-- Head helepers -->
         <Action ID="from_img_point_to_pan_tilt">
             <input_port name="x"> Image x coordinate</input_port>
-- 
GitLab