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
44namespace 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 */
50template <class IfaceT>
51std::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 */
55std::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 */
62template <class IfaceT, class MessageTypeList>
64{
65public:
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
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
104private:
105 IfaceT * interface_;
106 fawkes::BlackBoard * blackboard_;
108};
109
110/**
111 * Abstract superclass for sending out ProtoBuf messages
112 */
114{
115public:
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
133protected:
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 */
171template <class... IfaceManagerTs>
173{
174public:
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
188private:
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 */
201{
202public:
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
215protected:
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
228private:
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
271template <class... IfaceManagerTs>
274{
275}
276
277template <class... IfaceManagerTs>
278void
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
286template <class... IfaceManagerTs>
287void
289{
290 boost::fusion::for_each(bb_sending_interfaces_,
291 [this](auto &iface_mgr) { iface_mgr.finalize(); });
292}
293
294template <class IfaceT, class MessageT>
295void
297 const bb_iface_manager<IfaceT, type_list<MessageT>> &pair) const
298{
299 manager->handle_message_type<MessageT>(pair.interface());
300}
301
302template <class IfaceT, class MessageT1, class... MessageTs>
303void
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
313template <class MessageT, class InterfaceT>
314void
315BlackboardManager::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.