Skip to content

Commit ca8d32a

Browse files
committed
json convertion changed and
1 parent 97a434d commit ca8d32a

19 files changed

+1776
-317
lines changed

examples/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ CompileExample("ex02_runtime_ports")
3838
CompileExample("ex04_waypoints")
3939
CompileExample("ex05_subtree_model")
4040
CompileExample("ex06_access_by_ptr")
41+
CompileExample("ex07_blackboard_backup")
4142

4243
CompileExample("t13_plugin_executor")
4344

examples/ex07_blackboard_backup.cpp

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include "behaviortree_cpp/bt_factory.h"
2+
#include "dummy_nodes.h"
3+
4+
using namespace BT;
5+
6+
// clang-format off
7+
static const char* xml_tree = R"(
8+
<root BTCPP_format="4">
9+
<BehaviorTree ID="MainTree">
10+
<Sequence>
11+
<Script code="val_A:= 'john' "/>
12+
<Script code="val_B:= 42 "/>
13+
<SaySomething message="{val_A}" />
14+
<SaySomething message="hello world" />
15+
<SubTree ID="Sub" val="{val_A}" _autoremap="true" />
16+
<SaySomething message="{reply}" />
17+
</Sequence>
18+
</BehaviorTree>
19+
<BehaviorTree ID="Sub">
20+
<Sequence>
21+
<SaySomething message="{val}" />
22+
<SaySomething message="{val_B}" />
23+
<Script code="reply:= 'done' "/>
24+
</Sequence>
25+
</BehaviorTree>
26+
</root>
27+
)";
28+
29+
// clang-format on
30+
31+
int main()
32+
{
33+
BehaviorTreeFactory factory;
34+
factory.registerNodeType<DummyNodes::SaySomething>("SaySomething");
35+
factory.registerBehaviorTreeFromText(xml_tree);
36+
37+
auto tree = factory.createTree("MainTree");
38+
39+
// We want to create a memory of the blackboard in memory.
40+
// This is conveninet when we want to reset its state to the
41+
// original one.
42+
// It is certainly more efficient than destroying and creating the tree again,
43+
// in many casess.
44+
45+
const auto backup_before_tick = BlackboardBackup(tree);
46+
tree.tickWhileRunning();
47+
48+
// Restore the original status of the blackboard
49+
BlackboardRestore(backup_before_tick, tree);
50+
tree.tickWhileRunning();
51+
52+
// Alternatively, we may want to save he values of the element in the blackboard
53+
// to file, to restore them again. We use JSON serialization for that.
54+
nlohmann::json json_after_tick = ExportTreeToJSON(tree);
55+
56+
// The JSOn object can be saved to file. See https://github.com/nlohmann/json
57+
// for details. For the time being, we just print it in the console
58+
59+
std::cout << "--- blackboard serialized as JSON: ----\n"
60+
<< json_after_tick.dump(2) << std::endl;
61+
62+
// We can restore the values of the blackboards using the JSON
63+
ImportTreeFromJSON(json_after_tick, tree);
64+
65+
return 0;
66+
}

examples/t12_groot_howto.cpp

+9-7
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,21 @@
1010
* But this time we also show how to connect
1111
*/
1212

13-
// A custom structuree that I want to visualize in Groot2
13+
// A custom struct that I want to visualize in Groot2
1414
struct Position2D
1515
{
1616
double x;
1717
double y;
1818
};
1919

20-
// Allows Position2D to be visualized in Groot2
21-
// You still need BT::RegisterJsonDefinition<Position2D>(PositionToJson)
22-
void PositionToJson(nlohmann::json& j, const Position2D& p)
20+
// This macro will generate the code that is needed to convert
21+
// the object to/from JSON.
22+
// You still need to call BT::RegisterJsonDefinition<Position2D>()
23+
// in main()
24+
BT_JSON_CONVERTER(Position2D, pos)
2325
{
24-
j["x"] = p.x;
25-
j["y"] = p.y;
26+
add_field("x", &pos.x);
27+
add_field("y", &pos.y);
2628
}
2729

2830
// Simple Action that updates an instance of Position2D in the blackboard
@@ -101,7 +103,7 @@ int main()
101103
factory.registerBehaviorTreeFromText(xml_text);
102104

103105
// Add this to allow Groot2 to visualize your custom type
104-
BT::RegisterJsonDefinition<Position2D>(PositionToJson);
106+
BT::RegisterJsonDefinition<Position2D>();
105107

106108
auto tree = factory.createTree("MainTree");
107109

