1919#include < string>
2020#include < typeindex>
2121#include " behaviortree_cpp/basic_types.h"
22+ #include " behaviortree_cpp/utils/strcat.hpp"
2223
2324#if defined(_MSVC_LANG) && !defined(__clang__)
2425#define __bt_cplusplus (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
@@ -85,7 +86,7 @@ auto StrEqual = [](const char* str1, const char* str2) -> bool {
8586
8687struct SubtreeModel
8788{
88- std::unordered_map<std::string, BT::PortInfo> ports;
89+ PortsList ports;
8990};
9091
9192struct XMLParser ::PImpl
@@ -638,6 +639,7 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
638639 const auto element_name = element->Name ();
639640 const auto element_ID = element->Attribute (" ID" );
640641
642+ // TODO: Pull out this node type logic
641643 auto node_type = convertFromString<NodeType>(element_name);
642644 // name used by the factory
643645 std::string type_ID;
@@ -682,16 +684,20 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
682684
683685 PortsRemapping port_remap;
684686 NonPortAttributes other_attributes;
687+ // Only relevant for subtrees
688+ bool do_autoremap = false ;
685689
690+ // Parse ports and validate them where we can.
686691 for (const XMLAttribute* att = element->FirstAttribute (); att; att = att->Next ())
687692 {
688693 const std::string port_name = att->Name ();
689- const std::string port_value = att->Value ();
694+ std::string port_value = att->Value ();
690695 if (IsAllowedPortName (port_name))
691696 {
692- const std::string port_name = att->Name ();
693- const std::string port_value = att->Value ();
694-
697+ if (port_value == " {=}" )
698+ {
699+ port_value = StrCat (" {" , port_name, " }" );
700+ }
695701 if (manifest)
696702 {
697703 auto port_model_it = manifest->ports .find (port_name);
@@ -703,32 +709,34 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
703709 " ) but not in the providedPorts() of its "
704710 " registered node type." ));
705711 }
706- else
712+
713+ const auto & port_model = port_model_it->second ;
714+ bool is_blacbkboard = port_value.size () >= 3 && port_value.front () == ' {' &&
715+ port_value.back () == ' }' ;
716+ // let's test already if conversion is possible
717+ if (!is_blacbkboard && port_model.converter () && port_model.isStronglyTyped ())
707718 {
708- const auto & port_model = port_model_it->second ;
709- bool is_blacbkboard = port_value.size () >= 3 && port_value.front () == ' {' &&
710- port_value.back () == ' }' ;
711- // let's test already if conversion is possible
712- if (!is_blacbkboard && port_model.converter () && port_model.isStronglyTyped ())
719+ // This may throw
720+ try
713721 {
714- // This may throw
715- try
716- {
717- port_model.converter ()(port_value);
718- }
719- catch (std::exception& ex)
720- {
721- auto msg = StrCat (" The port with name \" " , port_name, " \" and value \" " ,
722- port_value, " \" can not be converted to " ,
723- port_model.typeName ());
724- throw LogicError (msg);
725- }
722+ port_model.converter ()(port_value);
723+ }
724+ catch (std::exception& ex)
725+ {
726+ auto msg =
727+ StrCat (" The port with name \" " , port_name, " \" and value \" " , port_value,
728+ " \" can not be converted to " , port_model.typeName ());
729+ throw LogicError (msg);
726730 }
727731 }
728732 }
729733
730734 port_remap[port_name] = port_value;
731735 }
736+ else if (node_type == NodeType::SUBTREE && port_name == " _autoremap" )
737+ {
738+ do_autoremap = convertFromString<bool >(port_value);
739+ }
732740 else if (!IsReservedAttribute (port_name))
733741 {
734742 other_attributes[port_name] = port_value;
@@ -740,6 +748,7 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
740748 config.path = prefix_path + instance_name;
741749 config.uid = output_tree.getUID ();
742750 config.manifest = manifest;
751+ config.auto_remapped = do_autoremap;
743752
744753 if (type_ID == instance_name)
745754 {
@@ -769,9 +778,62 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
769778 // ---------------------------------------------
770779 TreeNode::Ptr new_node;
771780
781+ // TODO: in order to set the config at this point, we need the subtree model, which is parsed after this function call in recursivelyCreateSubtree
772782 if (node_type == NodeType::SUBTREE)
773783 {
774- config.input_ports = port_remap;
784+ // check if this subtree has a model. If it does,
785+ // we want to check if all the mandatory ports were remapped and
786+ // add default ones, if necessary.
787+ auto subtree_model_it = subtree_models.find (type_ID);
788+ if (subtree_model_it != subtree_models.end ())
789+ {
790+ const PortsList& subtree_model_ports = subtree_model_it->second .ports ;
791+ // check if:
792+ // - remapping contains mandatory ports
793+ // - if any of these has default value
794+ for (const auto & [port_name, port_info] : subtree_model_ports)
795+ {
796+ auto it = port_remap.find (port_name);
797+ // don't override existing remapping
798+ if (it == port_remap.end () && !do_autoremap)
799+ {
800+ // remapping is not explicitly defined in the XML: use the model
801+ if (port_info.defaultValueString ().empty ())
802+ {
803+ auto msg = StrCat (" In the <TreeNodesModel> the <Subtree ID=\" " , type_ID,
804+ " \" > is defining a mandatory port called [" , port_name,
805+ " ], but you are not remapping it" );
806+ throw RuntimeError (msg);
807+ }
808+ port_remap.insert ({ port_name, port_info.defaultValueString () });
809+ }
810+ }
811+ }
812+ // populate the node config
813+ for (const auto & [port_name, port_value] : port_remap)
814+ {
815+ PortDirection direction = PortDirection::INPUT;
816+ if (subtree_model_it != subtree_models.end ())
817+ {
818+ const PortsList& subtree_model_ports = subtree_model_it->second .ports ;
819+ if (const auto & it = subtree_model_ports.find (port_name);
820+ it != subtree_model_ports.end ())
821+ {
822+ direction = it->second .direction ();
823+ }
824+ }
825+
826+ // Include the ports in the node config
827+ if (direction == PortDirection::INPUT || direction == PortDirection::INOUT)
828+ {
829+ config.input_ports [port_name] = port_value;
830+ }
831+ if (direction == PortDirection::OUTPUT || direction == PortDirection::INOUT)
832+ {
833+ config.output_ports [port_name] = port_value;
834+ }
835+ }
836+
775837 new_node =
776838 factory.instantiateTreeNode (instance_name, toStr (NodeType::SUBTREE), config);
777839 auto subtree_node = dynamic_cast <SubTreeNode*>(new_node.get ());
@@ -927,7 +989,8 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID,
927989 recursiveStep = [&](TreeNode::Ptr parent_node, Tree::Subtree::Ptr subtree,
928990 std::string prefix, const XMLElement* element) {
929991 // create the node
930- auto node = createNodeFromXML (element, blackboard, parent_node, prefix, output_tree);
992+ TreeNode::Ptr node =
993+ createNodeFromXML (element, blackboard, parent_node, prefix, output_tree);
931994 subtree->nodes .push_back (node);
932995
933996 // common case: iterate through all children
@@ -941,78 +1004,31 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID,
9411004 }
9421005 else // special case: SubTreeNode
9431006 {
944- auto new_bb = Blackboard::create (blackboard);
9451007 const std::string subtree_ID = element->Attribute (" ID" );
946- std::unordered_map<std::string, std::string> subtree_remapping;
947- bool do_autoremap = false ;
1008+ TreeNode::ConstPtr const_node = node;
9481009
949- for (auto attr = element->FirstAttribute (); attr != nullptr ; attr = attr->Next ())
950- {
951- std::string attr_name = attr->Name ();
952- std::string attr_value = attr->Value ();
953- if (attr_value == " {=}" )
954- {
955- attr_value = StrCat (" {" , attr_name, " }" );
956- }
957-
958- if (attr_name == " _autoremap" )
959- {
960- do_autoremap = convertFromString<bool >(attr_value);
961- new_bb->enableAutoRemapping (do_autoremap);
962- continue ;
963- }
964- if (!IsAllowedPortName (attr->Name ()))
965- {
966- continue ;
967- }
968- subtree_remapping.insert ({ attr_name, attr_value });
969- }
970- // check if this subtree has a model. If it does,
971- // we want to check if all the mandatory ports were remapped and
972- // add default ones, if necessary
973- auto subtree_model_it = subtree_models.find (subtree_ID);
974- if (subtree_model_it != subtree_models.end ())
975- {
976- const auto & subtree_model_ports = subtree_model_it->second .ports ;
977- // check if:
978- // - remapping contains mondatory ports
979- // - if any of these has default value
980- for (const auto & [port_name, port_info] : subtree_model_ports)
981- {
982- auto it = subtree_remapping.find (port_name);
983- // don't override existing remapping
984- if (it == subtree_remapping.end () && !do_autoremap)
985- {
986- // remapping is not explicitly defined in the XML: use the model
987- if (port_info.defaultValueString ().empty ())
988- {
989- auto msg = StrCat (" In the <TreeNodesModel> the <Subtree ID=\" " , subtree_ID,
990- " \" > is defining a mandatory port called [" , port_name,
991- " ], but you are not remapping it" );
992- throw RuntimeError (msg);
993- }
994- else
995- {
996- subtree_remapping.insert ({ port_name, port_info.defaultValueString () });
997- }
998- }
999- }
1000- }
1001-
1002- for (const auto & [attr_name, attr_value] : subtree_remapping)
1010+ auto new_bb = Blackboard::create (blackboard);
1011+ const bool do_autoremap = const_node->config ().auto_remapped ;
1012+ new_bb->enableAutoRemapping (do_autoremap);
1013+
1014+ // Populate the subtree's blackboard with it's port values.
1015+ PortsRemapping subtree_remapping = const_node->config ().input_ports ;
1016+ const PortsRemapping output_ports = const_node->config ().output_ports ;
1017+ subtree_remapping.insert (output_ports.begin (), output_ports.end ());
1018+ for (const auto & [port_name, port_value] : subtree_remapping)
10031019 {
1004- if (TreeNode::isBlackboardPointer (attr_value ))
1020+ if (TreeNode::isBlackboardPointer (port_value ))
10051021 {
10061022 // do remapping
1007- StringView port_name = TreeNode::stripBlackboardPointer (attr_value );
1008- new_bb->addSubtreeRemapping (attr_name, port_name );
1023+ StringView pointer_name = TreeNode::stripBlackboardPointer (port_value );
1024+ new_bb->addSubtreeRemapping (port_name, pointer_name );
10091025 }
10101026 else
10111027 {
10121028 // constant string: just set that constant value into the BB
10131029 // IMPORTANT: this must not be autoremapped!!!
10141030 new_bb->enableAutoRemapping (false );
1015- new_bb->set (attr_name , static_cast <std::string>(attr_value ));
1031+ new_bb->set (port_name , static_cast <std::string>(port_value ));
10161032 new_bb->enableAutoRemapping (do_autoremap);
10171033 }
10181034 }
0 commit comments