Skip to content

Commit b89ffff

Browse files
committed
Simplify the visualization of custom type in Groot2 and improved tutorial
1 parent eaa76be commit b89ffff

File tree

2 files changed

+95
-5
lines changed

2 files changed

+95
-5
lines changed

examples/t12_groot_howto.cpp

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,48 @@
44
#include "behaviortree_cpp/loggers/groot2_publisher.h"
55
#include "behaviortree_cpp/loggers/bt_sqlite_logger.h"
66
#include "behaviortree_cpp/xml_parsing.h"
7+
#include "behaviortree_cpp/json_export.h"
78

89
/** We are using the same example in Tutorial 5,
910
* But this time we also show how to connect
1011
*/
1112

13+
// A custom structuree that I want to visualize in Groot2
14+
struct Position2D {
15+
double x;
16+
double y;
17+
};
18+
19+
// Allows Position2D to be visualized in Groot2
20+
// You still need BT::RegisterJsonDefinition<Position2D>(PositionToJson)
21+
void PositionToJson(nlohmann::json& j, const Position2D& p)
22+
{
23+
j["x"] = p.x;
24+
j["y"] = p.y;
25+
}
26+
27+
// Simple Action that updates an instance of Position2D in the blackboard
28+
class UpdatePosition: public BT::SyncActionNode
29+
{
30+
public:
31+
UpdatePosition(const std::string& name, const BT::NodeConfig& config):
32+
BT::SyncActionNode(name, config) {}
33+
34+
BT::NodeStatus tick() override {
35+
_pos.x += 0.2;
36+
_pos.y += 0.1;
37+
setOutput("pos", _pos);
38+
return BT::NodeStatus::SUCCESS;
39+
}
40+
41+
static BT::PortsList providedPorts()
42+
{
43+
return {BT::OutputPort<Position2D>("pos")};
44+
}
45+
private:
46+
Position2D _pos = {0, 0};
47+
};
48+
1249
// clang-format off
1350

1451
static const char* xml_text = R"(
@@ -17,6 +54,7 @@ static const char* xml_text = R"(
1754
<BehaviorTree ID="MainTree">
1855
<Sequence>
1956
<Script code="door_open:=false" />
57+
<UpdatePosition pos="{pos_2D}" />
2058
<Fallback>
2159
<Inverter>
2260
<IsDoorClosed/>
@@ -46,27 +84,33 @@ int main()
4684
{
4785
BT::BehaviorTreeFactory factory;
4886

87+
// Nodes registration, as usual
4988
CrossDoor cross_door;
5089
cross_door.registerNodes(factory);
90+
factory.registerNodeType<UpdatePosition>("UpdatePosition");
5191

5292
// Groot2 editor requires a model of your registered Nodes.
5393
// You don't need to write that by hand, it can be automatically
5494
// generated using the following command.
5595
std::string xml_models = BT::writeTreeNodesModelXML(factory);
5696

5797
factory.registerBehaviorTreeFromText(xml_text);
58-
auto tree = factory.createTree("MainTree");
5998

99+
// Add this to allow Groot2 to visualize your custom type
100+
BT::RegisterJsonDefinition<Position2D>(PositionToJson);
101+
102+
auto tree = factory.createTree("MainTree");
60103

61104
std::cout << "----------- XML file ----------\n"
62105
<< BT::WriteTreeToXML(tree, false, false)
63106
<< "--------------------------------\n";
64107

65108
// Connect the Groot2Publisher. This will allow Groot2 to
66109
// get the tree and poll status updates.
67-
BT::Groot2Publisher publisher(tree);
110+
const unsigned port = 1667;
111+
BT::Groot2Publisher publisher(tree, port);
68112

69-
// Add also two logger which save the transitions into a file.
113+
// Add two more loggers, to save the transitions into a file.
70114
// Both formats are compatible with Groot2
71115

72116
// Lightweight serialization
@@ -83,5 +127,7 @@ int main()
83127
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
84128
}
85129

130+
BT::JsonExporter::get().addConverter<Position2D>();
131+
86132
return 0;
87133
}

include/behaviortree_cpp/json_export.h

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace BT
1919
*
2020
* Later, you MUST register this calling:
2121
*
22-
* JsonExporter::get().addConverter<Foo>();
22+
* RegisterJsonDefinition<Foo>();
2323
*/
2424

2525
class JsonExporter{
@@ -55,6 +55,14 @@ class JsonExporter{
5555
type_converters_.insert( {typeid(T), std::move(converter)} );
5656
}
5757

58+
template <typename T> void addConverter(std::function<void(nlohmann::json&, const T&)> func)
59+
{
60+
auto converter = [func](const BT::Any& entry, nlohmann::json& dst) {
61+
func(dst, entry.cast<T>());
62+
};
63+
type_converters_.insert( {typeid(T), std::move(converter)} );
64+
}
65+
5866
/// Register directly your own converter.
5967
template <typename T>
6068
void addConverter(std::function<void(const T&, nlohmann::json&)> to_json)
@@ -69,9 +77,45 @@ class JsonExporter{
6977

7078
using ToJonConverter = std::function<void(const BT::Any&, nlohmann::json&)>;
7179
std::unordered_map<std::type_index, ToJonConverter> type_converters_;
72-
7380
};
7481

82+
/* Function to use to register a specific implementation of nlohmann::to_json
83+
84+
Example:
85+
86+
namespace nlohmann {
87+
void to_json(nlohmann::json& j, const Position2D& p)
88+
{
89+
j["x"] = p.x;
90+
j["y"] = p.y;
91+
}
92+
} // namespace nlohmann
93+
94+
// In you main function
95+
RegisterJsonDefinition<Position2D>()
96+
*/
97+
template <typename T> inline void RegisterJsonDefinition()
98+
{
99+
JsonExporter::get().addConverter<T>();
100+
}
101+
102+
/* Function to use to register a specific implementation of "to_json"
103+
104+
Example:
105+
106+
RegisterJsonDefinition([](nlohmann::json& j, const Position2D& p)
107+
{
108+
j["x"] = p.x;
109+
j["y"] = p.y;
110+
} );
111+
*/
112+
113+
template <typename T> inline
114+
void RegisterJsonDefinition(std::function<void(nlohmann::json&, const T&)> func)
115+
{
116+
JsonExporter::get().addConverter<T>(func);
117+
}
118+
75119
nlohmann::json ExportBlackboardToJSON(BT::Blackboard& blackboard);
76120

77121
} // namespace BT

0 commit comments

Comments
 (0)