From 9baf4675dff475d453f684e0c0cae2da02893b43 Mon Sep 17 00:00:00 2001
From: Sarthak Mittal <sarthakmittal2608@gmail.com>
Date: Fri, 2 Oct 2020 06:46:40 +0530
Subject: [PATCH] Fix reset logic in nav2_behavior_tree control nodes (#2004)

* Fix RecoveryNode logic

Signed-off-by: Sarthak Mittal <sarthakmittal2608@gmail.com>

* Fix reset logic in all control nodes

Signed-off-by: Sarthak Mittal <sarthakmittal2608@gmail.com>

* Revert RoundRobin changes

Signed-off-by: Sarthak Mittal <sarthakmittal2608@gmail.com>

* Revert RoundRobin changes

Signed-off-by: Sarthak Mittal <sarthakmittal2608@gmail.com>

* Fix RecoveryNode halt

Signed-off-by: Sarthak Mittal <sarthakmittal2608@gmail.com>
---
 .../plugins/control/pipeline_sequence.cpp     |  1 +
 .../plugins/control/recovery_node.cpp         | 25 ++++++++++---------
 .../plugins/control/test_recovery_node.cpp    | 25 +++++++++++++------
 3 files changed, 32 insertions(+), 19 deletions(-)

diff --git a/nav2_behavior_tree/plugins/control/pipeline_sequence.cpp b/nav2_behavior_tree/plugins/control/pipeline_sequence.cpp
index 9b506c68..640c3ed7 100644
--- a/nav2_behavior_tree/plugins/control/pipeline_sequence.cpp
+++ b/nav2_behavior_tree/plugins/control/pipeline_sequence.cpp
@@ -69,6 +69,7 @@ BT::NodeStatus PipelineSequence::tick()
 void PipelineSequence::halt()
 {
   BT::ControlNode::halt();
+  last_child_ticked_ = 0;
 }
 
 }  // namespace nav2_behavior_tree
diff --git a/nav2_behavior_tree/plugins/control/recovery_node.cpp b/nav2_behavior_tree/plugins/control/recovery_node.cpp
index bfe6ac6a..42fc8076 100644
--- a/nav2_behavior_tree/plugins/control/recovery_node.cpp
+++ b/nav2_behavior_tree/plugins/control/recovery_node.cpp
@@ -47,19 +47,21 @@ BT::NodeStatus RecoveryNode::tick()
       switch (child_status) {
         case BT::NodeStatus::SUCCESS:
           {
-            retry_count_ = 0;
+            // reset node and return success when first child returns success
             halt();
             return BT::NodeStatus::SUCCESS;
           }
 
         case BT::NodeStatus::FAILURE:
           {
-            // tick second child
             if (retry_count_ < number_of_retries_) {
+              // halt first child and tick second child in next iteration
+              ControlNode::haltChild(0);
               current_child_idx_++;
               break;
             } else {
-              ControlNode::haltChildren();
+              // reset node and return failure when max retries has been exceeded
+              halt();
               return BT::NodeStatus::FAILURE;
             }
           }
@@ -71,8 +73,7 @@ BT::NodeStatus RecoveryNode::tick()
 
         default:
           {
-            halt();
-            return BT::NodeStatus::FAILURE;
+            throw BT::LogicError("A child node must never return IDLE");
           }
       }  // end switch
 
@@ -80,16 +81,16 @@ BT::NodeStatus RecoveryNode::tick()
       switch (child_status) {
         case BT::NodeStatus::SUCCESS:
           {
+            // halt second child, increment recovery count, and tick first child in next iteration
+            ControlNode::haltChild(1);
             retry_count_++;
             current_child_idx_--;
-            ControlNode::haltChildren();
           }
           break;
 
         case BT::NodeStatus::FAILURE:
           {
-            current_child_idx_--;
-            retry_count_ = 0;
+            // reset node and return failure if second child fails
             halt();
             return BT::NodeStatus::FAILURE;
           }
@@ -101,13 +102,13 @@ BT::NodeStatus RecoveryNode::tick()
 
         default:
           {
-            halt();
-            return BT::NodeStatus::FAILURE;
+            throw BT::LogicError("A child node must never return IDLE");
           }
       }  // end switch
     }
   }  // end while loop