examples/t12_groot_howto.cpp.orig

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#include "behaviortree_cpp/loggers/bt_file_logger_v2.h"
2+
#include "crossdoor_nodes.h"
3+
#include "behaviortree_cpp/bt_factory.h"
4+
#include "behaviortree_cpp/loggers/groot2_publisher.h"
5+
#include "behaviortree_cpp/loggers/bt_sqlite_logger.h"
6+
#include "behaviortree_cpp/xml_parsing.h"
7+
#include "behaviortree_cpp/json_export.h"
8+
9+
/** We are using the same example in Tutorial 5,
10+
* But this time we also show how to connect
11+
*/
12+
13+
<<<<<<< HEAD
14+
// A custom structuree that I want to visualize in Groot2
15+
=======
16+
// A custom struct that I want to visualize in Groot2
17+
>>>>>>> 8f94d2c (json conversions)
18+
struct Position2D
19+
{
20+
double x;
21+
double y;
22+
};
23+
24+
// This macro will generate the code that is needed to convert
25+
// the object to/from JSON.
26+
// You still need to call BT::RegisterJsonDefinition<Position2D>()
27+
// in main()
28+
BT_JSON_CONVERTER(Position2D, pos)
29+
{
30+
add_field("x", &pos.x);
31+
add_field("y", &pos.y);
32+
}
33+
34+
// Simple Action that updates an instance of Position2D in the blackboard
35+
class UpdatePosition : public BT::SyncActionNode
36+
{
37+
public:
38+
UpdatePosition(const std::string& name, const BT::NodeConfig& config)
39+
: BT::SyncActionNode(name, config)
40+
{}
41+
42+
BT::NodeStatus tick() override
43+
{
44+
_pos.x += 0.2;
45+
_pos.y += 0.1;
46+
setOutput("pos", _pos);
47+
return BT::NodeStatus::SUCCESS;
48+
}
49+
50+
static BT::PortsList providedPorts()
51+
{
52+
return { BT::OutputPort<Position2D>("pos") };
53+
}
54+
55+
private:
56+
Position2D _pos = { 0, 0 };
57+
};
58+
59+
// clang-format off
60+
61+
static const char* xml_text = R"(
62+
<root BTCPP_format="4">
63+
64+
<BehaviorTree ID="MainTree">
65+
<Sequence>
66+
<Script code="door_open:=false" />
67+
<UpdatePosition pos="{pos_2D}" />
68+
<Fallback>
69+
<Inverter>
70+
<IsDoorClosed/>
71+
</Inverter>
72+
<SubTree ID="DoorClosed" _autoremap="true" door_open="{door_open}"/>
73+
</Fallback>
74+
<PassThroughDoor/>
75+
</Sequence>
76+
</BehaviorTree>
77+
78+
<BehaviorTree ID="DoorClosed">
79+
<Fallback name="tryOpen" _onSuccess="door_open:=true">
80+
<OpenDoor/>
81+
<RetryUntilSuccessful num_attempts="5">
82+
<PickLock/>
83+
</RetryUntilSuccessful>
84+
<SmashDoor/>
85+
</Fallback>
86+
</BehaviorTree>
87+
88+
</root>
89+
)";
90+
91+
// clang-format on
92+
93+
int main()
94+
{
95+
BT::BehaviorTreeFactory factory;
96+
97+
// Nodes registration, as usual
98+
CrossDoor cross_door;
99+
cross_door.registerNodes(factory);
100+
factory.registerNodeType<UpdatePosition>("UpdatePosition");
101+
102+
// Groot2 editor requires a model of your registered Nodes.
103+
// You don't need to write that by hand, it can be automatically
104+
// generated using the following command.
105+
std::string xml_models = BT::writeTreeNodesModelXML(factory);
106+
107+
factory.registerBehaviorTreeFromText(xml_text);
108+
109+
// Add this to allow Groot2 to visualize your custom type
110+
BT::RegisterJsonDefinition<Position2D>();
111+
112+
auto tree = factory.createTree("MainTree");
113+
114+
std::cout << "----------- XML file ----------\n"
115+
<< BT::WriteTreeToXML(tree, false, false)
116+
<< "--------------------------------\n";
117+
118+
// Connect the Groot2Publisher. This will allow Groot2 to
119+
// get the tree and poll status updates.
120+
const unsigned port = 1667;
121+
BT::Groot2Publisher publisher(tree, port);
122+
123+
// Add two more loggers, to save the transitions into a file.
124+
// Both formats are compatible with Groot2
125+
126+
// Lightweight serialization
127+
BT::FileLogger2 logger2(tree, "t12_logger2.btlog");
128+
// SQLite logger can save multiple sessions into the same database
129+
bool append_to_database = true;
130+
BT::SqliteLogger sqlite_logger(tree, "t12_sqlitelog.db3", append_to_database);
131+
132+
while(1)
133+
{
134+
std::cout << "Start" << std::endl;
135+
cross_door.reset();
136+
tree.tickWhileRunning();
137+
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
138+
}
139+
140+
return 0;
141+
}

