diff --git a/include/behaviortree_cpp/basic_types.h b/include/behaviortree_cpp/basic_types.h index a86e31504..4e01392db 100644 --- a/include/behaviortree_cpp/basic_types.h +++ b/include/behaviortree_cpp/basic_types.h @@ -10,7 +10,9 @@ #include #include #include +#include #include +#include #include #include "behaviortree_cpp/utils/safe_any.hpp" @@ -438,14 +440,14 @@ struct has_static_method_providedPorts< }; template -struct has_static_method_description : std::false_type +struct has_static_method_metadata : std::false_type { }; template -struct has_static_method_description< +struct has_static_method_metadata< T, typename std::enable_if< - std::is_same::value>::type> + std::is_same>>::value>::type> : std::true_type { }; @@ -467,4 +469,3 @@ using TimePoint = std::chrono::high_resolution_clock::time_point; using Duration = std::chrono::high_resolution_clock::duration; } // namespace BT - diff --git a/include/behaviortree_cpp/bt_factory.h b/include/behaviortree_cpp/bt_factory.h index 4c126b7e3..cbbecea00 100644 --- a/include/behaviortree_cpp/bt_factory.h +++ b/include/behaviortree_cpp/bt_factory.h @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "behaviortree_cpp/contrib/magic_enum.hpp" #include "behaviortree_cpp/behavior_tree.h" @@ -44,13 +46,11 @@ template inline TreeNodeManifest CreateManifest(const std::string& ID, PortsList portlist = getProvidedPorts()) { - if constexpr( has_static_method_description::value) + if constexpr( has_static_method_metadata::value ) { - return {getType(), ID, portlist, T::description()}; - } - else { - return {getType(), ID, portlist, {}}; + return {getType(), ID, portlist, T::metadata()}; } + return {getType(), ID, portlist, {}}; } #ifdef BT_PLUGIN_EXPORT @@ -425,10 +425,10 @@ class BehaviorTreeFactory Tree createTree(const std::string& tree_name, Blackboard::Ptr blackboard = Blackboard::create()); - /// Add a description to a specific manifest. This description will be added + /// Add metadata to a specific manifest. This metadata will be added /// to with the function writeTreeNodesModelXML() - void addDescriptionToManifest(const std::string& node_id, - const std::string& description); + void addMetadataToManifest(const std::string& node_id, + const std::vector>& metadata); /** * @brief Add an Enum to the scripting language. diff --git a/include/behaviortree_cpp/tree_node.h b/include/behaviortree_cpp/tree_node.h index 20246dbc4..788addd38 100644 --- a/include/behaviortree_cpp/tree_node.h +++ b/include/behaviortree_cpp/tree_node.h @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include "behaviortree_cpp/utils/signal.h" #include "behaviortree_cpp/basic_types.h" @@ -38,7 +40,7 @@ struct TreeNodeManifest NodeType type; std::string registration_ID; PortsList ports; - std::string description; + std::vector> metadata; }; using PortsRemapping = std::unordered_map; diff --git a/src/bt_factory.cpp b/src/bt_factory.cpp index e14ca8162..ec7a0b777 100644 --- a/src/bt_factory.cpp +++ b/src/bt_factory.cpp @@ -91,7 +91,7 @@ BehaviorTreeFactory::BehaviorTreeFactory(): registerNodeType>("Switch4"); registerNodeType>("Switch5"); registerNodeType>("Switch6"); - + registerNodeType>("LoopInt"); registerNodeType>("LoopBool"); registerNodeType>("LoopDouble"); @@ -435,15 +435,15 @@ Tree BehaviorTreeFactory::createTree(const std::string& tree_name, return tree; } -void BehaviorTreeFactory::addDescriptionToManifest(const std::string& node_id, - const std::string& description) +void BehaviorTreeFactory::addMetadataToManifest(const std::string& node_id, + const std::vector>& metadata) { auto it = _p->manifests.find(node_id); if (it == _p->manifests.end()) { - throw std::runtime_error("addDescriptionToManifest: wrong ID"); + throw std::runtime_error("addMetadataToManifest: wrong ID"); } - it->second.description = description; + it->second.metadata = metadata; } void BehaviorTreeFactory::registerScriptingEnum(StringView name, int value) diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index 6fc77d9aa..87435a4c0 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -83,7 +83,7 @@ struct XMLParser::PImpl std::map subtree_models; int suffix_count; - + explicit PImpl(const BehaviorTreeFactory& fact) : factory(fact), current_path(std::filesystem::current_path()), suffix_count(0) {} @@ -982,11 +982,18 @@ void addNodeModelToXML(const TreeNodeManifest& model, element->InsertEndChild(port_element); } - if (!model.description.empty()) + if (!model.metadata.empty()) { - auto description_element = doc.NewElement("description"); - description_element->SetText(model.description.c_str()); - element->InsertEndChild(description_element); + auto metadata_root = doc.NewElement("MetadataFields"); + + for (const auto& [name, value] : model.metadata) + { + auto metadata_element = doc.NewElement("Metadata"); + metadata_element->SetAttribute(name.c_str(), value.c_str()); + metadata_root->InsertEndChild(metadata_element); + } + + element->InsertEndChild(metadata_root); } model_root->InsertEndChild(element); @@ -997,7 +1004,7 @@ void addTreeToXML(const Tree& tree, XMLElement* rootXML, bool add_metadata, bool add_builtin_models) -{ +{ std::function addNode; addNode = [&](const TreeNode& node, XMLElement* parent_elem) diff --git a/tests/gtest_factory.cpp b/tests/gtest_factory.cpp index 3cfe3831f..5dc7b9e9c 100644 --- a/tests/gtest_factory.cpp +++ b/tests/gtest_factory.cpp @@ -1,5 +1,8 @@ #include #include +#include +#include +#include #include "behaviortree_cpp/xml_parsing.h" #include "../sample_nodes/crossdoor_nodes.h" #include "../sample_nodes/dummy_nodes.h" @@ -388,10 +391,18 @@ TEST(BehaviorTreeReload, ReloadSameTree) } } -class DescriptiveAction : public SyncActionNode +std::vector> makeTestMetadata() +{ + return { + std::make_pair("foo", "hello"), + std::make_pair("bar", "42"), + }; +} + +class ActionWithMetadata : public SyncActionNode { public: - DescriptiveAction(const std::string& name, const NodeConfig& config): + ActionWithMetadata(const std::string& name, const NodeConfig& config): SyncActionNode(name, config) {} BT::NodeStatus tick() override { @@ -402,21 +413,39 @@ class DescriptiveAction : public SyncActionNode return {}; } - static std::string description() { - return "THE DESCRIPTION"; + static std::vector> metadata() { + return makeTestMetadata(); } }; -TEST(BehaviorTreeFactory, DescriptionMethod) +TEST(BehaviorTreeFactory, ManifestMethod) { + const char* expectedXML = R"( + + + + + + )"; BehaviorTreeFactory factory; - factory.registerNodeType("DescriptiveAction"); - const auto& manifest = factory.manifests().at("DescriptiveAction"); - ASSERT_EQ(manifest.description, "THE DESCRIPTION"); + factory.registerNodeType("ActionWithMetadata"); + const auto& manifest = factory.manifests().at("ActionWithMetadata"); + EXPECT_EQ(manifest.metadata, makeTestMetadata()); auto xml = writeTreeNodesModelXML(factory, false); std::cout << xml << std::endl; - ASSERT_NE(xml.find( "THE DESCRIPTION"), std::string::npos); + EXPECT_NE(xml.find(expectedXML), std::string::npos); +} + +TEST(BehaviorTreeFactory, addMetadataToManifest) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("SaySomething"); + const auto& initial_manifest = factory.manifests().at("SaySomething"); + EXPECT_TRUE(initial_manifest.metadata.empty()); + factory.addMetadataToManifest("SaySomething", makeTestMetadata()); + const auto& modified_manifest = factory.manifests().at("SaySomething"); + EXPECT_EQ(modified_manifest.metadata, makeTestMetadata()); }