Skip to content

Commit

Permalink
json convertion changed and
Browse files Browse the repository at this point in the history
  • Loading branch information
facontidavide committed Mar 18, 2024
1 parent 97a434d commit ca8d32a
Show file tree
Hide file tree
Showing 19 changed files with 1,776 additions and 317 deletions.
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ CompileExample("ex02_runtime_ports")
CompileExample("ex04_waypoints")
CompileExample("ex05_subtree_model")
CompileExample("ex06_access_by_ptr")
CompileExample("ex07_blackboard_backup")

CompileExample("t13_plugin_executor")

Expand Down
66 changes: 66 additions & 0 deletions examples/ex07_blackboard_backup.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include "behaviortree_cpp/bt_factory.h"
#include "dummy_nodes.h"

using namespace BT;

// clang-format off
static const char* xml_tree = R"(
<root BTCPP_format="4">
<BehaviorTree ID="MainTree">
<Sequence>
<Script code="val_A:= 'john' "/>
<Script code="val_B:= 42 "/>
<SaySomething message="{val_A}" />
<SaySomething message="hello world" />
<SubTree ID="Sub" val="{val_A}" _autoremap="true" />
<SaySomething message="{reply}" />
</Sequence>
</BehaviorTree>
<BehaviorTree ID="Sub">
<Sequence>
<SaySomething message="{val}" />
<SaySomething message="{val_B}" />
<Script code="reply:= 'done' "/>
</Sequence>
</BehaviorTree>
</root>
)";

// clang-format on

int main()
{
BehaviorTreeFactory factory;
factory.registerNodeType<DummyNodes::SaySomething>("SaySomething");
factory.registerBehaviorTreeFromText(xml_tree);

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

// We want to create a memory of the blackboard in memory.
// This is conveninet when we want to reset its state to the
// original one.
// It is certainly more efficient than destroying and creating the tree again,
// in many casess.

const auto backup_before_tick = BlackboardBackup(tree);
tree.tickWhileRunning();

// Restore the original status of the blackboard
BlackboardRestore(backup_before_tick, tree);
tree.tickWhileRunning();

// Alternatively, we may want to save he values of the element in the blackboard
// to file, to restore them again. We use JSON serialization for that.
nlohmann::json json_after_tick = ExportTreeToJSON(tree);

// The JSOn object can be saved to file. See https://github.com/nlohmann/json
// for details. For the time being, we just print it in the console

std::cout << "--- blackboard serialized as JSON: ----\n"
<< json_after_tick.dump(2) << std::endl;

// We can restore the values of the blackboards using the JSON
ImportTreeFromJSON(json_after_tick, tree);

return 0;
}
16 changes: 9 additions & 7 deletions examples/t12_groot_howto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@
* But this time we also show how to connect
*/

// A custom structuree that I want to visualize in Groot2
// A custom struct that I want to visualize in Groot2
struct Position2D
{
double x;
double y;
};

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

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

// Add this to allow Groot2 to visualize your custom type
BT::RegisterJsonDefinition<Position2D>(PositionToJson);
BT::RegisterJsonDefinition<Position2D>();

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

Expand Down
141 changes: 141 additions & 0 deletions examples/t12_groot_howto.cpp.orig
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#include "behaviortree_cpp/loggers/bt_file_logger_v2.h"
#include "crossdoor_nodes.h"
#include "behaviortree_cpp/bt_factory.h"
#include "behaviortree_cpp/loggers/groot2_publisher.h"
#include "behaviortree_cpp/loggers/bt_sqlite_logger.h"
#include "behaviortree_cpp/xml_parsing.h"
#include "behaviortree_cpp/json_export.h"

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

<<<<<<< HEAD
// A custom structuree that I want to visualize in Groot2
=======
// A custom struct that I want to visualize in Groot2
>>>>>>> 8f94d2c (json conversions)
struct Position2D
{
double x;
double y;
};

// This macro will generate the code that is needed to convert
// the object to/from JSON.
// You still need to call BT::RegisterJsonDefinition<Position2D>()
// in main()
BT_JSON_CONVERTER(Position2D, pos)
{
add_field("x", &pos.x);
add_field("y", &pos.y);
}

// Simple Action that updates an instance of Position2D in the blackboard
class UpdatePosition : public BT::SyncActionNode
{
public:
UpdatePosition(const std::string& name, const BT::NodeConfig& config)
: BT::SyncActionNode(name, config)
{}

BT::NodeStatus tick() override
{
_pos.x += 0.2;
_pos.y += 0.1;
setOutput("pos", _pos);
return BT::NodeStatus::SUCCESS;
}

static BT::PortsList providedPorts()
{
return { BT::OutputPort<Position2D>("pos") };
}

private:
Position2D _pos = { 0, 0 };
};

// clang-format off

static const char* xml_text = R"(
<root BTCPP_format="4">

<BehaviorTree ID="MainTree">
<Sequence>
<Script code="door_open:=false" />
<UpdatePosition pos="{pos_2D}" />
<Fallback>
<Inverter>
<IsDoorClosed/>
</Inverter>
<SubTree ID="DoorClosed" _autoremap="true" door_open="{door_open}"/>
</Fallback>
<PassThroughDoor/>
</Sequence>
</BehaviorTree>

