@@ -9,14 +9,17 @@ static const char* xml_text = R"(
9
9
<BehaviorTree ID="MainTree">
10
10
<Sequence>
11
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}"/>
12
+
13
+ <SubTree ID="MySub" name="mysub"/>
14
+
15
+ <Script name="set_message" code="msg:= 'the original message' "/>
16
+ <SaySomething message="{msg}"/>
17
+
18
+ <Sequence name="counting">
19
+ <SaySomething message="1"/>
20
+ <SaySomething message="2"/>
21
+ <SaySomething message="3"/>
22
+ </Sequence>
20
23
</Sequence>
21
24
</BehaviorTree>
22
25
@@ -30,95 +33,126 @@ static const char* xml_text = R"(
30
33
</root>
31
34
)" ;
32
35
33
- static const char * json_text = R"(
34
- {
35
- "TestNodeConfigs": {
36
- "MyTest": {
37
- "async_delay": 2000,
38
- "return_status": "SUCCESS",
39
- "post_script": "msg ='message SUBSTITUED'"
40
- }
41
- },
42
-
43
- "SubstitutionRules": {
44
- "mysub/action_*": "TestAction",
45
- "talk": "TestSaySomething",
46
- "last_action": "MyTest"
47
- }
48
- }
49
- )" ;
50
-
51
36
// clang-format on
52
37
38
+ /* *
39
+ * @brief In this example we will see how we can substitute some nodes
40
+ * in the Tree above with
41
+ * @param argc
42
+ * @param argv
43
+ * @return
44
+ */
45
+
53
46
int main (int argc, char ** argv)
54
47
{
55
48
using namespace DummyNodes ;
56
49
BT::BehaviorTreeFactory factory;
57
-
58
50
factory.registerNodeType <SaySomething>(" SaySomething" );
51
+ factory.registerBehaviorTreeFromText (xml_text);
59
52
60
- // We use lambdas and registerSimpleAction, to create
61
- // a "dummy" node, that we want to create instead of a given one.
53
+ // let's check what the "original" tree should return
54
+ {
55
+ auto tree = factory.createTree (" MainTree" );
56
+
57
+ std::cout << " ----- Nodes fullPath() -------\n " ;
58
+ // as a reminder, let's print the full names of all the nodes
59
+ tree.applyVisitor (
60
+ [](BT::TreeNode* node) { std::cout << node->fullPath () << std::endl; });
61
+
62
+ std::cout << " \n ------ Output (original) ------\n " ;
63
+ tree.tickWhileRunning ();
64
+ }
65
+
66
+ // We have three mechanisms to create Nodes to be used as "mocks".
67
+ // We will see later how to use them.
68
+
69
+ // ---------------------------------------------------------------
70
+ // Mock type 1: register a specific "dummy" Node into the factory
71
+ // You can use any registration method, but to keep this short,
72
+ // we use registerSimpleAction()
62
73
63
- // Simple node that just prints its name and return SUCCESS
64
74
factory.registerSimpleAction (" DummyAction" , [](BT::TreeNode& self) {
65
- std::cout << " DummyAction substituting: " << self.name () << std::endl;
75
+ std::cout << " DummyAction substituting node with fullPath(): " << self.fullPath ()
76
+ << std::endl;
66
77
return BT::NodeStatus::SUCCESS;
67
78
});
68
79
69
- // Action that is meant to substitute SaySomething.
70
- // It will try to use the input port "message"
71
- factory.registerSimpleAction (" TestSaySomething" , [](BT::TreeNode& self) {
80
+ factory.registerSimpleAction (" DummySaySomething" , [](BT::TreeNode& self) {
72
81
auto msg = self.getInput <std::string>(" message" );
73
- if (!msg)
74
- {
75
- throw BT::RuntimeError (" missing required input [message]: " , msg.error ());
76
- }
77
- std::cout << " TestSaySomething: " << msg.value () << std::endl;
82
+ std::cout << " DummySaySomething: " << msg.value () << std::endl;
78
83
return BT::NodeStatus::SUCCESS;
79
84
});
80
85
81
- // ----------------------------
82
- // pass "no_sub" as first argument to avoid adding rules
83
- bool skip_substitution = (argc == 2 ) && std::string (argv[1 ]) == " no_sub" ;
86
+ // ---------------------------------------------------------------
87
+ // Mock type 2: Use our configurable BT::TestNode
88
+
89
+ // This is the configuration passed to the TestNode
90
+ BT::TestNodeConfig test_config;
91
+ // we want this to return always SUCCESS
92
+ test_config.return_status = BT::NodeStatus::SUCCESS;
93
+ // Convert the node in asynchronous and wait 2000 ms
94
+ test_config.async_delay = std::chrono::milliseconds (2000 );
95
+ // Execute this postcondition, once completed
96
+ test_config.post_script = " msg := 'message SUBSTITUTED' " ;
84
97
85
- if (!skip_substitution)
98
+ // this will be synchronous (async_delay is 0)
99
+ BT::TestNodeConfig counting_config;
100
+ test_config.return_status = BT::NodeStatus::SUCCESS;
101
+
102
+ // ---------------------------------------------------------------
103
+ // Next, we want to substitute one or more of out Nodes with this mocks
104
+ // The simplest way is to use a JSON file, otherwise we can do it manually.
105
+ bool const USE_JSON = true ;
106
+
107
+ if (!USE_JSON) // manually add substitution rules
86
108
{
87
- // we can use a JSON file to configure the substitution rules
88
- // or do it manually
89
- bool const USE_JSON = true ;
109
+ // Substitute nodes which match the wildcard pattern "mysub/action_*"
110
+ // with DummyAction
111
+ factory. addSubstitutionRule ( " mysub/action_* " , " DummyAction " ) ;
90
112
91
- if (USE_JSON)
92
- {
93
- factory.loadSubstitutionRuleFromJSON (json_text);
94
- }
95
- else
96
- {
97
- // Substitute nodes which match this wildcard pattern with TestAction
98
- factory.addSubstitutionRule (" mysub/action_*" , " TestAction" );
99
-
100
- // Substitute the node with name [talk] with TestSaySomething
101
- factory.addSubstitutionRule (" talk" , " TestSaySomething" );
102
-
103
- // This configuration will be passed to a TestNode
104
- BT::TestNodeConfig test_config;
105
- // Convert the node in asynchronous and wait 2000 ms
106
- test_config.async_delay = std::chrono::milliseconds (2000 );
107
- // Execute this postcondition, once completed
108
- test_config.post_script = " msg ='message SUBSTITUED'" ;
109
-
110
- // Substitute the node with name [last_action] with a TestNode,
111
- // configured using test_config
112
- factory.addSubstitutionRule (" last_action" , test_config);
113
- }
114
- }
113
+ // Substitute the node with name "talk" with DummySaySomething
114
+ factory.addSubstitutionRule (" talk" , " DummySaySomething" );
115
115
116
- factory.registerBehaviorTreeFromText (xml_text);
116
+ // Substitute the node with name "set_message" with
117
+ // the a BT::TestNode with the give configuration
118
+ factory.addSubstitutionRule (" set_message" , test_config);
117
119
120
+ // we can also substitute entire branches, for instance the Sequence "counting"
121
+ factory.addSubstitutionRule (" counting" , counting_config);
122
+ }
123
+ else // use a JSON file to apply substitution rules programmatically
124
+ {
125
+ // this JSON is equivalent to the code we wrote above
126
+ const char * json_text = R"(
127
+ {
128
+ "TestNodeConfigs": {
129
+ "NewMessage": {
130
+ "async_delay": 2000,
131
+ "return_status": "SUCCESS",
132
+ "post_script": "msg ='message SUBSTITUTED'"
133
+ },
134
+ "NoCounting": {
135
+ "return_status": "SUCCESS"
136
+ }
137
+ },
138
+
139
+ "SubstitutionRules": {
140
+ "mysub/action_*": "DummyAction",
141
+ "talk": "DummySaySomething",
142
+ "set_message": "NewMessage",
143
+ "counting": "NoCounting"
144
+ }
145
+ })" ;
146
+
147
+ factory.loadSubstitutionRuleFromJSON (json_text);
148
+ }
149
+ // ---------------------------------------------------------------
150
+ // IMPORTANT: all substiutions must be done BEFORE creating the tree
118
151
// During the construction phase of the tree, the substitution
119
152
// rules will be used to instantiate the test nodes, instead of the
120
153
// original ones.
121
154
auto tree = factory.createTree (" MainTree" );
155
+ std::cout << " \n ------ Output (substituted) ------\n " ;
122
156
tree.tickWhileRunning ();
123
157
124
158
return 0 ;
0 commit comments