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