<BehaviorTree ID="DoorClosed">
<Fallback name="tryOpen" _onSuccess="door_open:=true">
<OpenDoor/>
<RetryUntilSuccessful num_attempts="5">
<PickLock/>
</RetryUntilSuccessful>
<SmashDoor/>
</Fallback>
</BehaviorTree>

</root>
)";

// clang-format on

int main()
{
BT::BehaviorTreeFactory factory;

// Nodes registration, as usual
CrossDoor cross_door;
cross_door.registerNodes(factory);
factory.registerNodeType<UpdatePosition>("UpdatePosition");

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

factory.registerBehaviorTreeFromText(xml_text);

// Add this to allow Groot2 to visualize your custom type
BT::RegisterJsonDefinition<Position2D>();

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

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

// Connect the Groot2Publisher. This will allow Groot2 to
// get the tree and poll status updates.
const unsigned port = 1667;
BT::Groot2Publisher publisher(tree, port);

// Add two more loggers, to save the transitions into a file.
// Both formats are compatible with Groot2

// Lightweight serialization
BT::FileLogger2 logger2(tree, "t12_logger2.btlog");
// SQLite logger can save multiple sessions into the same database
bool append_to_database = true;
BT::SqliteLogger sqlite_logger(tree, "t12_sqlitelog.db3", append_to_database);

while(1)
{
std::cout << "Start" << std::endl;
cross_door.reset();
tree.tickWhileRunning();
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
}

return 0;
}
86 changes: 65 additions & 21 deletions include/behaviortree_cpp/basic_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,74 @@ enum class PortDirection

using StringView = std::string_view;

bool StartWith(StringView str, StringView prefix);

// vector of key/value pairs
using KeyValueVector = std::vector<std::pair<std::string, std::string>>;

/** Usage: given a function/method like this:
*
* Expected<double> getAnswer();
*
* User code can check result and error message like this:
*
* auto res = getAnswer();
* if( res )
* {
* std::cout << "answer was: " << res.value() << std::endl;
* }
* else{
* std::cerr << "failed to get the answer: " << res.error() << std::endl;
* }
*
* */
template <typename T>
using Expected = nonstd::expected<T, std::string>;

struct AnyTypeAllowed
{
};

/**
* @brief convertFromJSON will parse a json string and use JsonExporter
* to convert its content to a given type. It will work only if
* the type was previously registered. May throw if it fails.
*
* @param json_text a valid JSON string
* @param type you must specify the typeid()
* @return the object, wrapped in Any.
*/
[[nodiscard]] Any convertFromJSON(StringView json_text, std::type_index type);

/// Same as the non template version, but with automatic casting
template <typename T>
[[nodiscard]] inline T convertFromJSON(StringView str)
{
return convertFromJSON(str, typeid(T)).cast<T>();
}

/**
* convertFromString is used to convert a string into a custom type.
*
* This function is invoked under the hood by TreeNode::getInput(), but only when the
* input port contains a string.
*
* If you have a custom type, you need to implement the corresponding template specialization.
* If you have a custom type, you need to implement the corresponding
* template specialization.
*
* If the string starts with the prefix "json:", it will
* fall back to convertFromJSON()
*/
template <typename T>
[[nodiscard]] inline T convertFromString(StringView /*str*/)
[[nodiscard]] inline T convertFromString(StringView str)
{
// if string starts with "json:{", try to parse it as json
if(StartWith(str, "json:"))
{
str.remove_prefix(5);
return convertFromJSON<T>(str);
}

auto type_name = BT::demangle(typeid(T));

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

Expected<std::string> toJsonString(const Any& value);

/**
* @brief toStr is the reverse operation of convertFromString.
*
* If T is a custom type and there is no template specialization,
* it will try to fall back to toJsonString()
*/
template <typename T>
[[nodiscard]] std::string toStr(const T& value)
{
Expand All @@ -180,6 +238,11 @@ template <typename T>
}
else if constexpr(!std::is_arithmetic_v<T>)
{
if(auto str = toJsonString(Any(value)))
{
return *str;
}

throw LogicError(StrCat("Function BT::toStr<T>() not specialized for type [",
BT::demangle(typeid(T)), "]"));
}
Expand Down Expand Up @@ -225,25 +288,6 @@ using enable_if = typename std::enable_if<Predicate::value>::type*;
template <typename Predicate>
using enable_if_not = typename std::enable_if<!Predicate::value>::type*;

/** Usage: given a function/method like this:
*
* Expected<double> getAnswer();
*
* User code can check result and error message like this:
*
* auto res = getAnswer();
* if( res )
* {
* std::cout << "answer was: " << res.value() << std::endl;
* }
* else{
* std::cerr << "failed to get the answer: " << res.error() << std::endl;
* }
*
* */
template <typename T>
using Expected = nonstd::expected<T, std::string>;

#ifdef USE_BTCPP3_OLD_NAMES
// note: we also use the name Optional instead of expected because it is more intuitive
// for users that are not up to date with "modern" C++
Expand Down
Loading

0 comments on commit ca8d32a

Please sign in to comment.