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_
protoboard::pb_convert::~pb_convert
virtual ~pb_convert()
Destructor. Does nothing since members aren't owned by this class.
Definition: protobuf_to_bb.cpp:32
protoboard::pb_converter::pb_converter
pb_converter(pb_converter< ProtoT, IfaceT > &&o)
Move construction.
Definition: protobuf_to_bb.h:119
protoboard::pb_converter
The workhorse of the ProtoBuf to Blackboard conversion.
Definition: protobuf_to_bb.h:95
protoboard::pb_converter::operator=
pb_converter< ProtoT, IfaceT > & operator=(pb_converter< ProtoT, IfaceT > &&o)
Move assignment.
Definition: protobuf_to_bb.h:131
protoboard::pb_convert::blackboard_
fawkes::BlackBoard * blackboard_
Blackboard used by the main thread.
Definition: protobuf_to_bb.h:83
protoboard::pb_converter::operator=
pb_converter< ProtoT, IfaceT > & operator=(const pb_converter< ProtoT, IfaceT > &)=delete
Copying this is prohibited.
protoboard::pb_converter::handle
virtual void handle(const ProtoT &msg)
Handle a ProtoBuf message with known type.
Definition: protobuf_to_bb.h:176
protoboard::pb_convert
Default ProtoBuf to blackboard converter.
Definition: protobuf_to_bb.h:54
fawkes::BlackBoard
The BlackBoard abstract class.
Definition: blackboard.h:46
protoboard::pb_convert::pb_convert
pb_convert()
Empty-init constructor.
Definition: protobuf_to_bb.cpp:28
protoboard::pb_converter::pb_converter
pb_converter(const pb_converter< ProtoT, IfaceT > &)=delete
Copying this is prohibited.
protoboard::pb_converter::output_type
IfaceT output_type
The blackboard interface type that the ProtoBuf contents are written to.
Definition: protobuf_to_bb.h:100
protoboard::pb_convert::operator=
pb_convert & operator=(const pb_convert &)=default
Default copy assignment.
protoboard::pb_convert::logger_
fawkes::Logger * logger_
Logger from the main thread.
Definition: protobuf_to_bb.h:85
protoboard::pb_converter::handle
virtual void handle(const google::protobuf::Message &msg) override
Handle a ProtoBuf message by reference.
Definition: protobuf_to_bb.h:167
fawkes::BlackBoard::close
virtual void close(Interface *interface)=0
Close interface.
protoboard::pb_converter::interface
IfaceT * interface()
Definition: protobuf_to_bb.h:201
fawkes::Logger
Interface for logging.
Definition: logger.h:42
protoboard::pb_sequence_converter::extract_sequence
virtual const sequence_type & extract_sequence(const ProtoT &msg)
Must be implemented by the user.
protoboard::pb_converter::handle
virtual void handle(const ProtoT &msg, IfaceT *iface)
Write the contents of a ProtoBuf message into the appropriate blackboard interface.
protoboard::pb_converter::close
virtual void close()
Give up the current blackboard interface (closes it)
Definition: protobuf_to_bb.h:191
protoboard::pb_converter::init
virtual void init(fawkes::BlackBoard *blackboard, fawkes::Logger *logger, const std::string &id="") override
Deferred initialization, coincides with main thread initialization.
Definition: protobuf_to_bb.h:151
protoboard::pb_converter::input_type
ProtoT input_type
The ProtoBuf message type that goes in.
Definition: protobuf_to_bb.h:98
protoboard::pb_sequence_converter::pb_sequence_converter
pb_sequence_converter()
Default constructor.
Definition: protobuf_to_bb.h:247
protoboard::pb_convert::pb_convert
pb_convert(const pb_convert &)=default
Default copy constructor.
protoboard::pb_converter::is_open
virtual bool is_open()
Definition: protobuf_to_bb.h:184
protoboard::pb_sequence_converter::handle
virtual void handle(const google::protobuf::Message &msg) override
Handle a repeated field inside a ProtoBuf message, where the individual repeated sub-messages should ...
Definition: protobuf_to_bb.h:255
protoboard::pb_converter::get_sequence_id
static std::string get_sequence_id(const ProtoT &)
Definition: protobuf_to_bb.h:210
protoboard::pb_converter::name
const char * name()
Definition: protobuf_to_bb.h:217
protoboard::pb_converter::pb_converter
pb_converter()
Empty-init.
Definition: protobuf_to_bb.h:103
fawkes::BlackBoard::open_for_writing
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
protoboard::pb_convert::handle
virtual void handle(std::shared_ptr< google::protobuf::Message > msg)
Dereference msg and pass it on to handle it by reference.
Definition: protobuf_to_bb.cpp:44
protoboard::pb_converter::~pb_converter
virtual ~pb_converter()
Close blackboard interface on destruction.
Definition: protobuf_to_bb.h:141
protoboard::pb_convert::init
virtual void init(fawkes::BlackBoard *blackboard, fawkes::Logger *logger, const std::string &="")
Deferred initialization.
Definition: protobuf_to_bb.cpp:37
protoboard::pb_sequence_converter
A special handler for repeated ProtoBuf fields.
Definition: protobuf_to_bb.h:241
fawkes::MultiLogger::log_info
virtual void log_info(const char *component, const char *format,...)
Log informational message.
Definition: multi.cpp:195