23 #if defined(_MSVC_LANG) && !defined(__clang__)
24 #define __bt_cplusplus (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
26 #define __bt_cplusplus __cplusplus
29 #if defined(__linux) || defined(__linux__)
30 #pragma GCC diagnostic push
31 #pragma GCC diagnostic ignored "-Wattributes"
44 #include <ament_index_cpp/get_package_share_directory.hpp>
53 std::string xsdAttributeType(
const BT::PortInfo& port_info)
57 return "blackboardType";
59 const auto& type_info = port_info.
type();
60 if((type_info ==
typeid(
int)) or (type_info ==
typeid(
unsigned int)))
62 return "integerOrBlackboardType";
64 else if(type_info ==
typeid(
double))
66 return "decimalOrBlackboardType";
68 else if(type_info ==
typeid(
bool))
70 return "booleanOrBlackboardType";
72 else if(type_info ==
typeid(std::string))
74 return "stringOrBlackboardType";
86 auto StrEqual = [](
const char* str1,
const char* str2) ->
bool {
87 return strcmp(str1, str2) == 0;
92 std::unordered_map<std::string, BT::PortInfo>
ports;
100 const std::string& prefix_path,
Tree& output_tree);
102 void recursivelyCreateSubtree(
const std::string& tree_ID,
const std::string& tree_path,
103 const std::string& prefix_path,
Tree& output_tree,
107 void getPortsRecursively(
const XMLElement* element,
108 std::vector<std::string>& output_ports);
110 void loadDocImpl(
XMLDocument* doc,
bool add_includes);
123 : factory(fact), current_path(
std::filesystem::current_path()), suffix_count(0)
129 current_path = std::filesystem::current_path();
130 opened_documents.clear();
135 void loadSubtreeModel(
const XMLElement* xml_root);
138 #if defined(__linux) || defined(__linux__)
139 #pragma GCC diagnostic pop
147 this->_p = std::move(other._p);
152 this->_p = std::move(other._p);
164 doc->
LoadFile(filepath.string().c_str());
166 _p->current_path = std::filesystem::absolute(filepath.parent_path());
168 _p->loadDocImpl(doc, add_includes);
178 _p->loadDocImpl(doc, add_includes);
183 std::vector<std::string> out;
184 for(
const auto& it :
_p->tree_roots)
186 out.push_back(it.first);
197 for(
auto sub_node = models_node->FirstChildElement(
"SubTree"); sub_node !=
nullptr;
198 sub_node = sub_node->NextSiblingElement(
"SubTree"))
200 auto subtree_id = sub_node->Attribute(
"ID");
201 auto& subtree_model = subtree_models[subtree_id];
203 std::pair<const char*, BT::PortDirection> port_types[3] = {
209 for(
const auto& [name, direction] : port_types)
211 for(
auto port_node = sub_node->FirstChildElement(name); port_node !=
nullptr;
212 port_node = port_node->NextSiblingElement(name))
215 auto name = port_node->Attribute(
"name");
218 throw RuntimeError(
"Missing attribute [name] in port (SubTree model)");
220 if(
auto default_value = port_node->Attribute(
"default"))
224 if(
auto description = port_node->Attribute(
"description"))
228 subtree_model.ports[name] = std::move(port);
247 throw RuntimeError(
"Invalid XML: missing root element");
250 auto format = xml_root->
Attribute(
"BTCPP_format");
253 std::cout <<
"Warnings: The first tag of the XML (<root>) should contain the "
254 "attribute [BTCPP_format=\"4\"]\n"
255 <<
"Please check if your XML is compatible with version 4.x of BT.CPP"
260 for(
auto incl_node = xml_root->
FirstChildElement(
"include"); incl_node !=
nullptr;
268 #if __bt_cplusplus >= 202002L
269 auto file_path(std::filesystem::path(incl_node->Attribute(
"path")));
271 auto file_path(std::filesystem::u8path(incl_node->Attribute(
"path")));
274 const char* ros_pkg_relative_path = incl_node->Attribute(
"ros_pkg");
276 if(ros_pkg_relative_path)
278 if(file_path.is_absolute())
280 std::cout <<
"WARNING: <include path=\"...\"> contains an absolute path.\n"
281 <<
"Attribute [ros_pkg] will be ignored." << std::endl;
285 std::string ros_pkg_path;
288 #elif defined USING_ROS2
290 ament_index_cpp::get_package_share_directory(ros_pkg_relative_path);
292 throw RuntimeError(
"Using attribute [ros_pkg] in <include>, but this library was "
293 "compiled without ROS support. Recompile the BehaviorTree.CPP "
296 file_path = std::filesystem::path(ros_pkg_path) / file_path;
300 if(!file_path.is_absolute())
310 current_path = std::filesystem::absolute(file_path.parent_path());
312 next_doc->
LoadFile(file_path.string().c_str());
320 std::unordered_map<std::string, BT::NodeType> registered_nodes;
323 registered_nodes.insert({ it.first, it.second.type });
327 doc->
Print(&printer);
336 for(
auto bt_node = xml_root->
FirstChildElement(
"BehaviorTree"); bt_node !=
nullptr;
339 std::string tree_name;
340 if(bt_node->Attribute(
"ID"))
342 tree_name = bt_node->Attribute(
"ID");
354 const std::unordered_map<std::string, BT::NodeType>& registered_nodes)
366 auto ThrowError = [&](
int line_num,
const std::string& text) {
368 snprintf(
buffer,
sizeof buffer,
"Error at line %d: -> %s", line_num, text.c_str());
372 auto ChildrenCount = [](
const XMLElement* parent_node) {
374 for(
auto node = parent_node->FirstChildElement(); node !=
nullptr;
375 node = node->NextSiblingElement())
387 throw RuntimeError(
"The XML must have a root node called <root>");
396 ThrowError(meta_sibling->GetLineNum(),
" Only a single node <TreeNodesModel> is "
406 const std::string name = node->Name();
407 if(name ==
"Action" || name ==
"Decorator" || name ==
"SubTree" ||
408 name ==
"Condition" || name ==
"Control")
410 const char* ID = node->Attribute(
"ID");
413 ThrowError(node->GetLineNum(),
"Error at line %d: -> The attribute "
414 "[ID] is mandatory");
421 int behavior_tree_count = 0;
425 behavior_tree_count++;
432 const int children_count = ChildrenCount(node);
433 const std::string name = node->Name();
434 const std::string ID = node->Attribute(
"ID") ? node->Attribute(
"ID") :
"";
435 const int line_number = node->GetLineNum();
437 if(name ==
"Decorator")
439 if(children_count != 1)
441 ThrowError(line_number,
"The tag <Decorator> must have exactly 1 "
446 ThrowError(line_number,
"The tag <Decorator> must have the "
450 else if(name ==
"Action")
452 if(children_count != 0)
454 ThrowError(line_number,
"The tag <Action> must not have any "
459 ThrowError(line_number,
"The tag <Action> must have the "
463 else if(name ==
"Condition")
465 if(children_count != 0)
467 ThrowError(line_number,
"The tag <Condition> must not have any "
472 ThrowError(line_number,
"The tag <Condition> must have the "
476 else if(name ==
"Control")
478 if(children_count == 0)
480 ThrowError(line_number,
"The tag <Control> must have at least 1 "
485 ThrowError(line_number,
"The tag <Control> must have the "
489 else if(name ==
"SubTree")
491 if(children_count != 0)
493 ThrowError(line_number,
"<SubTree> should not have any child");
497 ThrowError(line_number,
"The tag <SubTree> must have the "
500 if(registered_nodes.count(ID) != 0)
502 ThrowError(line_number,
"The attribute [ID] of tag <SubTree> must "
503 "not use the name of a registered Node");
506 else if(name ==
"BehaviorTree")
508 if(ID.empty() && behavior_tree_count > 1)
510 ThrowError(line_number,
"The tag <BehaviorTree> must have the "
513 if(children_count != 1)
515 ThrowError(line_number,
"The tag <BehaviorTree> must have exactly 1 "
518 if(registered_nodes.count(ID) != 0)
520 ThrowError(line_number,
"The attribute [ID] of tag <BehaviorTree> "
521 "must not use the name of a registered Node");
527 const auto search = registered_nodes.find(name);
528 bool found = (search != registered_nodes.end());
531 ThrowError(line_number, std::string(
"Node not recognized: ") + name);
536 if(children_count != 1)
538 ThrowError(line_number,
539 std::string(
"The node <") + name +
"> must have exactly 1 child");
544 if(children_count == 0)
546 ThrowError(line_number,
547 std::string(
"The node <") + name +
"> must have 1 or more children");
549 if(name ==
"ReactiveSequence")
551 size_t async_count = 0;
552 for(
auto child = node->FirstChildElement();
child !=
nullptr;
555 const std::string child_name =
child->Name();
556 const auto child_search = registered_nodes.find(child_name);
557 if(child_search == registered_nodes.end())
559 ThrowError(
child->GetLineNum(),
560 std::string(
"Unknown node type: ") + child_name);
562 const auto child_type = child_search->second;
564 ((child_name ==
"ThreadedAction") ||
565 (child_name ==
"StatefulActionNode") ||
566 (child_name ==
"CoroActionNode") || (child_name ==
"AsyncSequence")))
571 ThrowError(line_number, std::string(
"A ReactiveSequence cannot have more "
572 "than one async child."));
580 for(
auto child = node->FirstChildElement();
child !=
nullptr;
583 recursiveStep(
child);
587 for(
auto bt_root = xml_root->
FirstChildElement(
"BehaviorTree"); bt_root !=
nullptr;
590 recursiveStep(bt_root);
595 std::string main_tree_ID)
601 if(main_tree_ID.empty())
603 XMLElement* first_xml_root =
_p->opened_documents.front()->RootElement();
605 if(
auto main_tree_attribute = first_xml_root->
Attribute(
"main_tree_to_execute"))
607 main_tree_ID = main_tree_attribute;
609 else if(
_p->tree_roots.size() == 1)
612 main_tree_ID =
_p->tree_roots.begin()->first;
616 throw RuntimeError(
"[main_tree_to_execute] was not specified correctly");
623 throw RuntimeError(
"XMLParser::instantiateTree needs a non-empty "
627 _p->recursivelyCreateSubtree(main_tree_ID, {}, {}, output_tree, root_blackboard,
641 const std::string& prefix_path,
644 const auto element_name = element->
Name();
645 const auto element_ID = element->
Attribute(
"ID");
655 if(factory.builders().count(element_name) == 0)
657 throw RuntimeError(element_name,
" is not a registered node");
659 type_ID = element_name;
663 throw RuntimeError(
"Attribute [ID] is not allowed in <", type_ID,
">");
671 throw RuntimeError(
"Attribute [ID] is mandatory in <", type_ID,
">");
673 type_ID = element_ID;
678 const char* attr_name = element->
Attribute(
"name");
679 const std::string instance_name = (attr_name !=
nullptr) ? attr_name : type_ID;
683 auto manifest_it = factory.manifests().find(type_ID);
684 if(manifest_it != factory.manifests().end())
694 const std::string port_name = att->Name();
695 const std::string port_value = att->Value();
698 const std::string port_name = att->Name();
699 const std::string port_value = att->Value();
703 auto port_model_it =
manifest->ports.find(port_name);
704 if(port_model_it ==
manifest->ports.end())
707 "] is found in the XML (<", element->
Name(),
709 ") but not in the providedPorts() of its "
710 "registered node type."));
714 const auto& port_model = port_model_it->second;
715 bool is_blacbkboard = port_value.size() >= 3 && port_value.front() ==
'{' &&
716 port_value.back() ==
'}';
718 if(!is_blacbkboard && port_model.converter() && port_model.isStronglyTyped())
723 port_model.converter()(port_value);
725 catch(std::exception& ex)
727 auto msg =
StrCat(
"The port with name \"", port_name,
"\" and value \"",
728 port_value,
"\" can not be converted to ",
729 port_model.typeName());
736 port_remap[port_name] = port_value;
740 other_attributes[port_name] = port_value;
746 config.
path = prefix_path + instance_name;
750 if(type_ID == instance_name)
755 auto AddCondition = [&](
auto& conditions,
const char* attr_name,
auto ID) {
756 if(
auto script = element->
Attribute(attr_name))
758 conditions.insert({ ID, std::string(script) });
759 other_attributes.erase(attr_name);
765 auto pre =
static_cast<PreCond>(i);
770 auto post =
static_cast<PostCond>(i);
783 auto subtree_node =
dynamic_cast<SubTreeNode*
>(new_node.get());
790 auto msg =
StrCat(
"Missing manifest for element_ID: ", element_ID,
791 ". It shouldn't happen. Please report this issue.");
796 for(
const auto& [name_in_subtree,
_] : port_remap)
798 if(
manifest->ports.count(name_in_subtree) == 0)
800 throw RuntimeError(
"Possible typo? In the XML, you tried to remap port \"",
801 name_in_subtree,
"\" in node [", config.
path,
"(type ",
803 ")], but the manifest/model of this node does not contain a "
810 for(
const auto& [port_name, port_info] :
manifest->ports)
812 auto remap_it = port_remap.find(port_name);
813 if(remap_it == port_remap.end())
822 const auto port_key =
static_cast<std::string
>(param_res.value());
825 if(
auto prev_info = blackboard->entryInfo(port_key))
828 bool const port_type_mismatch =
829 (prev_info->isStronglyTyped() && port_info.isStronglyTyped() &&
830 prev_info->type() != port_info.type());
833 bool const string_input = (prev_info->type() ==
typeid(std::string));
837 blackboard->debugMessage();
839 throw RuntimeError(
"The creation of the tree failed because the port [",
840 port_key,
"] was initially created with type [",
841 demangle(prev_info->type()),
"] and, later type [",
842 demangle(port_info.type()),
"] was used somewhere else.");
848 blackboard->createEntry(port_key, port_info);
854 for(
const auto& remap_it : port_remap)
856 const auto& port_name = remap_it.first;
857 auto port_it =
manifest->ports.find(port_name);
858 if(port_it !=
manifest->ports.end())
860 auto direction = port_it->second.direction();
873 for(
const auto& port_it :
manifest->ports)
875 const std::string& port_name = port_it.first;
876 const PortInfo& port_info = port_it.second;
878 const auto direction = port_info.
direction();
880 if(!default_string.empty())
884 config.
input_ports.insert({ port_name, default_string });
891 config.
output_ports.insert({ port_name, default_string });
896 new_node = factory.instantiateTreeNode(instance_name, type_ID, config);
900 if(node_parent !=
nullptr)
902 if(
auto control_parent =
dynamic_cast<ControlNode*
>(node_parent.get()))
904 control_parent->addChild(new_node.get());
906 else if(
auto decorator_parent =
dynamic_cast<DecoratorNode*
>(node_parent.get()))
908 decorator_parent->setChild(new_node.get());
916 const std::string& tree_path,
917 const std::string& prefix_path,
927 std::string prefix,
const XMLElement* element) {
929 auto node = createNodeFromXML(element, blackboard, parent_node, prefix, output_tree);
930 subtree->nodes.push_back(node);
935 for(
auto child_element = element->FirstChildElement(); child_element;
936 child_element = child_element->NextSiblingElement())
938 recursiveStep(node, subtree, prefix, child_element);
944 const std::string subtree_ID = element->Attribute(
"ID");
945 std::unordered_map<std::string, std::string> subtree_remapping;
946 bool do_autoremap =
false;
948 for(
auto attr = element->FirstAttribute(); attr !=
nullptr; attr = attr->Next())
950 std::string attr_name = attr->Name();
951 std::string attr_value = attr->Value();
952 if(attr_value ==
"{=}")
954 attr_value =
StrCat(
"{", attr_name,
"}");
957 if(attr_name ==
"_autoremap")
960 new_bb->enableAutoRemapping(do_autoremap);
967 subtree_remapping.insert({ attr_name, attr_value });
972 auto subtree_model_it = subtree_models.find(subtree_ID);
973 if(subtree_model_it != subtree_models.end())
975 const auto& subtree_model_ports = subtree_model_it->second.ports;
979 for(
const auto& [port_name, port_info] : subtree_model_ports)
981 auto it = subtree_remapping.find(port_name);
983 if(it == subtree_remapping.end() && !do_autoremap)
986 if(port_info.defaultValueString().empty())
988 auto msg =
StrCat(
"In the <TreeNodesModel> the <Subtree ID=\"", subtree_ID,
989 "\"> is defining a mandatory port called [", port_name,
990 "], but you are not remapping it");
995 subtree_remapping.insert({ port_name, port_info.defaultValueString() });
1001 for(
const auto& [attr_name, attr_value] : subtree_remapping)
1007 new_bb->addSubtreeRemapping(attr_name, port_name);
1013 new_bb->enableAutoRemapping(
false);
1014 new_bb->set(attr_name,
static_cast<std::string
>(attr_value));
1015 new_bb->enableAutoRemapping(do_autoremap);
1019 std::string subtree_path = subtree->instance_name;
1020 if(!subtree_path.empty())
1022 subtree_path +=
"/";
1024 if(
auto name = element->Attribute(
"name"))
1026 subtree_path += name;
1033 recursivelyCreateSubtree(subtree_ID,
1036 output_tree, new_bb, node);
1040 auto it = tree_roots.find(tree_ID);
1041 if(it == tree_roots.end())
1043 throw std::runtime_error(std::string(
"Can't find a tree with name: ") + tree_ID);
1046 auto root_element = it->second->FirstChildElement();
1051 auto new_tree = std::make_shared<Tree::Subtree>();
1052 new_tree->blackboard = blackboard;
1053 new_tree->instance_name = tree_path;
1054 new_tree->tree_ID = tree_ID;
1055 output_tree.
subtrees.push_back(new_tree);
1057 recursiveStep(root_node, new_tree, prefix_path, root_element);
1061 std::vector<std::string>& output_ports)
1064 attr = attr->
Next())
1066 const char* attr_name = attr->Name();
1067 const char* attr_value = attr->Value();
1071 output_ports.push_back(
static_cast<std::string
>(port_name));
1078 getPortsRecursively(child_element, output_ports);
1088 for(
const auto& [port_name, port_info] : model.
ports)
1091 switch(port_info.direction())
1097 port_element = doc.
NewElement(
"output_port");
1105 if(port_info.type() !=
typeid(
void))
1109 if(!port_info.defaultValue().empty())
1111 port_element->
SetAttribute(
"default", port_info.defaultValueString().c_str());
1114 if(!port_info.description().empty())
1116 port_element->
SetText(port_info.description().c_str());
1123 auto metadata_root = doc.
NewElement(
"MetadataFields");
1125 for(
const auto& [name, value] : model.
metadata)
1127 auto metadata_element = doc.
NewElement(
"Metadata");
1128 metadata_element->
SetAttribute(name.c_str(), value.c_str());
1129 metadata_root->InsertEndChild(metadata_element);
1139 bool add_metadata,
bool add_builtin_models)
1145 if(
auto subtree =
dynamic_cast<const SubTreeNode*
>(&node))
1148 elem->
SetAttribute(
"ID", subtree->subtreeID().c_str());
1151 elem->
SetAttribute(
"_fullpath", subtree->config().path.c_str());
1187 parent_elem->InsertEndChild(elem);
1193 addNode(*
child, elem);
1196 else if(
auto decorator =
dynamic_cast<const DecoratorNode*
>(&node))
1200 addNode(*decorator->child(), elem);
1205 for(
const auto& subtree : tree.
subtrees)
1208 subtree_elem->
SetAttribute(
"ID", subtree->tree_ID.c_str());
1209 subtree_elem->
SetAttribute(
"_fullpath", subtree->instance_name.c_str());
1211 addNode(*subtree->nodes.front(), subtree_elem);
1219 std::map<std::string, const TreeNodeManifest*> ordered_models;
1220 for(
const auto& [registration_ID, model] : tree.
manifests)
1222 if(add_builtin_models || !temp_factory.
builtinNodes().count(registration_ID))
1224 ordered_models.insert({ registration_ID, &model });
1228 for(
const auto& [registration_ID, model] : ordered_models)
1235 bool include_builtin)
1246 std::map<std::string, const TreeNodeManifest*> ordered_models;
1248 for(
const auto& [registration_ID, model] : factory.
manifests())
1250 if(include_builtin || factory.
builtinNodes().count(registration_ID) == 0)
1252 ordered_models.insert({ registration_ID, &model });
1256 for(
const auto& [registration_ID, model] : ordered_models)
1262 doc.
Print(&printer);
1263 return std::string(printer.
CStr(),
size_t(printer.
CStrSize() - 1));
1274 std::map<std::string, const TreeNodeManifest*> ordered_models;
1275 for(
const auto& [registration_id, model] : factory.
manifests())
1277 ordered_models.insert({ registration_id, &model });
1284 "encoding=\"UTF-8\"");
1293 schema_element->
SetAttribute(
"xmlns:xs",
"http://www.w3.org/2001/XMLSchema");
1294 schema_element->
SetAttribute(
"elementFormDefault",
"qualified");
1297 auto parse_and_insert = [&doc](
XMLElement* parent_elem,
const char* str) {
1302 std::cerr <<
"Internal error parsing existing XML: " << tmp_doc.
ErrorStr()
1318 parse_and_insert(schema_element, R
"(
1319 <xs:simpleType name="blackboardType">
1320 <xs:restriction base="xs:string">
1321 <xs:pattern value="\{.*\}"/>
1324 <xs:simpleType name="booleanOrBlackboardType">
1325 <xs:union memberTypes="xs:boolean blackboardType"/>
1327 <xs:simpleType name="integerOrBlackboardType">
1328 <xs:union memberTypes="xs:integer blackboardType"/>
1330 <xs:simpleType name="decimalOrBlackboardType">
1331 <xs:union memberTypes="xs:decimal blackboardType"/>
1333 <xs:simpleType name="stringOrBlackboardType">
1334 <xs:union memberTypes="xs:string blackboardType"/>
1336 <xs:simpleType name="descriptionType">
1337 <xs:restriction base="xs:string">
1338 <xs:whiteSpace value="preserve"/>
1341 <xs:complexType name="inputPortType">
1343 <xs:extension base="xs:string">
1344 <xs:attribute name="name" type="xs:string" use="required"/>
1345 <xs:attribute name="type" type="xs:string" use="optional"/>
1346 <xs:attribute name="default" type="xs:string" use="optional"/>
1350 <xs:complexType name="outputPortType">
1352 <xs:extension base="xs:string">
1353 <xs:attribute name="name" type="xs:string" use="required"/>
1354 <xs:attribute name="type" type="xs:string" use="optional"/>
1358 <xs:attributeGroup name="preconditionAttributeGroup">
1359 <xs:attribute name="_failureIf" type="xs:string" use="optional"/>
1360 <xs:attribute name="_skipIf" type="xs:string" use="optional"/>
1361 <xs:attribute name="_successIf" type="xs:string" use="optional"/>
1362 <xs:attribute name="_while" type="xs:string" use="optional"/>
1363 </xs:attributeGroup>
1364 <xs:attributeGroup name="postconditionAttributeGroup">
1365 <xs:attribute name="_onSuccess" type="xs:string" use="optional"/>
1366 <xs:attribute name="_onFailure" type="xs:string" use="optional"/>
1367 <xs:attribute name="_post" type="xs:string" use="optional"/>
1368 <xs:attribute name="_onHalted" type="xs:string" use="optional"/>
1369 </xs:attributeGroup>)");
1377 parse_and_insert(schema_element, R
"(
1378 <xs:attributeGroup name="commonAttributeGroup">
1379 <xs:attribute name="name" type="xs:string" use="optional"/>
1380 <xs:attributeGroup ref="preconditionAttributeGroup"/>
1381 <xs:attributeGroup ref="postconditionAttributeGroup"/>
1382 </xs:attributeGroup>)");
1385 parse_and_insert(schema_element, R
"(
1386 <xs:complexType name="treeNodesModelNodeType">
1388 <xs:choice minOccurs="0" maxOccurs="unbounded">
1389 <xs:element name="input_port" type="inputPortType"/>
1390 <xs:element name="output_port" type="outputPortType"/>
1392 <xs:element name="description" type="descriptionType" minOccurs="0" maxOccurs="1"/>
1394 <xs:attribute name="ID" type="xs:string" use="required"/>
1396 <xs:group name="treeNodesModelNodeGroup">
1398 <xs:element name="Action" type="treeNodesModelNodeType"/>
1399 <xs:element name="Condition" type="treeNodesModelNodeType"/>
1400 <xs:element name="Control" type="treeNodesModelNodeType"/>
1401 <xs:element name="Decorator" type="treeNodesModelNodeType"/>
1407 const auto root_element_xsd = R
"(
1408 <xs:element name="root">
1411 <xs:choice minOccurs="0" maxOccurs="unbounded">
1412 <xs:element ref="include"/>
1413 <xs:element ref="BehaviorTree"/>
1415 <xs:element ref="TreeNodesModel" minOccurs="0" maxOccurs="1"/>
1417 <xs:attribute name="BTCPP_format" type="xs:string" use="required"/>
1418 <xs:attribute name="main_tree_to_execute" type="xs:string" use="optional"/>
1422 parse_and_insert(schema_element, root_element_xsd);
1428 std::ostringstream xsd;
1429 xsd <<
"<xs:choice>";
1430 for(
const auto& [registration_id, model] : ordered_models)
1432 xsd <<
"<xs:element name=\"" << registration_id <<
"\" type=\"" << registration_id
1435 xsd <<
"</xs:choice>";
1436 parse_and_insert(one_node_group, xsd.str().c_str());
1441 parse_and_insert(schema_element, R
"(
1442 <xs:element name="include">
1444 <xs:attribute name="path" type="xs:string" use="required"/>
1445 <xs:attribute name="ros_pkg" type="xs:string" use="optional"/>
1451 parse_and_insert(schema_element, R
"(
1452 <xs:element name="BehaviorTree">
1454 <xs:group ref="oneNodeGroup"/>
1455 <xs:attribute name="ID" type="xs:string" use="required"/>
1461 parse_and_insert(schema_element, R
"(
1462 <xs:element name="TreeNodesModel">
1464 <xs:group ref="treeNodesModelNodeGroup" minOccurs="0" maxOccurs="unbounded"/>
1470 for(
const auto& [registration_id, model] : ordered_models)
1473 type->
SetAttribute(
"name", (model->registration_ID +
"Type").c_str());
1501 common_attr_group->
SetAttribute(
"ref",
"commonAttributeGroup");
1503 for(
const auto& [port_name, port_info] : model->ports)
1507 const auto xsd_attribute_type = xsdAttributeType(port_info);
1508 if(not xsd_attribute_type.empty())
1510 attr->
SetAttribute(
"type", xsd_attribute_type.c_str());
1512 if(not port_info.defaultValue().empty())
1514 attr->
SetAttribute(
"default", port_info.defaultValueString().c_str());
1522 if(model->registration_ID ==
"SubTree")
1524 parse_and_insert(type, R
"(
1525 <xs:attribute name="ID" type="xs:string" use="required"/>
1526 <xs:anyAttribute processContents="skip"/>
1533 doc.Print(&printer);
1534 return std::string(printer.
CStr(),
size_t(printer.
CStrSize() - 1));
1561 addTreeToXML(tree, doc, rootXML, add_metadata, add_builtin_models);
1564 doc.
Print(&printer);
1565 return std::string(printer.
CStr(),
size_t(printer.
CStrSize() - 1));