Skip to content

Commit 4117e74

Browse files
Merge pull request #529 from BehaviorTree/preaparing-4.1
2 parents aaab50f + 3449eb0 commit 4117e74

30 files changed

+26861
-153
lines changed
File renamed without changes.

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,11 @@ list(APPEND BT_SOURCE
9292
src/shared_library.cpp
9393
src/tree_node.cpp
9494
src/script_parser.cpp
95+
src/json_export.cpp
9596
src/xml_parsing.cpp
9697

98+
src/actions/test_node.cpp
99+
97100
src/decorators/inverter_node.cpp
98101
src/decorators/repeat_node.cpp
99102
src/decorators/retry_node.cpp
@@ -113,6 +116,8 @@ list(APPEND BT_SOURCE
113116
src/loggers/bt_cout_logger.cpp
114117
src/loggers/bt_file_logger.cpp
115118
src/loggers/bt_minitrace_logger.cpp
119+
src/loggers/bt_observer.cpp
120+
src/loggers/groot2_publisher.cpp
116121

117122
3rdparty/tinyxml2/tinyxml2.cpp
118123
3rdparty/minitrace/minitrace.cpp

examples/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ CompileExample("t06_subtree_port_remapping")
2525
CompileExample("t07_load_multiple_xml")
2626
CompileExample("t08_additional_node_args")
2727
CompileExample("t09_scripting")
28-
28+
CompileExample("t10_observer")
29+
CompileExample("t11_replace_rules")
30+
CompileExample("t12_groot_howto")
2931

3032
CompileExample("ex01_wrap_legacy")
3133
CompileExample("ex02_runtime_ports")

examples/t10_observer.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#include "behaviortree_cpp/bt_factory.h"
2+
#include "behaviortree_cpp/loggers/bt_observer.h"
3+
4+
/** Show the use of the TreeObserver.
5+
*/
6+
7+
// clang-format off
8+
9+
static const char* xml_text = R"(
10+
<root BTCPP_format="4">
11+
12+
<BehaviorTree ID="MainTree">
13+
<Sequence>
14+
<Fallback>
15+
<AlwaysFailure name="failing_action"/>
16+
<SubTree ID="SubTreeA" name="mysub"/>
17+
</Fallback>
18+
<AlwaysSuccess name="last_action"/>
19+
</Sequence>
20+
</BehaviorTree>
21+
22+
<BehaviorTree ID="SubTreeA">
23+
<Sequence>
24+
<AlwaysSuccess name="action_subA"/>
25+
<SubTree ID="SubTreeB" name="sub_nested"/>
26+
<SubTree ID="SubTreeB" />
27+
</Sequence>
28+
</BehaviorTree>
29+
30+
<BehaviorTree ID="SubTreeB">
31+
<AlwaysSuccess name="action_subB"/>
32+
</BehaviorTree>
33+
34+
</root>
35+
)";
36+
37+
// clang-format on
38+
39+
int main()
40+
{
41+
BT::BehaviorTreeFactory factory;
42+
43+
factory.registerBehaviorTreeFromText(xml_text);
44+
auto tree = factory.createTree("MainTree");
45+
46+
// Helper function to print the tree.
47+
BT::printTreeRecursively(tree.rootNode());
48+
49+
// The purpose of the observer is to save some statistics about the number of times
50+
// a certain node returns SUCCESS or FAILURE.
51+
// This is particularly useful to create unit tests and to check if
52+
// a certain set of transitions happened as expected
53+
BT::TreeObserver observer(tree);
54+
55+
// Print the unique ID and the corresponding human readable path
56+
// Path is also expected to be unique.
57+
std::map<uint16_t, std::string> ordered_UID_to_path;
58+
for(const auto& [name, uid]: observer.pathToUID()) {
59+
ordered_UID_to_path[uid] = name;
60+
}
61+
62+
for(const auto& [uid, name]: ordered_UID_to_path) {
63+
std::cout << uid << " -> " << name << std::endl;
64+
}
65+
66+
67+
tree.tickWhileRunning();
68+
69+
// You can access a specific statistic, using is full path or the UID
70+
const auto& last_action_stats = observer.getStatistics("last_action");
71+
assert(last_action_stats.transitions_count > 0);
72+
73+
std::cout << "----------------" << std::endl;
74+
// print all the statistics
75+
for(const auto& [uid, name]: ordered_UID_to_path) {
76+
const auto& stats = observer.getStatistics(uid);
77+
78+
std::cout << "[" << name
79+
<< "] \tT/S/F: " << stats.transitions_count
80+
<< "/" << stats.success_count
81+
<< "/" << stats.failure_count
82+
<< std::endl;
83+
}
84+
85+
return 0;
86+
}

examples/t11_replace_rules.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#include "behaviortree_cpp/bt_factory.h"
2+
#include "dummy_nodes.h"
3+
4+
// clang-format off
5+
6+
static const char* xml_text = R"(
7+
<root BTCPP_format="4">
8+
9+
<BehaviorTree ID="MainTree">
10+
<Sequence>
11+
<SaySomething name="talk" message="hello world"/>
12+
<Fallback>
13+
<AlwaysFailure name="failing_action"/>
14+
<SubTree ID="MySub" name="mysub"/>
15+
</Fallback>
16+
<SaySomething message="before last_action"/>
17+
<Script code="msg:='after last_action'"/>
18+
<AlwaysSuccess name="last_action"/>
19+
<SaySomething message="{msg}"/>
20+
</Sequence>
21+
</BehaviorTree>
22+
23+
<BehaviorTree ID="MySub">
24+
<Sequence>
25+
<AlwaysSuccess name="action_subA"/>
26+
<AlwaysSuccess name="action_subB"/>
27+
</Sequence>
28+
</BehaviorTree>
29+
30+
</root>
31+
)";
32+
33+
// clang-format on
34+
35+
int main(int argc, char** argv)
36+
{
37+
using namespace DummyNodes;
38+
BT::BehaviorTreeFactory factory;
39+
40+
factory.registerNodeType<SaySomething>("SaySomething");
41+
42+
// We use lambdasand registerSimpleAction, to create
43+
// a "dummy" node, that we want to create instead of a given one.
44+
45+
// Simple node that just prints its name and return SUCCESS
46+
factory.registerSimpleAction("TestAction", [](BT::TreeNode& self){
47+
std::cout << "TestAction substituting: "<< self.name() << std::endl;
48+
return BT::NodeStatus::SUCCESS;
49+
});
50+
51+
// Action that is meant to substitute SaySomething.
52+
// It will try to use the input port "message"
53+
factory.registerSimpleAction("TestSaySomething", [](BT::TreeNode& self){
54+
auto msg = self.getInput<std::string>("message");
55+
if (!msg)
56+
{
57+
throw BT::RuntimeError( "missing required input [message]: ", msg.error() );
58+
}
59+
std::cout << "TestSaySomething: " << msg.value() << std::endl;
60+
return BT::NodeStatus::SUCCESS;
61+
});
62+
63+
// These configurations will be passed to a TestNode
64+
BT::TestNodeConfig test_config;
65+
// Convert the node in asynchronous and wait 2000 ms
66+
test_config.async_delay = std::chrono::milliseconds(2000);
67+
// Execute this postcondition, once completed
68+
test_config.post_script = "msg ='message SUBSTITUED'";
69+
70+
//----------------------------
71+
// pass "no_sub" as first argument to avoid adding rules
72+
bool skip_substitution = (argc == 2) && std::string(argv[1]) == "no_sub";
73+
74+
if(!skip_substitution)
75+
{
76+
// Substitute nodes which match this wildcard pattern with TestAction
77+
factory.addSubstitutionRule("mysub/action_*", "TestAction");
78+
79+
// Substitute the node with name [talk] with TestSaySomething
80+
factory.addSubstitutionRule("talk", "TestSaySomething");
81+
82+
// Substitute the node with name [last_action] with a TestNode,
83+
// configured using test_config
84+
factory.addSubstitutionRule("last_action", test_config);
85+
}
86+
87+
factory.registerBehaviorTreeFromText(xml_text);
88+
89+
// During the construction phase of the tree, the substitution
90+
// rules will be used to instantiate the test nodes, instead of the
91+
// original ones.
92+
auto tree = factory.createTree("MainTree");
93+
tree.tickWhileRunning();
94+
95+
return 0;
96+
}