-  retry_count_ = 0;
+
+  // reset node and return failure
   halt();
   return BT::NodeStatus::FAILURE;
 }
@@ -115,8 +116,8 @@ BT::NodeStatus RecoveryNode::tick()
 void RecoveryNode::halt()
 {
   ControlNode::halt();
-  current_child_idx_ = 0;
   retry_count_ = 0;
+  current_child_idx_ = 0;
 }
 
 }  // namespace nav2_behavior_tree
diff --git a/nav2_behavior_tree/test/plugins/control/test_recovery_node.cpp b/nav2_behavior_tree/test/plugins/control/test_recovery_node.cpp
index 8356a006..608d8f58 100644
--- a/nav2_behavior_tree/test/plugins/control/test_recovery_node.cpp
+++ b/nav2_behavior_tree/test/plugins/control/test_recovery_node.cpp
@@ -26,21 +26,31 @@ class RecoveryDummy : public nav2_behavior_tree::DummyNode
 public:
   BT::NodeStatus tick() override
   {
-    if (++ticks_ > num_failures_ && num_failures_ != -1) {
+    if (ticks_ == num_success_) {
       setStatus(BT::NodeStatus::SUCCESS);
+    } else if (ticks_ == num_failure_) {
+      setStatus(BT::NodeStatus::FAILURE);
     }
+    ticks_++;
     return status();
   }
 
-  void setMaxFailures(int max_failures)
+  void returnSuccessOn(int tick)
   {
-    num_failures_ = max_failures;
+    num_success_ = tick;
+    ticks_ = 0;
+  }
+
+  void returnFailureOn(int tick)
+  {
+    num_failure_ = tick;
     ticks_ = 0;
   }
 
 private:
   int ticks_{0};
-  int num_failures_{-1};
+  int num_success_{-1};
+  int num_failure_{-1};
 };
 
 class RecoveryNodeTestFixture : public nav2_behavior_tree::BehaviorTreeTestFixture
@@ -92,10 +102,10 @@ TEST_F(RecoveryNodeTestFixture, test_running)
 TEST_F(RecoveryNodeTestFixture, test_failure_on_idle_child)
 {
   first_child_->changeStatus(BT::NodeStatus::IDLE);
-  EXPECT_EQ(bt_node_->executeTick(), BT::NodeStatus::FAILURE);
+  EXPECT_THROW(bt_node_->executeTick(), BT::LogicError);
   first_child_->changeStatus(BT::NodeStatus::FAILURE);
   second_child_->changeStatus(BT::NodeStatus::IDLE);
-  EXPECT_EQ(bt_node_->executeTick(), BT::NodeStatus::FAILURE);
+  EXPECT_THROW(bt_node_->executeTick(), BT::LogicError);
 }
 
 TEST_F(RecoveryNodeTestFixture, test_success_one_retry)
@@ -105,7 +115,7 @@ TEST_F(RecoveryNodeTestFixture, test_success_one_retry)
   EXPECT_EQ(bt_node_->executeTick(), BT::NodeStatus::SUCCESS);
 
   // first child fails, second child succeeds, then first child succeeds (one retry)
-  first_child_->setMaxFailures(1);
+  first_child_->returnSuccessOn(1);
   first_child_->changeStatus(BT::NodeStatus::FAILURE);
   second_child_->changeStatus(BT::NodeStatus::SUCCESS);
   EXPECT_EQ(bt_node_->executeTick(), BT::NodeStatus::SUCCESS);
@@ -125,6 +135,7 @@ TEST_F(RecoveryNodeTestFixture, test_failure_one_retry)
   EXPECT_EQ(second_child_->status(), BT::NodeStatus::IDLE);
 
   // first child fails, second child succeeds, then first child fails (one retry)
+  first_child_->returnFailureOn(1);
   first_child_->changeStatus(BT::NodeStatus::FAILURE);
   second_child_->changeStatus(BT::NodeStatus::SUCCESS);
   EXPECT_EQ(bt_node_->executeTick(), BT::NodeStatus::FAILURE);
-- 
GitLab