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  /** Dereference @a msg and pass it on to handle it by reference
74  * @param msg shared_ptr to a ProtoBuf message */
75  virtual void handle(std::shared_ptr<google::protobuf::Message> msg);
76 
77  /** Handle a ProtoBuf message by reference. Overridden in @ref pb_converter
78  * @param msg Reference to a generic ProtoBuf message */
79  virtual void handle(const google::protobuf::Message &msg);
80 
81 protected:
82  /// Blackboard used by the main thread
84  /// Logger from the main thread
86 };
87 
88 /**
89  * The workhorse of the ProtoBuf to Blackboard conversion
90  * @tparam A concrete ProtoBuf message type
91  * @tparam The BlackBoard interface type that the ProtoBuf type should be mapped to
92  */
93 template <class ProtoT, class IfaceT>
94 class pb_converter : public pb_convert
95 {
96 public:
97  /// The ProtoBuf message type that goes in
98  typedef ProtoT input_type;
99  /// The blackboard interface type that the ProtoBuf contents are written to
100  typedef IfaceT output_type;
101 
102  /// Empty-init
104  : pb_convert(), interface_(nullptr), name_(boost::core::demangle(typeid(*this).name()))
105  {
106  }
107 
108  /** Copying this is prohibited
109  * @param "" deleted */
111 
112  /** Copying this is prohibited
113  * @param "" deleted
114  * @return deleted */
116 
117  /** Move construction
118  * @param o Another pb_converter to move from */
120  : pb_convert(o),
121  interface_(std::move(o.interface_)),
122  name_(boost::core::demangle(typeid(*this).name()))
123  {
124  o.interface_ = nullptr;
125  }
126 
127  /** Move assignment
128  * @param o Another pb_converter to move from
129  * @return A reference to this pb_converter */
132  {
134  this->interface_ = o.interface_;
135  o.interface_ = nullptr;
136  name_ = boost::core::demangle(typeid(*this).name());
137  return *this;
138  }
139 
140  /// Close blackboard interface on destruction
141  virtual ~pb_converter()
142  {
143  close();
144  }
145 
146  /** Deferred initialization, coincides with main thread initialization
147  * @param blackboard Initialized blackboard
148  * @param logger Logger used by the main thread
149  * @param id Blackboard interface ID to open */
150  virtual void
151  init(fawkes::BlackBoard *blackboard, fawkes::Logger *logger, const std::string &id = "") override
152  {
153  pb_convert::init(blackboard, logger);
154  std::string iface_id = iface_id_for_type<IfaceT>();
155 
156  if (id.length()) {
157  if (iface_id.back() != '/')
158  iface_id += '/';
159  iface_id += id;
160  }
161 
162  interface_ = blackboard_->open_for_writing<IfaceT>(iface_id.c_str());
163  logger->log_info(name(), "Initialized %s.", iface_id.c_str());
164  }
165 
166  virtual void
167  handle(const google::protobuf::Message &msg) override
168  {
169  handle(dynamic_cast<const ProtoT &>(msg));
170  }
171 
172  /** Handle a ProtoBuf message with known type. Just delegates to a user-definable method
173  * where the ProtoBuf message is matched up with the appropriate blackboard interface.
174  * @param msg The incoming ProtoBuf message */
175  virtual void
176  handle(const ProtoT &msg)
177  {
178  handle(msg, interface_);
179  interface_->write();
180  }
181 
182  /// @return whether we have a Blackboard interface
183  virtual bool
185  {
186  return interface_;
187  }
188 
189  /// Give up the current blackboard interface (closes it)
190  virtual void
192  {
193  if (is_open()) {
194  blackboard_->close(interface_);
195  interface_ = nullptr;
196  }
197  }
198 
199  /// @return the current blackboard interface
200  IfaceT *
202  {
203  return interface_;
204  }
205 
206  /** @return The blackboard ID suffix if this is part of a sequence. Defaults to "".
207  * Must be overriden for ProtoBuf message types that are part of a sequence and should be put
208  * in separate interfaces. */
209  static std::string
210  get_sequence_id(const ProtoT &)
211  {
212  return "";
213  }
214 
215  /// @return The demangled class name for logging
216  const char *
218  {
219  return name_.c_str();
220  }
221 
222 protected:
223  /** Write the contents of a ProtoBuf message into the appropriate blackboard interface.
224  * Must be specialized by the user for each ProtoBuf message -> blackboard interface pair
225  * @param msg The message received
226  * @param iface The appropriate interface */
227  virtual void handle(const ProtoT &msg, IfaceT *iface);
228 
229 private:
230  IfaceT * interface_;
231  std::string name_;
232 };
233 
234 /**
235  * A special handler for repeated ProtoBuf fields.
236  * @tparam ProtoT the ProtoBuf message type that contains a repeated field we want to unwrap
237  * @tparam The @a pb_converter type that should be used (repeatedly) on the repeated field
238  */
239 template <class ProtoT, class OutputT>
241 {
242 private:
243  typedef google::protobuf::RepeatedPtrField<typename OutputT::input_type> sequence_type;
244 
245 public:
246  /// Default constructor
248  {
249  }
250 
251  /** Handle a repeated field inside a ProtoBuf message, where the individual repeated
252  * sub-messages should be mapped to a blackboard interface each.
253  * @param msg The message containing the repeated field that should be extracted */
254  virtual void
255  handle(const google::protobuf::Message &msg) override
256  {
257  sequence_type fields = extract_sequence(dynamic_cast<const ProtoT &>(msg));
258 
259  if (fields.empty())
260  return;
261 
262  typename sequence_type::const_iterator field_it = fields.begin();
263 
264  for (; field_it != fields.end(); ++field_it) {
265  std::string seq_id = OutputT::get_sequence_id(*field_it);
266  auto map_it = sub_converters_.find(seq_id);
267  if (map_it == sub_converters_.end()) {
268  sub_converters_.insert({seq_id, OutputT()});
269  map_it = sub_converters_.find(seq_id);
270  }
271 
272  if (!map_it->second.is_open())
273  map_it->second.init(blackboard_, logger_, seq_id);
274  map_it->second.handle(*field_it);
275  }
276  }
277 
278  /** Must be implemented by the user.
279  * @param msg The message containing the repeated field
280  * @return The repeated field */
281  virtual const sequence_type &extract_sequence(const ProtoT &msg);
282 
283 private:
284  std::unordered_map<std::string, OutputT> sub_converters_;
285 };
286 
287 } // namespace protoboard
288 
289 #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.