BehaviorTree
Core Library to create and execute Behavior Trees
Loading...
Searching...
No Matches
groot2_protocol.h
1#pragma once
2
3#include "behaviortree_cpp/basic_types.h"
4#ifdef BTCPP_SYSTEM_JSON
5#include <nlohmann/json.hpp>
6#else
7#include "behaviortree_cpp/contrib/json.hpp"
8#endif
9
10#include <array>
11#include <condition_variable>
12#include <cstdint>
13#include <cstring>
14#include <memory>
15#include <mutex>
16#include <random>
17#include <stdexcept>
18
19namespace BT::Monitor
20{
21
22/*
23 * All the messages exchange with the BT executor are multipart ZMQ request-replies.
24 *
25 * The first part of the request and the reply have fixed size and are described below.
26 * The request and reply must have the same value of the fields:
27 *
28 * - request_id
29 * - request_type
30 * - protocol_id
31 */
32
33enum RequestType : uint8_t
34{
35 // Request the entire tree definition as XML
36 FULLTREE = 'T',
37 // Request the status of all the nodes
38 STATUS = 'S',
39 // retrieve the values in a set of blackboards
40 BLACKBOARD = 'B',
41
42 // Groot requests the insertion of a hook
43 HOOK_INSERT = 'I',
44 // Groot requests to remove a hook
45 HOOK_REMOVE = 'R',
46 // Notify Groot that we reached a breakpoint
47 BREAKPOINT_REACHED = 'N',
48 // Groot will unlock a breakpoint
49 BREAKPOINT_UNLOCK = 'U',
50 // receive the existing hooks in JSON format
51 HOOKS_DUMP = 'D',
52
53 // Remove all hooks. To be done before disconnecting Groot
54 REMOVE_ALL_HOOKS = 'A',
55
56 DISABLE_ALL_HOOKS = 'X',
57
58 // start/stop recordong
59 TOGGLE_RECORDING = 'r',
60 // get all transitions when recording
61 GET_TRANSITIONS = 't',
62
63 UNDEFINED = 0,
64};
65
66inline const char* ToString(const RequestType& type)
67{
68 switch(type)
69 {
70 case RequestType::FULLTREE:
71 return "full_tree";
72 case RequestType::STATUS:
73 return "status";
74 case RequestType::BLACKBOARD:
75 return "blackboard";
76
77 case RequestType::HOOK_INSERT:
78 return "hook_insert";
79 case RequestType::HOOK_REMOVE:
80 return "hook_remove";
81 case RequestType::BREAKPOINT_REACHED:
82 return "breakpoint_reached";
83 case RequestType::BREAKPOINT_UNLOCK:
84 return "breakpoint_unlock";
85 case RequestType::REMOVE_ALL_HOOKS:
86 return "hooks_remove_all";
87 case RequestType::HOOKS_DUMP:
88 return "hooks_dump";
89 case RequestType::DISABLE_ALL_HOOKS:
90 return "disable_hooks";
91 case RequestType::TOGGLE_RECORDING:
92 return "toggle_recording";
93 case RequestType::GET_TRANSITIONS:
94 return "get_transitions";
95
96 case RequestType::UNDEFINED:
97 return "undefined";
98 }
99 return "undefined";
100}
101
102constexpr uint8_t kProtocolID = 2;
103using TreeUniqueUUID = std::array<char, 16>;
104
105struct RequestHeader
106{
107 uint32_t unique_id = 0;
108 uint8_t protocol = kProtocolID;
109 RequestType type = RequestType::UNDEFINED;
110
111 static size_t size()
112 {
113 return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint8_t);
114 }
115
116 RequestHeader() = default;
117
118 RequestHeader(RequestType type) : type(type)
119 {
120 // a random number for request_id will do
121 static std::random_device rd;
122 std::mt19937 mt(rd());
123 std::uniform_int_distribution<uint32_t> dist;
124 unique_id = dist(mt);
125 }
126
127 bool operator==(const RequestHeader& other) const
128 {
129 return type == other.type && unique_id == other.unique_id;
130 }
131 bool operator!=(const RequestHeader& other) const
132 {
133 return !(*this == other);
134 }
135};
136
137struct ReplyHeader
138{
139 RequestHeader request;
140 TreeUniqueUUID tree_id = {};
141
142 static size_t size()
143 {
144 return RequestHeader::size() + 16;
145 }
146};
147
148template <typename T>
149inline unsigned Serialize(char* buffer, unsigned offset, T value)
150{
151 memcpy(buffer + offset, &value, sizeof(T));
152 return sizeof(T);
153}
154
155template <typename T>
156inline unsigned Deserialize(const char* buffer, unsigned offset, T& value)
157{
158 memcpy(&value, buffer + offset, sizeof(T));
159 return sizeof(T);
160}
161
162inline std::string SerializeHeader(const RequestHeader& header)
163{
164 std::string buffer;
165 buffer.resize(6);
166 unsigned offset = 0;
167 offset += Serialize(buffer.data(), offset, header.protocol);
168 offset += Serialize(buffer.data(), offset, uint8_t(header.type));
169 offset += Serialize(buffer.data(), offset, header.unique_id);
170 return buffer;
171}
172
173inline std::string SerializeHeader(const ReplyHeader& header)
174{
175 // copy the first part directly (6 bytes)
176 std::string buffer = SerializeHeader(header.request);
177 // add the following 16 bytes
178 unsigned const offset = 6;
179 buffer.resize(offset + 16);
180 Serialize(buffer.data(), offset, header.tree_id);
181 return buffer;
182}
183
184inline RequestHeader DeserializeRequestHeader(const std::string& buffer)
185{
186 RequestHeader header;
187 unsigned offset = 0;
188 offset += Deserialize(buffer.data(), offset, header.protocol);
189 uint8_t type = 0;
190 offset += Deserialize(buffer.data(), offset, type);
191 header.type = static_cast<Monitor::RequestType>(type);
192 offset += Deserialize(buffer.data(), offset, header.unique_id);
193 return header;
194}
195
196inline ReplyHeader DeserializeReplyHeader(const std::string& buffer)
197{
198 ReplyHeader header;
199 header.request = DeserializeRequestHeader(buffer);
200 unsigned const offset = 6;
201 Deserialize(buffer.data(), offset, header.tree_id);
202 return header;
203}
204
205struct Hook
206{
207 using Ptr = std::shared_ptr<Hook>;
208
209 // used to enable/disable the breakpoint
210 bool enabled = true;
211
212 enum class Position
213 {
214 PRE = 0,
215 POST = 1
216 };
217
218 Position position = Position::PRE;
219
220 uint16_t node_uid = 0;
221
222 enum class Mode
223 {
224 BREAKPOINT = 0,
225 REPLACE = 1
226 };
227
228 // interactive breakpoints are unblocked using unlockBreakpoint()
229 Mode mode = Mode::BREAKPOINT;
230
231 // used by interactive breakpoints to wait for unlocking
232 std::condition_variable wakeup;
233
234 std::mutex mutex;
235
236 // set to true to unlock an interactive breakpoint
237 bool ready = false;
238
239 // once finished self-destroy
240 bool remove_when_done = false;
241
242 // result to be returned
243 NodeStatus desired_status = NodeStatus::SKIPPED;
244};
245
246inline void to_json(nlohmann::json& js, const Hook& bp)
247{
248 js = nlohmann::json{ { "enabled", bp.enabled },
249 { "uid", bp.node_uid },
250 { "mode", int(bp.mode) },
251 { "once", bp.remove_when_done },
252 { "desired_status", toStr(bp.desired_status) },
253 { "position", int(bp.position) } };
254}
255
256inline void from_json(const nlohmann::json& js, Hook& bp)
257{
258 js.at("enabled").get_to(bp.enabled);
259 js.at("uid").get_to(bp.node_uid);
260 js.at("once").get_to(bp.remove_when_done);
261 bp.mode = static_cast<Hook::Mode>(js.at("mode").get<int>());
262 bp.position = static_cast<Hook::Position>(js.at("position").get<int>());
263
264 const std::string desired_value = js.at("desired_status").get<std::string>();
265 bp.desired_status = convertFromString<NodeStatus>(desired_value);
266}
267
268} // namespace BT::Monitor
Definition: action_node.h:24
NodeStatus
Definition: basic_types.h:34
Definition: groot2_protocol.h:206
Definition: groot2_protocol.h:138
Definition: groot2_protocol.h:106