Fawkes API Fawkes Development Version
protobuf_to_bb.h
1
2/***************************************************************************
3 * Protoboard plugin template
4 * - Templates that implement translation of incoming ProtoBuf messages
5 * to BlackBoard interfaces according to the appropriate template
6 * specializations
7 *
8 * Copyright 2019 Victor Mataré
9 ****************************************************************************/
10
11/* This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * Read the full text in the LICENSE.GPL file in the doc directory.
22 */
23
24#ifndef PROTOBUF_TO_BB_H_
25#define PROTOBUF_TO_BB_H_
26
27#include "protoboard_types.h"
28
29#include <blackboard/blackboard.h>
30#include <google/protobuf/message.h>
31#include <logging/logger.h>
32
33#include <boost/bimap.hpp>
34#include <boost/core/demangle.hpp>
35#include <memory>
36
37namespace protoboard {
38
39template <class IfaceT>
40std::string iface_id_for_type();
41
42/**
43 * Must be implemented by the user.
44 * @return A map of ProtoBuf type names to their appropriate @a pb_converter instances
45 */
46pb_conversion_map make_receiving_interfaces_map();
47
48/**
49 * Default ProtoBuf to blackboard converter. This class just defines the necessary operations
50 * but does nothing in itself. Thus it can be used to silently ignore certain incoming ProtoBuf
51 * message types.
52 */
53class pb_convert : public std::enable_shared_from_this<pb_convert>
54{
55public:
56 /// Empty-init constructor
57 pb_convert();
58 /// Default copy constructor
59 pb_convert(const pb_convert &) = default;
60 /// Destructor. Does nothing since members aren't owned by this class.
61 virtual ~pb_convert();
62
63 /** Default copy assignment
64 * @return The left-hand side */
65 pb_convert &operator=(const pb_convert &) = default;
66
67 /** Deferred initialization
68 * @param blackboard A pointer to a ready-to-use blackboard
69 * @param logger A pointer to a ready-to-use logger */
70 virtual void
71 init(fawkes::BlackBoard *blackboard, fawkes::Logger *logger, const std::string & = "");
72
73#pragma GCC diagnostic push
74#pragma GCC diagnostic ignored "-Woverloaded-virtual"
75
76 /** Dereference @a msg and pass it on to handle it by reference
77 * @param msg shared_ptr to a ProtoBuf message */
78 virtual void handle(std::shared_ptr<google::protobuf::Message> msg);
79
80 /** Handle a ProtoBuf message by reference. Overridden in @ref pb_converter
81 * @param msg Reference to a generic ProtoBuf message */
82 virtual void handle(const google::protobuf::Message &msg);
83
84#pragma GCC diagnostic pop
85
86protected:
87 /// Blackboard used by the main thread
89 /// Logger from the main thread
91};
92
93/**
94 * The workhorse of the ProtoBuf to Blackboard conversion
95 * @tparam A concrete ProtoBuf message type
96 * @tparam The BlackBoard interface type that the ProtoBuf type should be mapped to
97 */
98template <class ProtoT, class IfaceT>
100{
101public:
102 /// The ProtoBuf message type that goes in
103 typedef ProtoT input_type;
104 /// The blackboard interface type that the ProtoBuf contents are written to
105 typedef IfaceT output_type;
106
107 /// Empty-init
109 : pb_convert(), interface_(nullptr), name_(boost::core::demangle(typeid(*this).name()))
110 {
111 }
112
113 /** Copying this is prohibited
114 * @param "" deleted */
116
117 /** Copying this is prohibited
118 * @param "" deleted
119 * @return deleted */
121
122 /** Move construction
123 * @param o Another pb_converter to move from */
125 : pb_convert(o),
126 interface_(std::move(o.interface_)),
127 name_(boost::core::demangle(typeid(*this).name()))
128 {
129 o.interface_ = nullptr;
130 }
131
132 /** Move assignment
133 * @param o Another pb_converter to move from
134 * @return A reference to this pb_converter */
137 {
139 this->interface_ = o.interface_;
140 o.interface_ = nullptr;
141 name_ = boost::core::demangle(typeid(*this).name());
142 return *this;
143 }
144
145 /// Close blackboard interface on destruction
147 {
148 close();
149 }
150
151 /** Deferred initialization, coincides with main thread initialization
152 * @param blackboard Initialized blackboard
153 * @param logger Logger used by the main thread
154 * @param id Blackboard interface ID to open */
155 virtual void
156 init(fawkes::BlackBoard *blackboard, fawkes::Logger *logger, const std::string &id = "") override
157 {
158 pb_convert::init(blackboard, logger);
159 std::string iface_id = iface_id_for_type<IfaceT>();
160
161 if (id.length()) {
162 if (iface_id.back() != '/')
163 iface_id += '/';
164 iface_id += id;
165 }
166
167 interface_ = blackboard_->open_for_writing<IfaceT>(iface_id.c_str());
168 logger->log_info(name(), "Initialized %s.", iface_id.c_str());
169 }
170
171 virtual void
172 handle(const google::protobuf::Message &msg) override
173 {
174 handle(dynamic_cast<const ProtoT &>(msg));
175 }
176
177 /** Handle a ProtoBuf message with known type. Just delegates to a user-definable method
178 * where the ProtoBuf message is matched up with the appropriate blackboard interface.
179 * @param msg The incoming ProtoBuf message */
180 virtual void
181 handle(const ProtoT &msg)
182 {
183 handle(msg, interface_);
184 interface_->write();
185 }
186
187 /// @return whether we have a Blackboard interface
188 virtual bool
190 {
191 return interface_;
192 }
193
194 /// Give up the current blackboard interface (closes it)
195 virtual void
197 {
198 if (is_open()) {
199 blackboard_->close(interface_);
200 interface_ = nullptr;
201 }
202 }
203
204 /// @return the current blackboard interface
205 IfaceT *
207 {
208 return interface_;
209 }
210
211 /** @return The blackboard ID suffix if this is part of a sequence. Defaults to "".
212 * Must be overriden for ProtoBuf message types that are part of a sequence and should be put
213 * in separate interfaces. */
214 static std::string
215 get_sequence_id(const ProtoT &)
216 {
217 return "";
218 }
219
220 /// @return The demangled class name for logging
221 const char *
223 {
224 return name_.c_str();
225 }
226
227protected:
228 /** Write the contents of a ProtoBuf message into the appropriate blackboard interface.
229 * Must be specialized by the user for each ProtoBuf message -> blackboard interface pair
230 * @param msg The message received
231 * @param iface The appropriate interface */
232 virtual void handle(const ProtoT &msg, IfaceT *iface);
233
234private:
235 IfaceT * interface_;
236 std::string name_;
237};
238
239/**
240 * A special handler for repeated ProtoBuf fields.
241 * @tparam ProtoT the ProtoBuf message type that contains a repeated field we want to unwrap
242 * @tparam The @a pb_converter type that should be used (repeatedly) on the repeated field
243 */
244template <class ProtoT, class OutputT>
246{
247private:
248 typedef google::protobuf::RepeatedPtrField<typename OutputT::input_type> sequence_type;
249
250public:
251 /// Default constructor
253 {
254 }
255
256 /** Handle a repeated field inside a ProtoBuf message, where the individual repeated
257 * sub-messages should be mapped to a blackboard interface each.
258 * @param msg The message containing the repeated field that should be extracted */
259 virtual void
260 handle(const google::protobuf::Message &msg) override
261 {
262 sequence_type fields = extract_sequence(dynamic_cast<const ProtoT &>(msg));
263
264 if (fields.empty())
265 return;
266
267 typename sequence_type::const_iterator field_it = fields.begin();
268
269 for (; field_it != fields.end(); ++field_it) {
270 std::string seq_id = OutputT::get_sequence_id(*field_it);
271 auto map_it = sub_converters_.find(seq_id);
272 if (map_it == sub_converters_.end()) {
273 sub_converters_.insert({seq_id, OutputT()});
274 map_it = sub_converters_.find(seq_id);
275 }
276
277 if (!map_it->second.is_open())
278 map_it->second.init(blackboard_, logger_, seq_id);
279 map_it->second.handle(*field_it);
280 }
281 }
282
283 /** Must be implemented by the user.
284 * @param msg The message containing the repeated field
285 * @return The repeated field */
286 virtual const sequence_type &extract_sequence(const ProtoT &msg);
287
288private:
289 std::unordered_map<std::string, OutputT> sub_converters_;
290};
291
292} // namespace protoboard
293
294#endif //PROTOBUF_TO_BB_H_
The BlackBoard abstract class.
Definition: blackboard.h:46
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
virtual void close(Interface *interface)=0
Close interface.
Interface for logging.
Definition: logger.h:42
virtual void log_info(const char *component, const char *format,...)
Log informational message.
Definition: multi.cpp:195
Default ProtoBuf to blackboard converter.
fawkes::Logger * logger_
Logger from the main thread.
pb_convert()
Empty-init constructor.
virtual void init(fawkes::BlackBoard *blackboard, fawkes::Logger *logger, const std::string &="")
Deferred initialization.
pb_convert(const pb_convert &)=default
Default copy constructor.
virtual void handle(std::shared_ptr< google::protobuf::Message > msg)
Dereference msg and pass it on to handle it by reference.
virtual ~pb_convert()
Destructor. Does nothing since members aren't owned by this class.
pb_convert & operator=(const pb_convert &)=default
Default copy assignment.
fawkes::BlackBoard * blackboard_
Blackboard used by the main thread.
The workhorse of the ProtoBuf to Blackboard conversion.
virtual void handle(const ProtoT &msg, IfaceT *iface)
Write the contents of a ProtoBuf message into the appropriate blackboard interface.
pb_converter(const pb_converter< ProtoT, IfaceT > &)=delete
Copying this is prohibited.
virtual void handle(const google::protobuf::Message &msg) override
Handle a ProtoBuf message by reference.
pb_converter< ProtoT, IfaceT > & operator=(pb_converter< ProtoT, IfaceT > &&o)
Move assignment.
static std::string get_sequence_id(const ProtoT &)
pb_converter< ProtoT, IfaceT > & operator=(const pb_converter< ProtoT, IfaceT > &)=delete
Copying this is prohibited.
pb_converter(pb_converter< ProtoT, IfaceT > &&o)
Move construction.
virtual void init(fawkes::BlackBoard *blackboard, fawkes::Logger *logger, const std::string &id="") override
Deferred initialization, coincides with main thread initialization.
ProtoT input_type
The ProtoBuf message type that goes in.
virtual void handle(const ProtoT &msg)
Handle a ProtoBuf message with known type.
virtual ~pb_converter()
Close blackboard interface on destruction.
virtual void close()
Give up the current blackboard interface (closes it)
IfaceT output_type
The blackboard interface type that the ProtoBuf contents are written to.
A special handler for repeated ProtoBuf fields.
virtual void handle(const google::protobuf::Message &msg) override
Handle a repeated field inside a ProtoBuf message, where the individual repeated sub-messages should ...
pb_sequence_converter()
Default constructor.
virtual const sequence_type & extract_sequence(const ProtoT &msg)
Must be implemented by the user.