19
19
#include < string>
20
20
#include < typeindex>
21
21
#include " behaviortree_cpp/basic_types.h"
22
+ #include " behaviortree_cpp/utils/strcat.hpp"
22
23
23
24
#if defined(_MSVC_LANG) && !defined(__clang__)
24
25
#define __bt_cplusplus (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
@@ -85,7 +86,7 @@ auto StrEqual = [](const char* str1, const char* str2) -> bool {
85
86
86
87
struct SubtreeModel
87
88
{
88
- std::unordered_map<std::string, BT::PortInfo> ports;
89
+ PortsList ports;
89
90
};
90
91
91
92
struct XMLParser ::PImpl
@@ -638,6 +639,7 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
638
639
const auto element_name = element->Name ();
639
640
const auto element_ID = element->Attribute (" ID" );
640
641
642
+ // TODO: Pull out this node type logic
641
643
auto node_type = convertFromString<NodeType>(element_name);
642
644
// name used by the factory
643
645
std::string type_ID;
@@ -682,16 +684,20 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
682
684
683
685
PortsRemapping port_remap;
684
686
NonPortAttributes other_attributes;
687
+ // Only relevant for subtrees
688
+ bool do_autoremap = false ;
685
689
690
+ // Parse ports and validate them where we can.
686
691
for (const XMLAttribute* att = element->FirstAttribute (); att; att = att->Next ())
687
692
{
688
693
const std::string port_name = att->Name ();
689
- const std::string port_value = att->Value ();
694
+ std::string port_value = att->Value ();
690
695
if (IsAllowedPortName (port_name))
691
696
{
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
+ }
695
701
if (manifest)
696
702
{
697
703
auto port_model_it = manifest->ports .find (port_name);
@@ -703,32 +709,34 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
703
709
" ) but not in the providedPorts() of its "
704
710
" registered node type." ));
705
711
}
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 ())
707
718
{
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
713
721
{
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);
726
730
}
727
731
}
728
732
}
729
733
730
734
port_remap[port_name] = port_value;
731
735
}
736
+ else if (node_type == NodeType::SUBTREE && port_name == " _autoremap" )
737
+ {
738
+ do_autoremap = convertFromString<bool >(port_value);
739
+ }
732
740
else if (!IsReservedAttribute (port_name))
733
741
{
734
742
other_attributes[port_name] = port_value;
@@ -740,6 +748,7 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
740
748
config.path = prefix_path + instance_name;
741
749
config.uid = output_tree.getUID ();
742
750
config.manifest = manifest;
751
+ config.auto_remapped = do_autoremap;
743
752
744
753
if (type_ID == instance_name)
745
754
{
@@ -769,9 +778,62 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
769
778
// ---------------------------------------------
770
779
TreeNode::Ptr new_node;
771
780
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
772
782
if (node_type == NodeType::SUBTREE)
773
783
{
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
+
775
837
new_node =
776
838
factory.instantiateTreeNode (instance_name, toStr (NodeType::SUBTREE), config);
777
839
auto subtree_node = dynamic_cast <SubTreeNode*>(new_node.get ());
@@ -927,7 +989,8 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID,
927
989
recursiveStep = [&](TreeNode::Ptr parent_node, Tree::Subtree::Ptr subtree,
928
990
std::string prefix, const XMLElement* element) {
929
991
// 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);
931
994
subtree->nodes .push_back (node);
932
995
933
996
// common case: iterate through all children
@@ -941,78 +1004,31 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID,
941
1004
}
942
1005
else // special case: SubTreeNode
943
1006
{
944
- auto new_bb = Blackboard::create (blackboard);
945
1007
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;
948
1009
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)
1003
1019
{
1004
- if (TreeNode::isBlackboardPointer (attr_value ))
1020
+ if (TreeNode::isBlackboardPointer (port_value ))
1005
1021
{
1006
1022
// 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 );
1009
1025
}
1010
1026
else
1011
1027
{
1012
1028
// constant string: just set that constant value into the BB
1013
1029
// IMPORTANT: this must not be autoremapped!!!
1014
1030
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 ));
1016
1032
new_bb->enableAutoRemapping (do_autoremap);
1017
1033
}
1018
1034
}
0 commit comments