Skip to content

Commit 2c92bf1

Browse files
committed
merging groot2 publisher
1 parent 785b8da commit 2c92bf1

File tree

7 files changed

+943
-2
lines changed

7 files changed

+943
-2
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ list(APPEND BT_SOURCE
117117
src/loggers/bt_file_logger.cpp
118118
src/loggers/bt_minitrace_logger.cpp
119119
src/loggers/bt_observer.cpp
120+
src/loggers/groot2_publisher.cpp
120121

121122
3rdparty/tinyxml2/tinyxml2.cpp
122123
3rdparty/minitrace/minitrace.cpp

examples/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ CompileExample("t08_additional_node_args")
2727
CompileExample("t09_scripting")
2828
CompileExample("t10_observer")
2929
CompileExample("t11_replace_rules")
30-
30+
CompileExample("t12_groot_howto")
3131

3232
CompileExample("ex01_wrap_legacy")
3333
CompileExample("ex02_runtime_ports")

examples/t12_groot_howto.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include "crossdoor_nodes.h"
2+
#include "behaviortree_cpp/bt_factory.h"
3+
#include "behaviortree_cpp/loggers/groot2_publisher.h"
4+
#include "behaviortree_cpp/xml_parsing.h"
5+
6+
/** We are using the same example in Tutorial 5,
7+
* But this time we also show how to connect
8+
*/
9+
10+
// clang-format off
11+
12+
static const char* xml_text = R"(
13+
<root BTCPP_format="4">
14+
15+
<BehaviorTree ID="MainTree">
16+
<Sequence>
17+
<Fallback>
18+
<Inverter>
19+
<IsDoorClosed/>
20+
</Inverter>
21+
<SubTree ID="DoorClosed"/>
22+
</Fallback>
23+
<PassThroughDoor/>
24+
</Sequence>
25+
</BehaviorTree>
26+
27+
<BehaviorTree ID="DoorClosed">
28+
<Fallback>
29+
<OpenDoor/>
30+
<RetryUntilSuccessful num_attempts="5">
31+
<PickLock/>
32+
</RetryUntilSuccessful>
33+
<SmashDoor/>
34+
</Fallback>
35+
</BehaviorTree>
36+
37+
</root>
38+
)";
39+
40+
// clang-format on
41+
42+
int main()
43+
{
44+
BT::BehaviorTreeFactory factory;
45+
46+
CrossDoor cross_door;
47+
cross_door.registerNodes(factory);
48+
49+
// Groot2 editor requires a model of your registered Nodes.
50+
// You don't need to write that by hand, if can be automatically
51+
// generated using this command and imported.
52+
53+
std::string xml_models = BT::writeTreeNodesModelXML(factory);
54+
std::cout << " ---------- XML file containing models ----------\n"
55+
<< xml_models
56+
<< "-------------------------------------------------\n";
57+
58+
factory.registerBehaviorTreeFromText(xml_text);
59+
auto tree = factory.createTree("MainTree");
60+
61+
// Connect the Groot2Publisher. This will allow Groot2 to
62+
// get the tree and poll status updates.
63+
BT::Groot2Publisher publisher(tree);
64+
65+
while(1)
66+
{
67+
std::cout << "Start" << std::endl;
68+
cross_door.reset();
69+
tree.tickWhileRunning();
70+
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
71+
}
72+
73+
return 0;
74+
}

