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 
37 namespace protoboard {
38 
39 template <class IfaceT>
40 std::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  */
46 pb_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  */
53 class pb_convert : public std::enable_shared_from_this<pb_convert>
54 {
55 public:
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 
86 protected:
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  */
98 template <class ProtoT, class IfaceT>
99 class pb_converter : public pb_convert
100 {
101 public:
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
146  virtual ~pb_converter()
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 
227 protected:
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 
234 private:
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  */
244 template <class ProtoT, class OutputT>
246 {
247 private:
248  typedef google::protobuf::RepeatedPtrField<typename OutputT::input_type> sequence_type;
249 
250 public:
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 
288 private:
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 & operator=(const pb_convert &)=default
Default copy assignment.
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.
fawkes::BlackBoard * blackboard_
Blackboard used by the main thread.
The workhorse of the ProtoBuf to Blackboard conversion.
pb_converter< ProtoT, IfaceT > & operator=(const pb_converter< ProtoT, IfaceT > &)=delete
Copying this is prohibited.
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.
static std::string get_sequence_id(const ProtoT &)
pb_converter(pb_converter< ProtoT, IfaceT > &&o)
Move construction.
pb_converter< ProtoT, IfaceT > & operator=(pb_converter< ProtoT, IfaceT > &&o)
Move assignment.
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 ...
virtual const sequence_type & extract_sequence(const ProtoT &msg)
Must be implemented by the user.
pb_sequence_converter()
Default constructor.