@@ -9,14 +9,17 @@ static const char* xml_text = R"(
99 <BehaviorTree ID="MainTree">
1010 <Sequence>
1111 <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>
2023 </Sequence>
2124 </BehaviorTree>
2225
@@ -30,95 +33,126 @@ static const char* xml_text = R"(
3033</root>
3134 )" ;
3235
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-
5136// clang-format on
5237
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+
5346int main (int argc, char ** argv)
5447{
5548 using namespace DummyNodes ;
5649 BT::BehaviorTreeFactory factory;
57-
5850 factory.registerNodeType <SaySomething>(" SaySomething" );
51+ factory.registerBehaviorTreeFromText (xml_text);
5952
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()
6273
63- // Simple node that just prints its name and return SUCCESS
6474 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;
6677 return BT::NodeStatus::SUCCESS;
6778 });
6879
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) {
7281 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;
7883 return BT::NodeStatus::SUCCESS;
7984 });
8085
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' " ;
8497
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
86108 {
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 " ) ;
90112
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" );
115115
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);
117119
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
118151 // During the construction phase of the tree, the substitution
119152 // rules will be used to instantiate the test nodes, instead of the
120153 // original ones.
121154 auto tree = factory.createTree (" MainTree" );
155+ std::cout << " \n ------ Output (substituted) ------\n " ;
122156 tree.tickWhileRunning ();
123157
124158 return 0 ;
0 commit comments