Skip to content

Commit acbc707

Browse files
committed
Merge branch 'master' of github.com:BehaviorTree/BehaviorTree.CPP
2 parents 9f17bc3 + ff1bfcd commit acbc707

File tree

4 files changed

+92
-60
lines changed

4 files changed

+92
-60
lines changed

include/behaviortree_cpp/controls/parallel_node.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,19 @@ class ParallelNode : public ControlNode
6565
private:
6666
int success_threshold_;
6767
int failure_threshold_;
68+
69+
std::set<size_t> completed_list_;
6870

69-
std::set<size_t> skip_list_;
70-
bool all_skipped_ = true;
71+
size_t success_count_ = 0;
72+
size_t failure_count_ = 0;
7173

7274
bool read_parameter_from_ports_;
7375
static constexpr const char* THRESHOLD_SUCCESS = "success_count";
7476
static constexpr const char* THRESHOLD_FAILURE = "failure_count";
7577

7678
virtual BT::NodeStatus tick() override;
79+
80+
void clear();
7781
};
7882

7983
} // namespace BT

src/controls/parallel_node.cpp

Lines changed: 55 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,6 @@ NodeStatus ParallelNode::tick()
5252
}
5353
}
5454

55-
size_t success_children_num = 0;
56-
size_t failure_children_num = 0;
57-
5855
const size_t children_count = children_nodes_.size();
5956

6057
if (children_count < successThreshold())
@@ -67,79 +64,81 @@ NodeStatus ParallelNode::tick()
6764
throw LogicError("Number of children is less than threshold. Can never fail.");
6865
}
6966

70-
if(status() == NodeStatus::IDLE)
71-
{
72-
all_skipped_ = true;
73-
}
7467
setStatus(NodeStatus::RUNNING);
7568

