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)
@@ -89,7 +90,7 @@ auto StrEqual = [](const char* str1, const char* str2) -> bool {
89
90
90
91
struct SubtreeModel
91
92
{
92
- std::unordered_map<std::string, BT::PortInfo> ports;
93
+ PortsList ports;
93
94
};
94
95
95
96
struct XMLParser ::PImpl
@@ -644,6 +645,7 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
644
645
const auto element_name = element->Name ();
645
646
const auto element_ID = element->Attribute (" ID" );
646
647
648
+ // TODO: Pull out this node type logic
647
649
auto node_type = convertFromString<NodeType>(element_name);
648
650
// name used by the factory
649
651
std::string type_ID;
@@ -688,16 +690,20 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
688
690
689
691
PortsRemapping port_remap;
690
692
NonPortAttributes other_attributes;
693
+ // Only relevant for subtrees
694
+ bool do_autoremap = false ;
691
695
696
+ // Parse ports and validate them where we can.
692
697
for (const XMLAttribute* att = element->FirstAttribute (); att; att = att->Next ())
693
698
{
694
699
const std::string port_name = att->Name ();
695
- const std::string port_value = att->Value ();
700
+ std::string port_value = att->Value ();
696
701
if (IsAllowedPortName (port_name))
697
702
{
698
- const std::string port_name = att->Name ();
699
- const std::string port_value = att->Value ();
700
-
703
+ if (port_value == " {=}" )
704
+ {
705
+ port_value = StrCat (" {" , port_name, " }" );
706
+ }
701
707
if (manifest)
702
708
{
703
709
auto port_model_it = manifest->ports .find (port_name);
@@ -709,32 +715,34 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
709
715
" ) but not in the providedPorts() of its "
710
716
" registered node type." ));
711
717
}
712
- else
718
+
719
+ const auto & port_model = port_model_it->second ;
720
+ bool is_blacbkboard = port_value.size () >= 3 && port_value.front () == ' {' &&
721
+ port_value.back () == ' }' ;
722
+ // let's test already if conversion is possible
723
+ if (!is_blacbkboard && port_model.converter () && port_model.isStronglyTyped ())
713
724
{
714
- const auto & port_model = port_model_it->second ;
715
- bool is_blacbkboard = port_value.size () >= 3 && port_value.front () == ' {' &&
716
- port_value.back () == ' }' ;
717
- // let's test already if conversion is possible
718
- if (!is_blacbkboard && port_model.converter () && port_model.isStronglyTyped ())
725
+ // This may throw
726
+ try
719
727
{
720
- // This may throw
721
- try
722
- {
723
- port_model.converter ()(port_value);
724
- }
725
- catch (std::exception& ex)
726
- {
727
- auto msg = StrCat (" The port with name \" " , port_name, " \" and value \" " ,
728
- port_value, " \" can not be converted to " ,
729
- port_model.typeName ());
730
- throw LogicError (msg);
731
- }
728
+ port_model.converter ()(port_value);
729
+ }
730
+ catch (std::exception& ex)
731
+ {
732
+ auto msg =
733
+ StrCat (" The port with name \" " , port_name, " \" and value \" " , port_value,
734
+ " \" can not be converted to " , port_model.typeName ());
735
+ throw LogicError (msg);
732
736
}
733
737
}
734
738
}
735
739
736
740
port_remap[port_name] = port_value;
737
741
}
742
+ else if (node_type == NodeType::SUBTREE && port_name == " _autoremap" )
743
+ {
744
+ do_autoremap = convertFromString<bool >(port_value);
745
+ }
738
746
else if (!IsReservedAttribute (port_name))
739
747
{
740
748
other_attributes[port_name] = port_value;
@@ -746,6 +754,7 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
746
754
config.path = prefix_path + instance_name;
747
755
config.uid = output_tree.getUID ();
748
756
config.manifest = manifest;
757
+ config.auto_remapped = do_autoremap;
749
758
750
759
if (type_ID == instance_name)
751
760
{
@@ -775,9 +784,62 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element,
775
784
// ---------------------------------------------
776
785
TreeNode::Ptr new_node;
777
786
787
+ // TODO: in order to set the config at this point, we need the subtree model, which is parsed after this function call in recursivelyCreateSubtree
778
788
if (node_type == NodeType::SUBTREE)
779
789
{
780
- config.input_ports = port_remap;
790
+ // check if this subtree has a model. If it does,
791
+ // we want to check if all the mandatory ports were remapped and
792
+ // add default ones, if necessary.
793
+ auto subtree_model_it = subtree_models.find (type_ID);
794
+ if (subtree_model_it != subtree_models.end ())
795
+ {
796
+ const PortsList& subtree_model_ports = subtree_model_it->second .ports ;
797
+ // check if:
798
+ // - remapping contains mandatory ports
799
+ // - if any of these has default value
800
+ for (const auto & [port_name, port_info] : subtree_model_ports)
801
+ {
802
+ auto it = port_remap.find (port_name);
803
+ // don't override existing remapping
804
+ if (it == port_remap.end () && !do_autoremap)
805
+ {
806
+ // remapping is not explicitly defined in the XML: use the model
807
+ if (port_info.defaultValueString ().empty ())
808
+ {
809
+ auto msg = StrCat (" In the <TreeNodesModel> the <Subtree ID=\" " , type_ID,
810
+ " \" > is defining a mandatory port called [" , port_name,
811
+ " ], but you are not remapping it" );
812
+ throw RuntimeError (msg);
813
+ }
814
+ port_remap.insert ({ port_name, port_info.defaultValueString () });
815
+ }
816
+ }
817
+ }
818
+ // populate the node config
819
+ for (const auto & [port_name, port_value] : port_remap)
820
+ {
821
+ PortDirection direction = PortDirection::INPUT;
822
+ if (subtree_model_it != subtree_models.end ())
823
+ {
824
+ const PortsList& subtree_model_ports = subtree_model_it->second .ports ;
825
+ if (const auto & it = subtree_model_ports.find (port_name);
826
+ it != subtree_model_ports.end ())
827
+ {
828
+ direction = it->second .direction ();
829
+ }
830
+ }
831
+
832
+ // Include the ports in the node config
833
+ if (direction == PortDirection::INPUT || direction == PortDirection::INOUT)
834
+ {
835
+ config.input_ports [port_name] = port_value;
836
+ }
837
+ if (direction == PortDirection::OUTPUT || direction == PortDirection::INOUT)
838
+ {
839
+ config.output_ports [port_name] = port_value;
840
+ }
841
+ }
842
+
781
843
new_node =
782
844
factory.instantiateTreeNode (instance_name, toStr (NodeType::SUBTREE), config);
783
845
auto subtree_node = dynamic_cast <SubTreeNode*>(new_node.get ());
@@ -933,7 +995,8 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID,
933
995
recursiveStep = [&](TreeNode::Ptr parent_node, Tree::Subtree::Ptr subtree,
934
996
std::string prefix, const XMLElement* element) {
935
997
// create the node
936
- auto node = createNodeFromXML (element, blackboard, parent_node, prefix, output_tree);
998
+ TreeNode::Ptr node =
999
+ createNodeFromXML (element, blackboard, parent_node, prefix, output_tree);
937
1000
subtree->nodes .push_back (node);
938
1001
939
1002
// common case: iterate through all children
@@ -947,78 +1010,31 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID,
947
1010
}
948
1011
else // special case: SubTreeNode
949
1012
{
950
- auto new_bb = Blackboard::create (blackboard);
951
1013
const std::string subtree_ID = element->Attribute (" ID" );
952
- std::unordered_map<std::string, std::string> subtree_remapping;
953
- bool do_autoremap = false ;
1014
+ TreeNode::ConstPtr const_node = node;
954
1015
955
- for (auto attr = element->FirstAttribute (); attr != nullptr ; attr = attr->Next ())
956
- {
957
- std::string attr_name = attr->Name ();
958
- std::string attr_value = attr->Value ();
959
- if (attr_value == " {=}" )
960
- {
961
- attr_value = StrCat (" {" , attr_name, " }" );
962
- }
963
-
964
- if (attr_name == " _autoremap" )
965
- {
966
- do_autoremap = convertFromString<bool >(attr_value);
967
- new_bb->enableAutoRemapping (do_autoremap);
968
- continue ;
969
- }
970
- if (!IsAllowedPortName (attr->Name ()))
971
- {
972
- continue ;
973
- }
974
- subtree_remapping.insert ({ attr_name, attr_value });
975
- }
976
- // check if this subtree has a model. If it does,
977
- // we want to check if all the mandatory ports were remapped and
978
- // add default ones, if necessary
979
- auto subtree_model_it = subtree_models.find (subtree_ID);
980
- if (subtree_model_it != subtree_models.end ())
981
- {
982
- const auto & subtree_model_ports = subtree_model_it->second .ports ;
983
- // check if:
984
- // - remapping contains mondatory ports
985
- // - if any of these has default value
986
- for (const auto & [port_name, port_info] : subtree_model_ports)
987
- {
988
- auto it = subtree_remapping.find (port_name);
989
- // don't override existing remapping
990
- if (it == subtree_remapping.end () && !do_autoremap)
991
- {
992
- // remapping is not explicitly defined in the XML: use the model
993
- if (port_info.defaultValueString ().empty ())
994
- {
995
- auto msg = StrCat (" In the <TreeNodesModel> the <Subtree ID=\" " , subtree_ID,
996
- " \" > is defining a mandatory port called [" , port_name,
997
- " ], but you are not remapping it" );
998
- throw RuntimeError (msg);
999
- }
1000
- else
1001
- {
1002
- subtree_remapping.insert ({ port_name, port_info.defaultValueString () });
1003
- }
1004
- }
1005
- }
1006
- }
1007
-
1008
- for (const auto & [attr_name, attr_value] : subtree_remapping)
1016
+ auto new_bb = Blackboard::create (blackboard);
1017
+ const bool do_autoremap = const_node->config ().auto_remapped ;
1018
+ new_bb->enableAutoRemapping (do_autoremap);
1019
+
1020
+ // Populate the subtree's blackboard with it's port values.
1021
+ PortsRemapping subtree_remapping = const_node->config ().input_ports ;
1022
+ const PortsRemapping output_ports = const_node->config ().output_ports ;
1023
+ subtree_remapping.insert (output_ports.begin (), output_ports.end ());
1024
+ for (const auto & [port_name, port_value] : subtree_remapping)
1009
1025
{
1010
- if (TreeNode::isBlackboardPointer (attr_value ))
1026
+ if (TreeNode::isBlackboardPointer (port_value ))
1011
1027
{
1012
1028
// do remapping
1013
- StringView port_name = TreeNode::stripBlackboardPointer (attr_value );
1014
- new_bb->addSubtreeRemapping (attr_name, port_name );
1029
+ StringView pointer_name = TreeNode::stripBlackboardPointer (port_value );
1030
+ new_bb->addSubtreeRemapping (port_name, pointer_name );
1015
1031
}
1016
1032
else
1017
1033
{
1018
1034
// constant string: just set that constant value into the BB
1019
1035
// IMPORTANT: this must not be autoremapped!!!
1020
1036
new_bb->enableAutoRemapping (false );
1021
- new_bb->set (attr_name , static_cast <std::string>(attr_value ));
1037
+ new_bb->set (port_name , static_cast <std::string>(port_value ));
1022
1038
new_bb->enableAutoRemapping (do_autoremap);
1023
1039
}
1024
1040
}
0 commit comments