Skip to content

Commit a363bdc

Browse files
authored
threshold child count dynamically in parallel control node (#363)
1 parent b8fd0b2 commit a363bdc

File tree

3 files changed

+100
-13
lines changed

3 files changed

+100
-13
lines changed

include/behaviortree_cpp_v3/controls/parallel_node.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ class ParallelNode : public ControlNode
4141

4242
unsigned int thresholdM();
4343
unsigned int thresholdFM();
44-
void setThresholdM(unsigned int threshold_M);
45-
void setThresholdFM(unsigned int threshold_M);
44+
void setThresholdM(int threshold_M);
45+
void setThresholdFM(int threshold_M);
4646

4747
private:
48-
unsigned int success_threshold_;
49-
unsigned int failure_threshold_;
48+
int success_threshold_;
49+
int failure_threshold_;
5050

5151
std::set<int> skip_list_;
5252

src/controls/parallel_node.cpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1212
*/
1313

14+
#include <algorithm>
15+
#include <cstddef>
16+
1417
#include "behaviortree_cpp_v3/controls/parallel_node.h"
1518

1619
namespace BT
@@ -58,12 +61,12 @@ NodeStatus ParallelNode::tick()
5861

5962
const size_t children_count = children_nodes_.size();
6063

61-
if( children_count < success_threshold_)
64+
if( children_count < thresholdM())
6265
{
6366
throw LogicError("Number of children is less than threshold. Can never succeed.");
6467
}
6568

66-
if( children_count < failure_threshold_)
69+
if( children_count < thresholdFM())
6770
{
6871
throw LogicError("Number of children is less than threshold. Can never fail.");
6972
}
@@ -94,7 +97,7 @@ NodeStatus ParallelNode::tick()
9497
}
9598
success_childred_num++;
9699