69+
size_t skipped_count = 0;
70+
7671
// Routing the tree according to the sequence node's logic:
7772
for (size_t i = 0; i < children_count; i++)
7873
{
79-
TreeNode* child_node = children_nodes_[i];
80-
81-
bool const in_skip_list = (skip_list_.count(i) != 0);
82-
83-
NodeStatus const prev_status = child_node->status();
84-
NodeStatus const child_status =
85-
in_skip_list ? prev_status : child_node->executeTick();
74+
if(completed_list_.count(i) == 0)
75+
{
76+
TreeNode* child_node = children_nodes_[i];
77+
NodeStatus const child_status = child_node->executeTick();
78+
79+
switch (child_status)
80+
{
81+
case NodeStatus::SKIPPED: {
82+
skipped_count++;
83+
} break;
84+
85+
case NodeStatus::SUCCESS: {
86+
completed_list_.insert(i);
87+
success_count_++;
88+
}
89+
break;
8690

87-
// switch to RUNNING state as soon as you find an active child
88-
all_skipped_ &= (child_status == NodeStatus::SKIPPED);
91+
case NodeStatus::FAILURE: {
92+
completed_list_.insert(i);
93+
failure_count_++;
94+
}
95+
break;
8996

90-
switch (child_status)
91-
{
92-
case NodeStatus::SUCCESS: {
93-
skip_list_.insert(i);
94-
success_children_num++;
95-
96-
if (success_children_num == successThreshold())
97-
{
98-
skip_list_.clear();
99-
resetChildren();
100-
return NodeStatus::SUCCESS;
97+
case NodeStatus::RUNNING: {
98+
// Still working. Check the next
10199
}
102-
}
103-
break;
104-
105-
case NodeStatus::FAILURE: {
106-
skip_list_.insert(i);
107-
failure_children_num++;
108-
109-
// It fails if it is not possible to succeed anymore or if
110-
// number of failures are equal to failure_threshold_
111-
if ((failure_children_num > children_count - successThreshold()) ||
112-
(failure_children_num == failureThreshold()))
113-
{
114-
skip_list_.clear();
115-
resetChildren();
116-
return NodeStatus::FAILURE;
100+
break;
101+
102+
case NodeStatus::IDLE: {
103+
throw LogicError("[", name(), "]: A children should not return IDLE");
117104
}
118105
}
119-
break;
106+
}
120107

121-
case NodeStatus::RUNNING: {
122-
// Still working. Check the next
123-
}
124-
break;
108+
const size_t required_success_count = successThreshold();
125109

126-
case NodeStatus::SKIPPED: {
127-
// Node requested to be skipped or halted. Check the next
128-
}
129-
break;
110+
if(success_count_ >= required_success_count ||
111+
(success_threshold_ < 0 && (success_count_ + skipped_count) >= required_success_count))
112+
{
113+
clear();
114+
resetChildren();
115+
return NodeStatus::SUCCESS;
116+
}
130117

131-
case NodeStatus::IDLE: {
132-
throw LogicError("[", name(), "]: A children should not return IDLE");
133-
}
118+
// It fails if it is not possible to succeed anymore or if
119+
// number of failures are equal to failure_threshold_
120+
if (((children_count - failure_count_) < required_success_count) ||
121+
(failure_count_ == failureThreshold()))
122+
{
123+
clear();
124+
resetChildren();
125+
return NodeStatus::FAILURE;
134126
}
135127
}
136128
// Skip if ALL the nodes have been skipped
137-
return all_skipped_ ? NodeStatus::SKIPPED : NodeStatus::RUNNING;
129+
return (skipped_count == children_count) ? NodeStatus::SKIPPED : NodeStatus::RUNNING;
130+
}
131+
132+
void ParallelNode::clear()
133+
{
134+
completed_list_.clear();
135+
success_count_ = 0;
136+
failure_count_ = 0;
138137
}
139138

140139
void ParallelNode::halt()
141140
{
142-
skip_list_.clear();
141+
clear();
143142
ControlNode::halt();
144143
}
145144

src/tree_node.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ NodeStatus TreeNode::executeTick()
7272
{
7373
// injected pre-callback
7474
bool substituted = false;
75-
if(_p->status == NodeStatus::IDLE)
75+
if(!isStatusCompleted(_p->status))
7676
{
7777
PreTickCallback callback;
7878
{
@@ -181,7 +181,7 @@ Expected<NodeStatus> TreeNode::checkPreConditions()
181181
if (result.cast<bool>())
182182
{
183183
// Some preconditions are applied only when the node is started
184-
if (_p->status == NodeStatus::IDLE)
184+
if (!isStatusCompleted(_p->status))
185185
{
186186
if (preID == PreCond::FAILURE_IF)
187187
{

tests/gtest_parallel.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* Copyright (C) 2015-2017 Michele Colledanchise - All Rights Reserved
2+
* Copyright (C) 2018-2023 Davide Faconti - All Rights Reserved
23
*
34
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
45
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
@@ -15,6 +16,7 @@
1516
#include "behaviortree_cpp/loggers/bt_observer.h"
1617
#include "condition_test_node.h"
1718
#include "behaviortree_cpp/bt_factory.h"
19+
#include "test_helper.hpp"
1820

1921
using BT::NodeStatus;
2022
using std::chrono::milliseconds;
@@ -495,3 +497,30 @@ TEST(Parallel, ParallelAll)
495497
ASSERT_EQ( 1, observer.getStatistics("third").success_count);
496498
}
497499
}
500+
501+
TEST(Parallel, Issue593)
502+
{
503+
static const char* xml_text = R"(
504+
<root BTCPP_format="4">
505+
<BehaviorTree ID="TestTree">
506+
<Sequence>
507+
<Script code="test := true"/>
508+
<Parallel failure_count="1" success_count="-1">
509+
<TestA _skipIf="test == true"/>
510+
<Sleep msec="100"/>
511+
</Parallel>
512+
</Sequence>
513+
</BehaviorTree>
514+
</root>
515+
)";
516+
using namespace BT;
517+
518+
BehaviorTreeFactory factory;
519+
std::array<int, 1> counters;
520+
RegisterTestTick(factory, "Test", counters);
521+
522+
auto tree = factory.createTreeFromText(xml_text);
523+
tree.tickWhileRunning();
524+
525+
ASSERT_EQ(0, counters[0]);
526+
}

0 commit comments

Comments
 (0)