include/behaviortree_cpp/loggers/bt_zmq_publisher.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
namespace BT
99
{
10-
class PublisherZMQ : public StatusChangeLogger
10+
class [[deprecated("Please use Groot2Publisher instead")]]
11+
PublisherZMQ : public StatusChangeLogger
1112
{
1213
static std::atomic<bool> ref_count;
1314

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <array>
5+
#include <cstring>
6+
#include <stdexcept>
7+
#include <random>
8+
#include <memory>
9+
#include <condition_variable>
10+
#include <mutex>
11+
#include "behaviortree_cpp/basic_types.h"
12+
#include "behaviortree_cpp/utils/json.hpp"
13+
14+
namespace BT::Monitor {
15+
16+
/*
17+
* All the messages exchange with the BT executor are multipart ZMQ request-replies.
18+
*
19+
* The first part of the request and the reply have fixed size and are described below.
20+
* The request and reply must have the same value of the fields:
21+
*
22+
* - request_id
23+
* - request_type
24+
* - protocol_id
25+
*/
26+
27+
enum RequestType : uint8_t
28+
{
29+
// Request the entire tree defintion as XML
30+
FULLTREE = 'T',
31+
// Request the staus of all the nodes
32+
STATUS = 'S',
33+
// retrieve the valus in a set of blackboards
34+
BLACKBOARD = 'B',
35+
36+
// Groot requests the insertion of a breakpoint
37+
BREAKPOINT_INSERT = 'I',
38+
// Groot requests to remove a breakpoint
39+
BREAKPOINT_REMOVE = 'R',
40+
// Notify Groot that we reached a breakpoint
41+
BREAKPOINT_REACHED = 'N',
42+
// Groot will unlock a breakpoint
43+
BREAKPOINT_UNLOCK = 'U',
44+
// receive the existing breakpoints in JSON format
45+
BREAKPOINTS_DUMP = 'D',
46+
47+
// Remove all breakpoints. To be done before disconnecting Groot
48+
REMOVE_ALL_BREAKPOINTS = 'A',
49+
50+
DISABLE_ALL_BREAKPOINTS = 'X',
51+
52+
UNDEFINED = 0,
53+
};
54+
55+
inline const char* ToString(const RequestType& type)
56+
{
57+
switch(type)
58+
{
59+
case RequestType::FULLTREE: return "full_tree";
60+
case RequestType::STATUS: return "status";
61+
case RequestType::BLACKBOARD: return "blackboard";
62+
63+
case RequestType::BREAKPOINT_INSERT: return "breakpoint_insert";
64+
case RequestType::BREAKPOINT_REMOVE: return "breakpoint_remove";
65+
case RequestType::BREAKPOINT_REACHED: return "breakpoint_reached";
66+
case RequestType::BREAKPOINT_UNLOCK: return "breakpoint_unlock";
67+
case RequestType::REMOVE_ALL_BREAKPOINTS: return "breakpoint_remove_all";
68+
case RequestType::BREAKPOINTS_DUMP: return "breakpoints_dump";
69+
case RequestType::DISABLE_ALL_BREAKPOINTS: return "disable_breakpoints";
70+
71+
case RequestType::UNDEFINED: return "undefined";
72+
}
73+
return "undefined";
74+
}
75+
76+
constexpr uint8_t kProtocolID = 1;
77+
using TreeUniqueUUID = std::array<char, 16>;
78+
79+
struct RequestHeader
80+
{
81+
uint32_t unique_id = 0;
82+
uint8_t protocol = kProtocolID;
83+
RequestType type = RequestType::UNDEFINED;
84+
85+
static size_t size() {
86+
return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint8_t);
87+
}
88+
89+
RequestHeader() = default;
90+
91+
RequestHeader(RequestType type): type(type)
92+
{
93+
// a random number for request_id will do
94+
static std::random_device rd;
95+
std::mt19937 mt(rd());
96+
std::uniform_int_distribution<uint32_t> dist;
97+
unique_id = dist(mt);
98+
}
99+
100+
bool operator==(const RequestHeader& other) const
101+
{
102+
return type == other.type &&
103+
protocol == other.protocol &&
104+
unique_id == other.unique_id;
105+
}
106+
bool operator!=(const RequestHeader& other) const
107+
{
108+
return !(*this == other);
109+
}
110+
};
111+
112+
struct ReplyHeader
113+
{
114+
RequestHeader request;
115+
TreeUniqueUUID tree_id;
116+
117+
static size_t size() {
118+
return RequestHeader::size() + 16;
119+
}
120+
121+
ReplyHeader() {
122+
tree_id.fill(0);
123+
}
124+
};
125+
126+
template <typename T> inline
127+
unsigned Serialize(char* buffer, unsigned offset, T value)
128+
{
129+
memcpy(buffer + offset, &value, sizeof(T));
130+
return sizeof(T);
131+
}
132+
133+
template <typename T> inline
134+
unsigned Deserialize(const char* buffer, unsigned offset, T& value)
135+
{
136+
memcpy(reinterpret_cast<char*>(&value), buffer + offset, sizeof(T));
137+
return sizeof(T);
138+
}
139+
140+
141+
inline std::string SerializeHeader(const RequestHeader& header)
142+
{
143+
std::string buffer;
144+
buffer.resize(6);
145+
unsigned offset = 0;
146+
offset += Serialize(buffer.data(), offset, header.protocol);
147+
offset += Serialize(buffer.data(), offset, uint8_t(header.type));
148+
offset += Serialize(buffer.data(), offset, header.unique_id);
149+
return buffer;
150+
}
151+
152+
inline std::string SerializeHeader(const ReplyHeader& header)
153+
{
154+
// copy the first part directly (6 bytes)
155+
std::string buffer = SerializeHeader(header.request);
156+
// add the following 16 bytes
157+
unsigned const offset = 6;
158+
buffer.resize(offset + 16);
159+
Serialize(buffer.data(), offset, header.tree_id);
160+
return buffer;
161+
}
162+
163+
inline RequestHeader DeserializeRequestHeader(const std::string& buffer)
164+
{
165+
RequestHeader header;
166+
unsigned offset = 0;
167+
offset += Deserialize(buffer.data(), offset, header.protocol);
168+
uint8_t type;
169+
offset += Deserialize(buffer.data(), offset, type);
170+
header.type = static_cast<Monitor::RequestType>(type);
171+
offset += Deserialize(buffer.data(), offset, header.unique_id);
172+
return header;
173+
}
174+
175+
176+
inline ReplyHeader DeserializeReplyHeader(const std::string& buffer)
177+
{
178+
ReplyHeader header;
179+
header.request = DeserializeRequestHeader(buffer);
180+
unsigned const offset = 6;
181+
Deserialize(buffer.data(), offset, header.tree_id);
182+
return header;
183+
}
184+
185+
struct Breakpoint
186+
{
187+
using Ptr = std::shared_ptr<Breakpoint>;
188+
189+
// used to enable/disable the breakpoint
190+
bool enabled = true;
191+
192+
uint16_t node_uid = 0;
193+
194+
// interactive breakpoints are unblucked using unlockBreakpoint()
195+
bool is_interactive = true;
196+
197+
// used by interactive breakpoints to wait for unlocking
198+
std::condition_variable wakeup;
199+
200+
std::mutex mutex;
201+
202+
// set to true to unlock an interactive breakpoint
203+
bool ready = false;
204+
205+
// once finished self-destroy
206+
bool remove_when_done = false;
207+
208+
// result to be returned
209+
NodeStatus desired_status = NodeStatus::SKIPPED;
210+
};
211+
212+
213+
void to_json(nlohmann::json& js, const Breakpoint& bp) {
214+
js = nlohmann::json {
215+
{"enabled", bp.enabled},
216+
{"uid", bp.node_uid},
217+
{"interactive", bp.is_interactive},
218+
{"once", bp.remove_when_done},
219+
{"desired_status", toStr(bp.desired_status)}
220+
};
221+
}
222+
223+
void from_json(const nlohmann::json& js, Breakpoint& bp) {
224+
js.at("enabled").get_to(bp.enabled);
225+
js.at("uid").get_to(bp.node_uid);
226+
js.at("interactive").get_to(bp.is_interactive);
227+
js.at("once").get_to(bp.remove_when_done);
228+
const std::string desired_value = js.at("desired_status").get<std::string>();
229+
bp.desired_status = convertFromString<NodeStatus>(desired_value);
230+
}
231+
232+
}

0 commit comments

Comments
 (0)