16 #if defined(__linux) || defined(__linux__) 17 #pragma GCC diagnostic push 18 #pragma GCC diagnostic ignored "-Wattributes" 22 #pragma warning(disable : 4996) // do not complain about sprintf 46 void recursivelyCreateTree(
const std::string& tree_ID,
54 std::unordered_map<std::string,const XMLElement*>
tree_roots;
64 current_path( filesystem::
path::getcwd() ),
71 current_path = filesystem::path::getcwd();
72 opened_documents.clear();
78 #if defined(__linux) || defined(__linux__) 79 #pragma GCC diagnostic pop 83 _p( new
Pimpl(factory) )
99 filesystem::path file_path( filename );
110 doc->
Parse(xml_text.c_str(), xml_text.size());
120 sprintf(buffer,
"Error parsing the XML: %s", doc->
ErrorName() );
128 include_node !=
nullptr;
132 filesystem::path file_path( include_node->Attribute(
"path") );
134 if( include_node->Attribute(
"ros_pkg") )
137 if( file_path.is_absolute() )
139 std::cout <<
"WARNING: <include path=\"...\"> containes an absolute path.\n" 140 <<
"Attribute [ros_pkg] will be ignored."<< std::endl;
144 file_path = filesystem::path( ros_pkg_path ) / file_path;
147 throw RuntimeError(
"Using attribute [ros_pkg] in <include>, but this library was compiled " 148 "without ROS support. Recompile the BehaviorTree.CPP using catkin");
152 if( !file_path.is_absolute() )
154 file_path = current_path / file_path;
159 next_doc->
LoadFile(file_path.str().c_str());
160 loadDocImpl(next_doc);
167 std::string tree_name;
168 if (bt_node->Attribute(
"ID"))
170 tree_name = bt_node->Attribute(
"ID");
175 tree_roots.insert( {tree_name, bt_node} );
178 std::set<std::string> registered_nodes;
180 doc->
Print(&printer);
183 for(
const auto& it: factory.manifests())
185 registered_nodes.insert( it.first );
187 for(
const auto& it: tree_roots)
189 registered_nodes.insert( it.first );
196 const std::set<std::string>& registered_nodes)
200 auto xml_error = doc.
Parse( xml_text.c_str(), xml_text.size());
204 sprintf(buffer,
"Error parsing the XML: %s", doc.
ErrorName() );
209 auto StrEqual = [](
const char* str1,
const char* str2) ->
bool {
210 return strcmp(str1, str2) == 0;
213 auto ThrowError = [&](
int line_num,
const std::string& text) {
215 sprintf(buffer,
"Error at line %d: -> %s", line_num, text.c_str());
219 auto ChildrenCount = [](
const XMLElement* parent_node) {
221 for (
auto node = parent_node->FirstChildElement(); node !=
nullptr;
222 node = node->NextSiblingElement())
232 if (!xml_root || !StrEqual(xml_root->
Name(),
"root"))
234 throw RuntimeError(
"The XML must have a root node called <root>");
238 auto meta_sibling = models_root ? models_root->
NextSiblingElement(
"TreeNodesModel") :
nullptr;
242 ThrowError(meta_sibling->GetLineNum(),
243 " Only a single node <TreeNodesModel> is supported");
252 const char* name = node->
Name();
253 if (StrEqual(name,
"Action") || StrEqual(name,
"Decorator") ||
254 StrEqual(name,
"SubTree") || StrEqual(name,
"Condition"))
256 const char* ID = node->Attribute(
"ID");
259 ThrowError(node->GetLineNum(),
260 "Error at line %d: -> The attribute [ID] is mandatory");
268 std::function<void(const XMLElement*)> recursiveStep;
271 const int children_count = ChildrenCount(node);
272 const char* name = node->Name();
273 if (StrEqual(name,
"Decorator"))
275 if (children_count != 1)
277 ThrowError(node->GetLineNum(),
278 "The node <Decorator> must have exactly 1 child");
280 if (!node->Attribute(
"ID"))
282 ThrowError(node->GetLineNum(),
283 "The node <Decorator> must have the attribute [ID]");
286 else if (StrEqual(name,
"Action"))
288 if (children_count != 0)
290 ThrowError(node->GetLineNum(),
291 "The node <Action> must not have any child");
293 if (!node->Attribute(
"ID"))
295 ThrowError(node->GetLineNum(),
296 "The node <Action> must have the attribute [ID]");
299 else if (StrEqual(name,
"Condition"))
301 if (children_count != 0)
303 ThrowError(node->GetLineNum(),
304 "The node <Condition> must not have any child");
306 if (!node->Attribute(
"ID"))
308 ThrowError(node->GetLineNum(),
309 "The node <Condition> must have the attribute [ID]");
312 else if (StrEqual(name,
"Sequence") ||
313 StrEqual(name,
"SequenceStar") ||
314 StrEqual(name,
"Fallback") )
316 if (children_count == 0)
318 ThrowError(node->GetLineNum(),
319 "A Control node must have at least 1 child");
322 else if (StrEqual(name,
"SubTree"))
324 for (
auto child = node->FirstChildElement(); child !=
nullptr;
325 child = child->NextSiblingElement())
327 if( StrEqual(child->Name(),
"remap") )
329 ThrowError(node->GetLineNum(),
"<remap> was deprecated");
332 ThrowError(node->GetLineNum(),
"<SubTree> should not have any child");
336 if (!node->Attribute(
"ID"))
338 ThrowError(node->GetLineNum(),
339 "The node <SubTree> must have the attribute [ID]");
345 bool found = ( registered_nodes.find(name) != registered_nodes.end() );
348 ThrowError(node->GetLineNum(),
349 std::string(
"Node not recognized: ") + name);
353 if (StrEqual(name,
"SubTree") ==
false)
355 for (
auto child = node->FirstChildElement(); child !=
nullptr;
356 child = child->NextSiblingElement())
358 recursiveStep(child);
363 std::vector<std::string> tree_names;
366 for (
auto bt_root = xml_root->
FirstChildElement(
"BehaviorTree"); bt_root !=
nullptr;
370 if (bt_root->Attribute(
"ID"))
372 tree_names.emplace_back(bt_root->Attribute(
"ID"));
374 if (ChildrenCount(bt_root) != 1)
376 ThrowError(bt_root->GetLineNum(),
377 "The node <BehaviorTree> must have exactly 1 child");
381 recursiveStep(bt_root->FirstChildElement());
385 if (xml_root->
Attribute(
"main_tree_to_execute"))
387 std::string main_tree = xml_root->
Attribute(
"main_tree_to_execute");
388 if (std::find(tree_names.begin(), tree_names.end(), main_tree) == tree_names.end())
390 throw RuntimeError(
"The tree specified in [main_tree_to_execute] can't be found");
397 throw RuntimeError(
"If you don't specify the attribute [main_tree_to_execute], " 398 "Your file must contain a single BehaviorTree");
409 std::string main_tree_ID;
410 if (xml_root->
Attribute(
"main_tree_to_execute"))
412 main_tree_ID = xml_root->
Attribute(
"main_tree_to_execute");
419 throw RuntimeError(
"[main_tree_to_execute] was not specified correctly");
422 if( !root_blackboard )
424 throw RuntimeError(
"XMLParser::instantiateTree needs a non-empty root_blackboard");
434 if( output_tree.
nodes.size() > 0)
445 const std::string element_name = element->
Name();
447 std::string instance_name;
450 if (element_name ==
"Action" || element_name ==
"Decorator" || element_name ==
"Condition")
459 const char* attr_alias = element->
Attribute(
"name");
462 instance_name = attr_alias;
469 if (element_name ==
"SubTree")
471 instance_name = element->
Attribute(
"ID");
476 if (element_name !=
"SubTree")
480 const std::string attribute_name = att->Name();
481 if (attribute_name !=
"ID" && attribute_name !=
"name")
483 remapping_parameters[attribute_name] = att->Value();
493 if( factory.builders().count(ID) != 0)
495 const auto&
manifest = factory.manifests().at(ID);
498 for(
const auto& remapping_it: remapping_parameters)
500 if(
manifest.ports.count( remapping_it.first ) == 0 )
502 throw RuntimeError(
"Possible typo? In the XML, you tried to remap port \"",
503 remapping_it.first,
"\" in node [", ID,
" / ", instance_name,
504 "], but the manifest of this node does not contain a port with this name.");
509 for(
const auto& port_it:
manifest.ports)
511 const std::string& port_name = port_it.first;
512 const auto& port_info = port_it.second;
514 auto remap_it = remapping_parameters.find(port_name);
515 if( remap_it == remapping_parameters.end())
519 StringView remapping_value = remap_it->second;
523 const auto& port_key = remapped_res.value().to_string();
525 auto prev_info = blackboard->portInfo( port_key );
529 blackboard->setPortInfo( port_key, port_info );
533 if( prev_info->type() && port_info.type() &&
534 prev_info->type()!= port_info.type())
536 blackboard->debugMessage();
538 throw RuntimeError(
"The creation of the tree failed because the port [", port_key,
539 "] was initially created with type [",
demangle( prev_info->type() ),
540 "] and, later type [",
demangle( port_info.type() ),
541 "] was used somewhere else." );
548 for(
const auto& remap_it: remapping_parameters)
550 const auto& port_name = remap_it.first;
551 auto port_it =
manifest.ports.find( port_name );
552 if( port_it !=
manifest.ports.end() )
554 auto direction = port_it->second.direction();
566 for (
const auto& port_it:
manifest.ports)
568 const std::string& port_name = port_it.first;
569 const PortInfo& port_info = port_it.second;
579 child_node = factory.instantiateTreeNode(instance_name, ID, config);
581 else if( tree_roots.count(ID) != 0) {
582 child_node = std::make_unique<DecoratorSubtreeNode>( instance_name );
585 throw RuntimeError( ID,
" is not a registered node, nor a Subtree");
590 if (
auto control_parent = dynamic_cast<ControlNode*>(node_parent.get()))
592 control_parent->addChild(child_node.get());
594 if (
auto decorator_parent = dynamic_cast<DecoratorNode*>(node_parent.get()))
596 decorator_parent->setChild(child_node.get());
607 std::function<void(const TreeNode::Ptr&, const XMLElement*)> recursiveStep;
612 auto node = createNodeFromXML(element, blackboard, parent);
613 output_tree.
nodes.push_back(node);
619 for (
const XMLAttribute* attr = element->FirstAttribute(); attr !=
nullptr; attr = attr->
Next())
621 new_bb->addSubtreeRemapping( attr->Name(), attr->Value() );
625 recursivelyCreateTree( node->name(), output_tree, new_bb, node );
629 for (
auto child_element = element->FirstChildElement(); child_element;
630 child_element = child_element->NextSiblingElement())
632 recursiveStep(node, child_element);
637 auto root_element = tree_roots[tree_ID]->FirstChildElement();
640 recursiveStep(root_parent, root_element);
656 for (
auto& model_it : factory.
manifests())
658 const auto& registration_ID = model_it.first;
659 const auto& model = model_it.second;
661 if( factory.
builtinNodes().count( registration_ID ) != 0)
671 element->
SetAttribute(
"ID", model.registration_ID.c_str());
673 for (
auto& port : model.ports)
675 const auto& port_name = port.first;
676 const auto& port_info = port.second;
679 switch( port_info.direction() )
687 if( port_info.type() )
691 if( !port_info.defaultValue().empty() )
693 port_element->
SetAttribute(
"default", port_info.defaultValue().c_str() );
696 if( !port_info.description().empty() )
698 port_element->
SetText( port_info.description().c_str() );
709 return std::string(printer.
CStr(), size_t(printer.
CStrSize() - 1));
Tree buildTreeFromText(const BehaviorTreeFactory &factory, const std::string &text, const Blackboard::Ptr &blackboard)
void SetAttribute(const char *name, const char *value)
Sets the named attribute to value.
Pimpl(const BehaviorTreeFactory &fact)
static Blackboard::Ptr create(Blackboard::Ptr parent={})
const XMLElement * NextSiblingElement(const char *name=0) const
Get the next (right) sibling element of this node, with an optionally supplied name.
PortDirection direction() const
void recursivelyCreateTree(const std::string &tree_ID, Tree &output_tree, Blackboard::Ptr blackboard, const TreeNode::Ptr &root_parent)
static raw_event_t * buffer
std::string toStr(T value)
const XMLAttribute * Next() const
The next attribute in the list.
std::unordered_map< std::string, const XMLElement * > tree_roots
filesystem::path current_path
XMLNode * InsertFirstChild(XMLNode *addThis)
void loadDocImpl(BT_TinyXML2::XMLDocument *doc)
const XMLElement * FirstChildElement(const char *name=0) const
std::shared_ptr< Blackboard > Ptr
const std::set< std::string > & builtinNodes() const
List of builtin IDs.
Blackboard::Ptr blackboard
XMLElement * NewElement(const char *name)
void SetText(const char *inText)
XMLError LoadFile(const char *filename)
std::string demangle(char const *name)
XMLElement * RootElement()
bool Error() const
Return true if there was an error parsing the document.
The BehaviorTreeFactory is used to create instances of a TreeNode at run-time.
const std::string & defaultValue() const
const char * CStr() const
std::vector< Blackboard::Ptr > blackboard_stack
nonstd::string_view StringView
void Print(XMLPrinter *streamer=0) const
const BehaviorTreeFactory & factory
const XMLAttribute * FirstAttribute() const
Return the first attribute in the list.
XMLError Parse(const char *xml, size_t nBytes=(size_t)(-1))
PortsRemapping output_ports
Tree instantiateTree(const Blackboard::Ptr &root_blackboard) override
Simple class for manipulating paths on Linux/Windows/Mac OS.
ROSLIB_DECL std::string getPath(const std::string &package_name)
static volatile int count
const std::unordered_map< std::string, TreeNodeManifest > & manifests() const
Manifests of all the registered TreeNodes.
std::vector< TreeNode::Ptr > nodes
std::string writeTreeNodesModelXML(const BehaviorTreeFactory &factory)
The XMLParser is a class used to read the model of a BehaviorTree from file or text and instantiate t...
static const char * xml_text
void loadFromText(const std::string &xml_text) override
const char * ErrorName() const
std::list< std::unique_ptr< BT_TinyXML2::XMLDocument > > opened_documents
XMLParser(const BehaviorTreeFactory &factory)
const char * Name() const
Get the name of an element (which is the Value() of the node.)
std::shared_ptr< TreeNode > Ptr
Struct used to store a tree. If this object goes out of scope, the tree is destroyed.
void VerifyXML(const std::string &xml_text, const std::set< std::string > ®istered_nodes)
const char * Attribute(const char *name, const char *value=0) const
TreeNode::Ptr createNodeFromXML(const XMLElement *element, const Blackboard::Ptr &blackboard, const TreeNode::Ptr &node_parent)
void loadFromFile(const std::string &filename) override
std::basic_string< CharT, Traits > to_string(basic_string_view< CharT, Traits > v)
PortsRemapping input_ports
Tree buildTreeFromFile(const BehaviorTreeFactory &factory, const std::string &filename, const Blackboard::Ptr &blackboard)
XMLNode * InsertEndChild(XMLNode *addThis)
static Optional< StringView > getRemappedKey(StringView port_name, StringView remapping_value)
std::unordered_map< std::string, std::string > PortsRemapping