include/behaviortree_cpp/basic_types.h

+65-21
Original file line numberDiff line numberDiff line change
@@ -58,24 +58,74 @@ enum class PortDirection
5858

5959
using StringView = std::string_view;
6060

61+
bool StartWith(StringView str, StringView prefix);
62+
6163
// vector of key/value pairs
6264
using KeyValueVector = std::vector<std::pair<std::string, std::string>>;
6365

66+
/** Usage: given a function/method like this:
67+
*
68+
* Expected<double> getAnswer();
69+
*
70+
* User code can check result and error message like this:
71+
*
72+
* auto res = getAnswer();
73+
* if( res )
74+
* {
75+
* std::cout << "answer was: " << res.value() << std::endl;
76+
* }
77+
* else{
78+
* std::cerr << "failed to get the answer: " << res.error() << std::endl;
79+
* }
80+
*
81+
* */
82+
template <typename T>
83+
using Expected = nonstd::expected<T, std::string>;
84+
6485
struct AnyTypeAllowed
6586
{
6687
};
6788

89+
/**
90+
* @brief convertFromJSON will parse a json string and use JsonExporter
91+
* to convert its content to a given type. It will work only if
92+
* the type was previously registered. May throw if it fails.
93+
*
94+
* @param json_text a valid JSON string
95+
* @param type you must specify the typeid()
96+
* @return the object, wrapped in Any.
97+
*/
98+
[[nodiscard]] Any convertFromJSON(StringView json_text, std::type_index type);
99+
100+
/// Same as the non template version, but with automatic casting
101+
template <typename T>
102+
[[nodiscard]] inline T convertFromJSON(StringView str)
103+
{
104+
return convertFromJSON(str, typeid(T)).cast<T>();
105+
}
106+
68107
/**
69108
* convertFromString is used to convert a string into a custom type.
70109
*
71110
* This function is invoked under the hood by TreeNode::getInput(), but only when the
72111
* input port contains a string.
73112
*
74-
* If you have a custom type, you need to implement the corresponding template specialization.
113+
* If you have a custom type, you need to implement the corresponding
114+
* template specialization.
115+
*
116+
* If the string starts with the prefix "json:", it will
117+
* fall back to convertFromJSON()
75118
*/
76119
template <typename T>
77-
[[nodiscard]] inline T convertFromString(StringView /*str*/)
120+
[[nodiscard]] inline T convertFromString(StringView str)
78121
{
122+
// if string starts with "json:{", try to parse it as json
123+
if(StartWith(str, "json:"))
124+
{
125+
str.remove_prefix(5);
126+
return convertFromJSON<T>(str);
127+
}
128+
79129
auto type_name = BT::demangle(typeid(T));
80130

81131
std::cerr << "You (maybe indirectly) called BT::convertFromString() for type ["
@@ -171,6 +221,14 @@ constexpr bool IsConvertibleToString()
171221
std::is_convertible_v<T, std::string_view>;
172222
}
173223

224+
Expected<std::string> toJsonString(const Any& value);
225+
226+
/**
227+
* @brief toStr is the reverse operation of convertFromString.
228+
*
229+
* If T is a custom type and there is no template specialization,
230+
* it will try to fall back to toJsonString()
231+
*/
174232
template <typename T>
175233
[[nodiscard]] std::string toStr(const T& value)
176234
{
@@ -180,6 +238,11 @@ template <typename T>
180238
}
181239
else if constexpr(!std::is_arithmetic_v<T>)
182240
{
241+
if(auto str = toJsonString(Any(value)))
242+
{
243+
return *str;
244+
}
245+
183246
throw LogicError(StrCat("Function BT::toStr<T>() not specialized for type [",
184247
BT::demangle(typeid(T)), "]"));
185248
}
@@ -225,25 +288,6 @@ using enable_if = typename std::enable_if<Predicate::value>::type*;
225288
template <typename Predicate>
226289
using enable_if_not = typename std::enable_if<!Predicate::value>::type*;
227290

228-
/** Usage: given a function/method like this:
229-
*
230-
* Expected<double> getAnswer();
231-
*
232-
* User code can check result and error message like this:
233-
*
234-
* auto res = getAnswer();
235-
* if( res )
236-
* {
237-
* std::cout << "answer was: " << res.value() << std::endl;
238-
* }
239-
* else{
240-
* std::cerr << "failed to get the answer: " << res.error() << std::endl;
241-
* }
242-
*
243-
* */
244-
template <typename T>
245-
using Expected = nonstd::expected<T, std::string>;
246-
247291
#ifdef USE_BTCPP3_OLD_NAMES
248292
// note: we also use the name Optional instead of expected because it is more intuitive
249293
// for users that are not up to date with "modern" C++

0 commit comments

Comments
 (0)