Skip to content

Commit 6042049

Browse files
committed
Event based trigger introduced
Added a new mechanism to emit "state changed" events that can "wake up" a tree. In short, it just provide an interruptible "sleep" function.
1 parent a4b115a commit 6042049

23 files changed

+219
-32
lines changed

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ to visualize, record, replay and analyze state transitions.
2323
## Community
2424

2525
If this documentation doesn't answer your questions or if you simply want to
26-
connect with the community of BT.CPP users, visit https://discourse.behaviortree.dev/
26+
connect with the community of BT.CPP users, visit [discourse.behaviortree.dev](https://discourse.behaviortree.dev/).
2727

2828
## What is a Behavior Tree?
2929

docs/tutorial_04_sequence.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ NodeStatus MoveBaseAction::tick()
6363
int count = 0;
6464

6565
// Pretend that "computing" takes 250 milliseconds.
66-
// It is up to you to check periodicall _halt_requested and interrupt
66+
// It is up to you to check periodically _halt_requested and interrupt
6767
// this tick() if it is true.
6868
while (!_halt_requested && count++ < 25)
6969
{
70-
SleepMS(10);
70+
std::this_thread::sleep_for( std::chrono::milliseconds(10) );
7171
}
7272

7373
std::cout << "[ MoveBase: FINISHED ]" << std::endl;
@@ -105,6 +105,7 @@ The following example should use a simple `SequenceNode`.
105105
int main()
106106
{
107107
using namespace DummyNodes;
108+
using std::chrono::milliseconds;
108109

109110
BehaviorTreeFactory factory;
110111
factory.registerSimpleCondition("BatteryOK", std::bind(CheckBattery));
@@ -118,11 +119,11 @@ int main()
118119
std::cout << "\n--- 1st executeTick() ---" << std::endl;
119120
status = tree.tickRoot();
120121

121-
SleepMS(150);
122+
tree.sleep( milliseconds(150) );
122123
std::cout << "\n--- 2nd executeTick() ---" << std::endl;
123124
status = tree.tickRoot();
124125

125-
SleepMS(150);
126+
tree.sleep( milliseconds(150) );
126127
std::cout << "\n--- 3rd executeTick() ---" << std::endl;
127128
status = tree.tickRoot();
128129

@@ -191,5 +192,15 @@ Expected output:
191192
Robot says: "mission completed!"
192193
```
193194

195+
## Event Driven trees?
196+
197+
!!! important
198+
We used the command `tree.sleep()` instead of `std::this_thread::sleep_for()` for a reason.
199+
200+
The method `Tree::sleep()` should be preferred, because it can be interrupted by a Node in the tree when
201+
"something changed".
202+
Tree::sleep() will be interrupted when an `AsyncActionNode::tick()` is completed or, more generally,
203+
when the method `TreeNode::emitStateChanged()` is invoked.
204+
194205

195206

docs/tutorial_05_subtrees.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,16 @@ int main()
110110
while( status == NodeStatus::RUNNING)
111111
{
112112
status = tree.tickRoot();
113-
CrossDoor::SleepMS(1); // optional sleep to avoid "busy loops"
113+
// IMPORTANT: you must always add some sleep if you call tickRoot()
114+
// in a loop, to avoid using 100% of your CPU (busy loop).
115+
// The method Tree::sleep() is recommended, because it can be
116+
// interrupted by an internal event inside the tree.
117+
tree.sleep( milliseconds(10) );
118+
}
119+
if( LOOP )
120+
{
121+
std::this_thread::sleep_for( milliseconds(1000) );
114122
}
115-
CrossDoor::SleepMS(2000);
116123
}
117124
return 0;
118125
}

docs/tutorial_06_subtree_ports.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ int main()
7878
while( status == NodeStatus::RUNNING)
7979
{
8080
status = tree.tickRoot();
81-
SleepMS(1); // optional sleep to avoid "busy loops"
81+
// IMPORTANT: add sleep to avoid busy loops.
82+
// You should use Tree::sleep(). Don't be afraid to run this at 1 KHz.
83+
tree.sleep( std::chrono::milliseconds(1) );
8284
}
8385

8486
// let's visualize some information about the current state of the blackboards.

docs/tutorial_09_coroutines.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,10 @@ int main()
147147
auto tree = factory.createTreeFromText(xml_text);
148148

149149
//---------------------------------------
150-
// keep executin tick until it returns etiher SUCCESS or FAILURE
150+
// keep executing tick until it returns either SUCCESS or FAILURE
151151
while( tree.tickRoot() == NodeStatus::RUNNING)
152152
{
153-
std::this_thread::sleep_for( Milliseconds(10) );
153+
tree.sleep( std::chrono::milliseconds(10) );
154154
}
155155
return 0;
156156
}

examples/t04_reactive_sequence.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,11 @@ void Assert(bool condition)
5757
throw RuntimeError("this is not what I expected");
5858
}
5959

60+
6061
int main()
6162
{
6263
using namespace DummyNodes;
64+
using std::chrono::milliseconds;
6365

6466
BehaviorTreeFactory factory;
6567
factory.registerSimpleCondition("BatteryOK", std::bind(CheckBattery));
@@ -85,12 +87,12 @@ int main()
8587
status = tree.tickRoot();
8688
Assert(status == NodeStatus::RUNNING);
8789

88-
SleepMS(150);
90+
tree.sleep( milliseconds(150) );
8991
std::cout << "\n--- 2nd executeTick() ---" << std::endl;
9092
status = tree.tickRoot();
9193
Assert(status == NodeStatus::RUNNING);
9294

93-
SleepMS(150);
95+
tree.sleep( milliseconds(150) );
9496
std::cout << "\n--- 3rd executeTick() ---" << std::endl;
9597
status = tree.tickRoot();
9698
Assert(status == NodeStatus::SUCCESS);

examples/t05_crossdoor.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,24 @@ int main(int argc, char** argv)
8585

8686
const bool LOOP = ( argc == 2 && strcmp( argv[1], "loop") == 0);
8787

88+
using std::chrono::milliseconds;
8889
do
8990
{
9091
NodeStatus status = NodeStatus::RUNNING;
9192
// Keep on ticking until you get either a SUCCESS or FAILURE state
9293
while( status == NodeStatus::RUNNING)
9394
{
9495
status = tree.tickRoot();
95-
CrossDoor::SleepMS(1); // optional sleep to avoid "busy loops"
96+
// IMPORTANT: you must always add some sleep if you call tickRoot()
97+
// in a loop, to avoid using 100% of your CPU (busy loop).
98+
// The method Tree::sleep() is recommended, because it can be
99+
// interrupted by an internal event inside the tree.
100+
tree.sleep( milliseconds(10) );
101+
}
102+
if( LOOP )
103+
{
104+
std::this_thread::sleep_for( milliseconds(1000) );
96105
}
97-
CrossDoor::SleepMS(1000);
98106
}
99107
while(LOOP);
100108

examples/t06_subtree_port_remapping.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ int main()
6868
while( status == NodeStatus::RUNNING)
6969
{
7070
status = tree.tickRoot();
71-
SleepMS(1); // optional sleep to avoid "busy loops"
71+
// IMPORTANT: add sleep to avoid busy loops.
72+
// You should use Tree::sleep(). Don't be afraid to run this at 1 KHz.
73+
tree.sleep( std::chrono::milliseconds(1) );
7274
}
7375

7476
// let's visualize some information about the current state of the blackboards.

examples/t09_async_actions_coroutines.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,10 @@ int main()
115115
auto tree = factory.createTreeFromText(xml_text);
116116

117117
//---------------------------------------
118-
// keep executin tick until it returns etiher SUCCESS or FAILURE
118+
// keep executing tick until it returns either SUCCESS or FAILURE
119119
while( tree.tickRoot() == NodeStatus::RUNNING)
120120
{
121-
std::this_thread::sleep_for( std::chrono::milliseconds(10) );
121+
tree.sleep( std::chrono::milliseconds(10) );
122122
}
123123
return 0;
124124
}

include/behaviortree_cpp_v3/action_node.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ class SimpleActionNode : public SyncActionNode
113113
* - remember, with few exceptions, a halted AsyncAction must return NodeStatus::IDLE.
114114
*
115115
* For a complete example, look at __AsyncActionTest__ in action_test_node.h in the folder test.
116+
*
117+
* NOTE: when the thread is completed, i.e. the tick() returns its status,
118+
* a TreeNode::emitStateChanged() will be called.
116119
*/
117120
class AsyncActionNode : public ActionNodeBase
118121
{
@@ -136,7 +139,7 @@ class AsyncActionNode : public ActionNodeBase
136139

137140
std::exception_ptr exptr_;
138141
std::atomic_bool halt_requested_;
139-
std::future<NodeStatus> thread_handle_;
142+
std::future<void> thread_handle_;
140143
std::mutex m_;
141144
};
142145

0 commit comments

Comments
 (0)