97-
if (success_childred_num == success_threshold_)
100+
if (success_childred_num == thresholdM())
98101
{
99102
skip_list_.clear();
100103
haltChildren();
@@ -112,8 +115,8 @@ NodeStatus ParallelNode::tick()
112115

113116
// It fails if it is not possible to succeed anymore or if
114117
// number of failures are equal to failure_threshold_
115-
if ((failure_childred_num > children_count - success_threshold_)
116-
|| (failure_childred_num == failure_threshold_))
118+
if ((failure_childred_num > children_count - thresholdM())
119+
|| (failure_childred_num == thresholdFM()))
117120
{
118121
skip_list_.clear();
119122
haltChildren();
@@ -144,20 +147,24 @@ void ParallelNode::halt()
144147

145148
unsigned int ParallelNode::thresholdM()
146149
{
147-
return success_threshold_;
150+
return success_threshold_ < 0
151+
? std::max(children_nodes_.size() + success_threshold_ + 1, static_cast<std::size_t>(0))
152+
: success_threshold_;
148153
}
149154

150155
unsigned int ParallelNode::thresholdFM()
151156
{
152-
return failure_threshold_;
157+
return failure_threshold_ < 0
158+
? std::max(children_nodes_.size() + failure_threshold_ + 1, static_cast<std::size_t>(0))
159+
: failure_threshold_;
153160
}
154161

155-
void ParallelNode::setThresholdM(unsigned int threshold_M)
162+
void ParallelNode::setThresholdM(int threshold_M)
156163
{
157164
success_threshold_ = threshold_M;
158165
}
159166

160-
void ParallelNode::setThresholdFM(unsigned int threshold_M)
167+
void ParallelNode::setThresholdFM(int threshold_M)
161168
{
162169
failure_threshold_ = threshold_M;
163170
}

tests/gtest_parallel.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,85 @@ TEST_F(SimpleParallelTest, Threshold_3)
145145
ASSERT_EQ(NodeStatus::SUCCESS, state);
146146
}
147147

148+
TEST_F(SimpleParallelTest, Threshold_neg2)
149+
{
150+
root.setThresholdM(-2);
151+
action_1.setTime( milliseconds(100) );
152+
action_2.setTime( milliseconds(500) ); // this takes a lot of time
153+
154+
BT::NodeStatus state = root.executeTick();
155+
// first tick, zero wait
156+
ASSERT_EQ(NodeStatus::SUCCESS, condition_1.status());
157+
ASSERT_EQ(NodeStatus::SUCCESS, condition_2.status());
158+
ASSERT_EQ(NodeStatus::RUNNING, action_1.status());
159+
ASSERT_EQ(NodeStatus::RUNNING, action_2.status());
160+
ASSERT_EQ(NodeStatus::RUNNING, state);
161+
162+
std::this_thread::sleep_for( milliseconds(150) );
163+
state = root.executeTick();
164+
// second tick: action1 should be completed, but not action2
165+
// nevertheless it is sufficient because threshold is 3
166+
ASSERT_EQ(NodeStatus::IDLE, condition_1.status());
167+
ASSERT_EQ(NodeStatus::IDLE, condition_2.status());
168+
ASSERT_EQ(NodeStatus::IDLE, action_1.status());
169+
ASSERT_EQ(NodeStatus::IDLE, action_2.status());
170+
ASSERT_EQ(NodeStatus::SUCCESS, state);
171+
}
172+
173+
174+
TEST_F(SimpleParallelTest, Threshold_neg1)
175+
{
176+
root.setThresholdM(-1);
177+
action_1.setTime( milliseconds(100) );
178+
action_2.setTime( milliseconds(500) ); // this takes a lot of time
179+
180+
BT::NodeStatus state = root.executeTick();
181+
// first tick, zero wait
182+
ASSERT_EQ(NodeStatus::SUCCESS, condition_1.status());
183+
ASSERT_EQ(NodeStatus::SUCCESS, condition_2.status());
184+
ASSERT_EQ(NodeStatus::RUNNING, action_1.status());
185+
ASSERT_EQ(NodeStatus::RUNNING, action_2.status());
186+
ASSERT_EQ(NodeStatus::RUNNING, state);
187+
188+
std::this_thread::sleep_for( milliseconds(150) );
189+
state = root.executeTick();
190+
// second tick: action1 should be completed, but not action2
191+
ASSERT_EQ(NodeStatus::SUCCESS, condition_1.status());
192+
ASSERT_EQ(NodeStatus::SUCCESS, condition_2.status());
193+
ASSERT_EQ(NodeStatus::SUCCESS, action_1.status());
194+
ASSERT_EQ(NodeStatus::RUNNING, action_2.status());
195+
ASSERT_EQ(NodeStatus::RUNNING, state);
196+
197+
std::this_thread::sleep_for( milliseconds(650) );
198+
state = root.executeTick();
199+
// third tick: all actions completed
200+
ASSERT_EQ(NodeStatus::IDLE, condition_1.status());
201+
ASSERT_EQ(NodeStatus::IDLE, condition_2.status());
202+
ASSERT_EQ(NodeStatus::IDLE, action_1.status());
203+
ASSERT_EQ(NodeStatus::IDLE, action_2.status());
204+
ASSERT_EQ(NodeStatus::SUCCESS, state);
205+
}
206+
207+
208+
TEST_F(SimpleParallelTest, Threshold_thresholdFneg1)
209+
{
210+
root.setThresholdM(1);
211+
root.setThresholdFM(-1);
212+
action_1.setTime( milliseconds(100) );
213+
action_1.setExpectedResult(NodeStatus::FAILURE);
214+
condition_1.setExpectedResult(NodeStatus::FAILURE);
215+
action_2.setTime( milliseconds(200) );
216+
condition_2.setExpectedResult(NodeStatus::FAILURE);
217+
action_2.setExpectedResult(NodeStatus::FAILURE);
218+
219+
BT::NodeStatus state = root.executeTick();
220+
ASSERT_EQ(NodeStatus::RUNNING, state);
221+
222+
std::this_thread::sleep_for(milliseconds(250));
223+
state = root.executeTick();
224+
ASSERT_EQ(NodeStatus::FAILURE, state);
225+
}
226+
148227
TEST_F(SimpleParallelTest, Threshold_2)
149228
{
150229
root.setThresholdM(2);
@@ -271,6 +350,7 @@ TEST_F(ComplexParallelTest, ConditionRightFalse_thresholdF_2)
271350
ASSERT_EQ(NodeStatus::SUCCESS, state);
272351
}
273352

353+
274354
TEST_F(ComplexParallelTest, ConditionRightFalseAction1Done)
275355
{
276356
condition_R.setExpectedResult(NodeStatus::FAILURE);

0 commit comments

Comments
 (0)