examples/t12_groot_howto.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include "crossdoor_nodes.h"
2+
#include "behaviortree_cpp/bt_factory.h"
3+
#include "behaviortree_cpp/loggers/groot2_publisher.h"
4+
#include "behaviortree_cpp/xml_parsing.h"
5+
6+
/** We are using the same example in Tutorial 5,
7+
* But this time we also show how to connect
8+
*/
9+
10+
// clang-format off
11+
12+
static const char* xml_text = R"(
13+
<root BTCPP_format="4">
14+
15+
<BehaviorTree ID="MainTree">
16+
<Sequence>
17+
<Fallback>
18+
<Inverter>
19+
<IsDoorClosed/>
20+
</Inverter>
21+
<SubTree ID="DoorClosed"/>
22+
</Fallback>
23+
<PassThroughDoor/>
24+
</Sequence>
25+
</BehaviorTree>
26+
27+
<BehaviorTree ID="DoorClosed">
28+
<Fallback>
29+
<OpenDoor/>
30+
<RetryUntilSuccessful num_attempts="5">
31+
<PickLock/>
32+
</RetryUntilSuccessful>
33+
<SmashDoor/>
34+
</Fallback>
35+
</BehaviorTree>
36+
37+
</root>
38+
)";
39+
40+
// clang-format on
41+
42+
int main()
43+
{
44+
BT::BehaviorTreeFactory factory;
45+
46+
CrossDoor cross_door;
47+
cross_door.registerNodes(factory);
48+
49+
// Groot2 editor requires a model of your registered Nodes.
50+
// You don't need to write that by hand, if can be automatically
51+
// generated using this command and imported.
52+
53+
std::string xml_models = BT::writeTreeNodesModelXML(factory);
54+
std::cout << " ---------- XML file containing models ----------\n"
55+
<< xml_models
56+
<< "-------------------------------------------------\n";
57+
58+
factory.registerBehaviorTreeFromText(xml_text);
59+
auto tree = factory.createTree("MainTree");
60+
61+
// Connect the Groot2Publisher. This will allow Groot2 to
62+
// get the tree and poll status updates.
63+
BT::Groot2Publisher publisher(tree);
64+
65+
while(1)
66+
{
67+
std::cout << "Start" << std::endl;
68+
cross_door.reset();
69+
tree.tickWhileRunning();
70+
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
71+
}
72+
73+
return 0;
74+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/* Copyright (C) 2022 Davide Faconti - All Rights Reserved
2+
*
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
5+
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
6+
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8+
*
9+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
11+
* 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.
12+
*/
13+
14+
#pragma once
15+
16+
#include "behaviortree_cpp/action_node.h"
17+
#include "behaviortree_cpp/decorators/timer_queue.h"
18+
#include "behaviortree_cpp/scripting/script_parser.hpp"
19+
20+
namespace BT
21+
{
22+
23+
struct TestNodeConfig
24+
{
25+
/// status to return when the action is completed
26+
NodeStatus return_status = NodeStatus::SUCCESS;
27+
28+
/// script to execute when actions is completed
29+
std::string post_script;
30+
31+
/// if async_delay > 0, this action become asynchronous and wait this amount of time
32+
std::chrono::milliseconds async_delay = std::chrono::milliseconds(0);
33+
34+
/// C++ callback to execute at the beginning
35+
std::function<void()> pre_func;
36+
37+
/// C++ callback to execute at the end
38+
std::function<void()> post_func;
39+
};
40+
41+
/**
42+
* @brief The TestNode is a Node that can be configure to:
43+
*
44+
* 1. Return a specific status (SUCCESS / FAILURE)
45+
* 2. Execute a post condition script (unless halted)
46+
* 3. Either complete immediately (synchronous action), or after a
47+
* given period of time (asynchronous action)
48+
*
49+
* This behavior is changed by the parameters pased with TestNodeConfig.
50+
*
51+
* This particular node is created by the factory when TestNodeConfig is
52+
* added as a substitution rule:
53+
*
54+
* TestNodeConfig test_config;
55+
* // change fields of test_config
56+
* factory.addSubstitutionRule(pattern, test_config);
57+
*
58+
* See tutorial 11 for more details.
59+
*/
60+
class TestNode : public BT::StatefulActionNode
61+
{
62+
public:
63+
TestNode(const std::string& name, const NodeConfig& config) :
64+
StatefulActionNode(name, config)
65+
{
66+
setRegistrationID("TestNode");
67+
}
68+
69+
static PortsList providedPorts()
70+
{
71+
return {};
72+
}
73+
74+
void setConfig(const TestNodeConfig& config);
75+
76+
private:
77+
78+
virtual NodeStatus onStart() override;
79+
80+
virtual NodeStatus onRunning() override;
81+
82+
virtual void onHalted() override;
83+
84+
NodeStatus onCompleted();
85+
86+
TestNodeConfig _test_config;
87+
ScriptFunction _executor;
88+
TimerQueue<> _timer;
89+
std::atomic_bool _completed;
90+
};
91+
92+
} // namespace BT

include/behaviortree_cpp/basic_types.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ enum class NodeStatus
4242
SKIPPED = 4,
4343
};
4444

45-
inline bool StatusActive(const NodeStatus& status)
45+
inline bool isStatusActive(const NodeStatus& status)
4646
{
4747
return status != NodeStatus::IDLE && status != NodeStatus::SKIPPED;
4848
}
4949

50-
inline bool StatusCompleted(const NodeStatus& status)
50+
inline bool isStatusCompleted(const NodeStatus& status)
5151
{
5252
return status == NodeStatus::SUCCESS || status == NodeStatus::FAILURE;
5353
}

0 commit comments

Comments
 (0)