Fawkes API  Fawkes Development Version
blackboard_manager.h
1 
2 /***************************************************************************
3  * Protoboard plugin template
4  * - Header for the blackboard manager
5  *
6  * Copyright 2019 Victor MatarĂ©
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Library General Public License for more details.
18  *
19  * Read the full text in the LICENSE.GPL file in the doc directory.
20  */
21 
22 #ifndef BLACKBOARD_MANAGER_H
23 #define BLACKBOARD_MANAGER_H
24 
25 #include "protoboard_types.h"
26 #include "protobuf_thread.h"
27 
28 #include <aspect/blackboard.h>
29 #include <aspect/blocked_timing.h>
30 #include <aspect/clock.h>
31 #include <aspect/configurable.h>
32 #include <aspect/logging.h>
33 #include <blackboard/utils/on_message_waker.h>
34 #include <core/threading/thread.h>
35 #include <interfaces/ProtobufPeerInterface.h>
36 
37 #include <boost/fusion/include/any.hpp>
38 #include <boost/fusion/include/for_each.hpp>
39 #include <boost/fusion/include/std_tuple.hpp>
40 #include <type_traits>
41 #include <unordered_map>
42 #include <vector>
43 
44 namespace protoboard {
45 
46 /** Map a blackboard interface type to a blackboard interface ID.
47  * Must be implemented by the user. Will be called for every type used with
48  * an @a bb_iface_manager.
49  * @return The interface name (ID) that should be used for the given @tparam type */
50 template <class IfaceT>
51 std::string iface_id_for_type();
52 
53 /** Must be implemented by the user.
54  * @return a vector of paths where ProtoBuf should look for message definitions */
55 std::vector<std::string> proto_dirs();
56 
57 /**
58  * Container for an opened interface of type @tparam IfaceT.
59  * @tparam MessageTypeList must be a @a type_list of the message types that should be
60  * handled on @tparam IfaceT.
61  */
62 template <class IfaceT, class MessageTypeList>
64 {
65 public:
66  /// Constructor. Not responsible for actual initialization.
67  bb_iface_manager() : interface_(nullptr), blackboard_(nullptr), waker_(nullptr)
68  {
69  }
70 
71  /** Open an interface of the given type with the ID supplied by @a iface_id_for_type and
72  * register to wake the given thread when any of the given types arrives.
73  * @param blackboard The blackboard to use
74  * @param thread The thread to wake */
75  void
76  init(fawkes::BlackBoard *blackboard, fawkes::Thread *thread)
77  {
78  blackboard_ = blackboard;
79  interface_ = blackboard_->open_for_writing<IfaceT>(iface_id_for_type<IfaceT>().c_str());
80 
81  // TODO: This rather unspecific waker just triggers a loop(), so we need to go over all the interfaces
82  // each time and check where there's a message in the queue. This should be converted to a
83  // BlackBoardInterfaceListener, which calls a specific method and passes in the affected interface
84  // right away. That should allow significant simplification of all this template hackery.
85  waker_ = new fawkes::BlackBoardOnMessageWaker(blackboard, interface_, thread);
86  }
87 
88  /// Cleanup.
89  void
91  {
92  delete waker_;
93  if (blackboard_ && interface_)
94  blackboard_->close(interface_);
95  }
96 
97  /// @return The managed interface
98  IfaceT *
99  interface() const
100  {
101  return interface_;
102  }
103 
104 private:
105  IfaceT * interface_;
106  fawkes::BlackBoard * blackboard_;
108 };
109 
110 /**
111  * Abstract superclass for sending out ProtoBuf messages
112  */
114 {
115 public:
116  /** Constructor.
117  * @param bb_mgr The BlackboardManager that uses this */
119 
120  /// Destructor
121  virtual ~AbstractProtobufSender();
122 
123  /** Go through all interface managers, empty all blackboard message queues and send out
124  * ProtoBuf messages accordingly.
125  */
126  virtual void process_sending_interfaces() = 0;
127 
128  /// Deferred initialization, coincides with the main thread.
129  virtual void init() = 0;
130  /// Deferred cleanup, concides with the main thread.
131  virtual void finalize() = 0;
132 
133 protected:
134  /// Pointer to the main thread that uses this
136 
137  /**
138  * Functor that iterates over all message types that should be handled on a given interface type
139  * and calls the approate handlers for each message type in turn.
140  */
142  {
143  /// Pointer to the main thread
145 
146  /** Handle a specific blackboard message type on a given interface manager
147  * @tparam IfaceT the interface type handled by the interface manager
148  * @tparam MessageT the current
149  * @param iface_mgr a bb_iface_manager for a specific message type
150  */
151  template <class IfaceT, class MessageT>
152  void operator()(const bb_iface_manager<IfaceT, type_list<MessageT>> &iface_mgr) const;
153 
154  /** Iterate through all given message types on a certain interface and
155  * handle them individually
156  * @tparam IfaceT the interface type
157  * @tparam MessageT1 First message type in the list
158  * @tparam MessageTs Remaining message types
159  * @param iface_mgr a bb_iface_manager with a list of message type to go through
160  */
161  template <class IfaceT, class MessageT1, class... MessageTs>
162  void
163  operator()(const bb_iface_manager<IfaceT, type_list<MessageT1, MessageTs...>> &iface_mgr) const;
164  };
165 };
166 
167 /**
168  * Sends out ProtoBuf messages for all given interface managers
169  * @tparam IfaceManagerTs a set of @a bb_iface_manager instantiations
170  */
171 template <class... IfaceManagerTs>
173 {
174 public:
175  /** Constructor
176  * @param bb_mgr A pointer to the main thread */
178 
179  virtual void init() override;
180  virtual void finalize() override;
181 
182  virtual void
184  {
185  boost::fusion::for_each(bb_sending_interfaces_, handle_messages{this->bb_manager});
186  }
187 
188 private:
189  std::tuple<IfaceManagerTs...> bb_sending_interfaces_;
190 };
191 
192 /**
193  * The main thread that is woken each time a message arrives on any of the interfaces
194  * watched by a @a bb_iface_manager.
195  */
197  public fawkes::LoggingAspect,
200  public fawkes::ClockAspect
201 {
202 public:
203  /** Main thread constructor
204  * @param msg_handler A pointer to the thread that receives incoming ProtoBuf messages */
205  BlackboardManager(ProtobufThead *msg_handler);
206 
207  /** Helper for other classes to get access to the blackboard
208  * @return Pointer to the blackboard used by this thread */
210 
211  /** The ProtoBuf sender must be initialized after construction to beak a dependency loop
212  * @param sender The initialized ProtobufSender */
214 
215 protected:
216  virtual void init() override;
217  virtual void finalize() override;
218  virtual void loop() override;
219 
220  /** Act on a given message on a given blackboard interface. Must be implemented by the user.
221  * @tparam the blackboard interface type
222  * @tparam the blackboard message type
223  * @param iface a pointer to the concrete interface
224  * @param msg a pointer to the concrete message that came in on that interface */
225  template <class InterfaceT, class MessageT>
226  void handle_message(InterfaceT *iface, MessageT *msg);
227 
228 private:
229  friend AbstractProtobufSender;
230 
231  ProtobufThead * message_handler_;
232  fawkes::ProtobufPeerInterface * peer_iface_;
233  pb_conversion_map bb_receiving_interfaces_;
234  fawkes::BlackBoardOnMessageWaker * on_message_waker_;
235  unsigned int next_peer_idx_;
236  std::unique_ptr<AbstractProtobufSender> pb_sender_;
237 
238  void add_peer(fawkes::ProtobufPeerInterface *iface, long peer_id);
239 
240  template <class MessageT, class InterfaceT>
241  void handle_message_type(InterfaceT *iface);
242 
243  template <class InterfaceT>
244  struct on_interface
245  {
246  InterfaceT * iface;
247  BlackboardManager *manager;
248 
249  on_interface(InterfaceT *iface, BlackboardManager *manager) : iface(iface), manager(manager)
250  {
251  }
252 
253  template <class MessageT>
254  void
255  handle_msg_types()
256  {
257  manager->handle_message_type<MessageT>(iface);
258  }
259 
260  // This template is disabled if MessageTs is {} to resolve ambiguity
261  template <class MessageT1, class... MessageTs>
262  typename std::enable_if<(sizeof...(MessageTs) > 0)>::type
263  handle_msg_types()
264  {
265  handle_msg_types<MessageT1>();
266  handle_msg_types<MessageTs...>();
267  }
268  };
269 };
270 
271 template <class... IfaceManagerTs>
273 : AbstractProtobufSender(bb_mgr)
274 {
275 }
276 
277 template <class... IfaceManagerTs>
278 void
280 {
281  boost::fusion::for_each(bb_sending_interfaces_, [this](auto &iface_mgr) {
282  iface_mgr.init(this->bb_manager->get_blackboard(), this->bb_manager);
283  });
284 }
285 
286 template <class... IfaceManagerTs>
287 void
289 {
290  boost::fusion::for_each(bb_sending_interfaces_,
291  [this](auto &iface_mgr) { iface_mgr.finalize(); });
292 }
293 
294 template <class IfaceT, class MessageT>
295 void
297  const bb_iface_manager<IfaceT, type_list<MessageT>> &pair) const
298 {
299  manager->handle_message_type<MessageT>(pair.interface());
300 }
301 
302 template <class IfaceT, class MessageT1, class... MessageTs>
303 void
305  const bb_iface_manager<IfaceT, type_list<MessageT1, MessageTs...>> &iface_mgr) const
306 {
307  BlackboardManager::on_interface<IfaceT>{iface_mgr.interface(), manager}
308  .template handle_msg_types<MessageTs...>();
309 
310  manager->handle_message_type<MessageT1>(iface_mgr.interface());
311 }
312 
313 template <class MessageT, class InterfaceT>
314 void
315 BlackboardManager::handle_message_type(InterfaceT *iface)
316 {
317  if (!iface->msgq_empty()) {
318  while (MessageT *msg = iface->msgq_first_safe(msg)) {
319  try {
320  handle_message(iface, msg);
321  iface->write();
322  } catch (std::exception &e) {
323  logger->log_error(
324  name(), "Exception handling %s on %s: %s", msg->type(), iface->uid(), e.what());
325  }
326  iface->msgq_pop();
327  }
328  }
329 }
330 
331 } // namespace protoboard
332 
333 #endif // BLACKBOARD_MANAGER_H
Thread aspect to access to BlackBoard.
Definition: blackboard.h:34
Wake threads on receiving a blackboard message.
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.
Thread aspect that allows to obtain the current time from the clock.
Definition: clock.h:34
Thread aspect to access configuration data.
Definition: configurable.h:33
Thread aspect to log output.
Definition: logging.h:33
virtual void log_error(const char *component, const char *format,...)
Log error message.
Definition: multi.cpp:237
ProtobufPeerInterface Fawkes BlackBoard Interface.
Thread class encapsulation of pthreads.
Definition: thread.h:46
Abstract superclass for sending out ProtoBuf messages.
virtual void process_sending_interfaces()=0
Go through all interface managers, empty all blackboard message queues and send out ProtoBuf messages...
virtual void init()=0
Deferred initialization, coincides with the main thread.
BlackboardManager * bb_manager
Pointer to the main thread that uses this.
AbstractProtobufSender(BlackboardManager *bb_mgr)
Constructor.
virtual void finalize()=0
Deferred cleanup, concides with the main thread.
virtual ~AbstractProtobufSender()
Destructor.
The main thread that is woken each time a message arrives on any of the interfaces watched by a bb_if...
virtual void finalize() override
Finalize the thread.
void set_protobuf_sender(AbstractProtobufSender *sender)
The ProtoBuf sender must be initialized after construction to beak a dependency loop.
fawkes::BlackBoard * get_blackboard()
Helper for other classes to get access to the blackboard.
virtual void init() override
Initialize the thread.
virtual void loop() override
Code to execute in the thread.
void handle_message(InterfaceT *iface, MessageT *msg)
Act on a given message on a given blackboard interface.
BlackboardManager(ProtobufThead *msg_handler)
Main thread constructor.
Sends out ProtoBuf messages for all given interface managers.
virtual void process_sending_interfaces() override
Go through all interface managers, empty all blackboard message queues and send out ProtoBuf messages...
virtual void finalize() override
Deferred cleanup, concides with the main thread.
virtual void init() override
Deferred initialization, coincides with the main thread.
ProtobufSender(BlackboardManager *bb_mgr)
Constructor.
Receive incoming ProtoBuf messages and pass them on to the BlackboardManager for publication to the a...
Container for an opened interface of type.
bb_iface_manager()
Constructor. Not responsible for actual initialization.
void init(fawkes::BlackBoard *blackboard, fawkes::Thread *thread)
Open an interface of the given type with the ID supplied by iface_id_for_type and register to wake th...
Functor that iterates over all message types that should be handled on a given interface type and cal...
BlackboardManager * manager
Pointer to the main thread.
void operator()(const bb_iface_manager< IfaceT, type_list< MessageT >> &iface_mgr) const
Handle a specific blackboard message type on a given interface manager.
Helper structure to wrap a list of types into a single type.