Fawkes API Fawkes Development Version
interface_listener.cpp
1
2/***************************************************************************
3 * interface_listener.cpp - BlackBoard event listener
4 *
5 * Created: Wed Nov 08 10:00:34 2007
6 * Copyright 2007-2008 Tim Niemueller [www.niemueller.de]
7 *
8 ****************************************************************************/
9
10/* This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version. A runtime exception applies to
14 * this software (see LICENSE.GPL_WRE file mentioned below for details).
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_WRE file in the doc directory.
22 */
23
24#include <blackboard/interface_listener.h>
25#include <core/exceptions/system.h>
26#include <core/threading/mutex_locker.h>
27#include <interface/interface.h>
28
29#include <cstdio>
30#include <cstdlib>
31#include <cstring>
32
33namespace fawkes {
34
35/** @class BlackBoardInterfaceListener <blackboard/interface_listener.h>
36 * BlackBoard interface listener.
37 * Derive this class if you want to be notified of specific BlackBoard
38 * events regarding instances of interfaces.
39 *
40 * The bb_interface_* methods are called during the appropriate operation. The
41 * operation that you carry out in this event handler really has to damn fast, or
42 * the performance of the whole system will suffer severely. For this reason use
43 * this notification facility only rarely and only register for the appropriate
44 * events.
45 *
46 * This class provides the basic infrastructure that can be used to build
47 * your own event handler. During the life time of your event handler your
48 * first add all the interfaces to the appropriate structures that you want
49 * to listen for and add the interface types where you want to be notified
50 * of creation events.
51 *
52 * The reader/writer added/removed and data changed notifications act upon a
53 * specific interface. Any modification done with any instance of the interface
54 * is reported to you. The interface creation notification deals only
55 * with types of interfaces. There is no interface deletion notification because
56 * the general idea is that you opened the interface by yourself for reading and
57 * thus the deletion will not happen before you close the interface.
58 *
59 * You will not be notified if you change data of the interface that you registered
60 * for or remove your own reading/writing instance of an interface.
61 *
62 * Here is a simple life cycle of a BlackBoard interface listener:
63 * First you create your interface that you want to listen for.
64 * The protected methods bbil_add_data_interface(), bbil_add_reader_interface(),
65 * bbil_add_writer_interface() and bbil_add_interface_create_type() have to
66 * be called with the appropriate interfaces <i>before</i> the event handler is
67 * actually registered with the interface manager! From
68 * now on will be called for the all registered events.
69 * In the end you unregister the event listener and <i>then</i> close any
70 * interface that you had registered before.
71 *
72 * It is important that you first unregister as an event handler before closing
73 * the interface. Otherwise it could happen that you close the interface and
74 * the instance is deleted and afterwards an event for that very interface
75 * happens. A warning is reported via the LibLogger whenever you forget this.
76 *
77 * @author Tim Niemueller
78 * @see BlackBoardInterfaceManager::register_listener()
79 * @see BlackBoardInterfaceManager::unregister_listener()
80 */
81
82/** Constructor.
83 * @param name_format format of name to identify the listener,
84 * see sprintf for supported tokens
85 */
87{
88 va_list arg;
89 va_start(arg, name_format);
90 if (vasprintf(&name_, name_format, arg) == -1) {
91 throw OutOfMemoryException("BlackBoardInterfaceListener ctor: vasprintf() failed");
92 }
93 va_end(arg);
94
95 bbil_queue_mutex_ = new Mutex();
96 bbil_maps_mutex_ = new Mutex();
97}
98
99/** Destructor. */
101{
102 free(name_);
103
104 delete bbil_queue_mutex_;
105 delete bbil_maps_mutex_;
106}
107
108/** Get BBIL name.
109 * @return BBIL name
110 */
111const char *
113{
114 return name_;
115}
116
117/** BlackBoard data refreshed notification.
118 * This is called whenever the data in an interface that you registered for is
119 * refreshed. This happens when a writer calls the Interface::write(), regardless
120 * of whether any data have changed.
121 * @param interface interface instance that you supplied to bbil_add_data_interface()
122 * @see bb_interface_data_changed
123 */
124void
126{
127}
128
129/** BlackBoard data changed notification.
130 * This is called whenever the data in an interface that you registered for is
131 * changed. This happens when a writer calls Interface::write() AND any data field
132 * has a different value than it had at the last write().
133 * Note that a change implies a refresh, so when data change, both this method AND
134 * @ref bb_interface_data_refreshed will be called!
135 * @param interface interface instance that you supplied to bbil_add_data_interface()
136 * @see bb_interface_data_refreshed
137 */
138void
140{
141}
142
143/** BlackBoard message received notification.
144 * This is called whenever a message is received for this interface. This method is
145 * only called for writing instances of an interface, never on reading instances.
146 * If you have processed the message already, you can order that the message is not
147 * enqueued by returning false. Returning true will enqueue the message as usual.
148 * You should only do very (very!) quick tasks directly in this method, as it is
149 * out of the regular thread context and can harm performance of other plugins and
150 * the system as a whole. Note that if you decide to return false the message is
151 * not referenced. If you want to keep it longer you have to ref() it by yourself.
152 * An example where this would really make sense is a "STOP" message for the motor,
153 * which needs to be processed ASAP and maybe even waiting a couple of miliseconds
154 * for the next cycle is not acceptable.
155 * @param interface interface instance that you supplied to bbil_add_message_interface()
156 * @param message the message that was sent
157 * @return true to get the message enqueued afterwards as usual, false to prevent
158 * queuing of the message.
159 */
160bool
162 Message * message) noexcept
163{
164 return true;
165}
166
167/** A reading instance has been opened for a watched interface.
168 * This is called whenever a reading instance of the interface you are watching
169 * is opened.
170 * @param interface interface instance that you supplied to bbil_add_reader_interface()
171 * @param instance_serial the instance serial of the reading instance that has just been
172 * added.
173 */
174void
176 Uuid instance_serial) noexcept
177{
178}
179
180/** A reading instance has been closed for a watched interface.
181 * This is called whenever a reading instance of an interface you are watching
182 * is closed.
183 * @param interface interface instance that you supplied to bbil_add_reader_interface()
184 * @param instance_serial the instance serial of the reading instance that has just been
185 * removed.
186 */
187void
189 Uuid instance_serial) noexcept
190{
191}
192
193/** A writing instance has been opened for a watched interface.
194 * This is called whenever a writing instance of the interface you are watching
195 * is opened.
196 * @param interface interface instance that you supplied to bbil_add_writer_interface()
197 * @param instance_serial the instance serial of the writing instance that has just been
198 * added.
199 */
200void
202 Uuid instance_serial) noexcept
203{
204}
205
206/** A writing instance has been closed for a watched interface.
207 * This is called whenever a writing instance of an interface you are watching
208 * is closed.
209 * @param interface interface instance that you supplied to bbil_add_writer_interface()
210 * @param instance_serial the instance serial of the writing instance that has just been
211 * removed.
212 */
213void
215 Uuid instance_serial) noexcept
216{
217}
218
219void
220BlackBoardInterfaceListener::bbil_queue_add(QueueEntryType type,
221 bool op,
222 InterfaceMap & not_in_map,
223 Interface * interface,
224 const char * hint)
225{
226 MutexLocker lock(bbil_queue_mutex_);
227
228 if (op) {
229 if (not_in_map.find(interface->uid()) != not_in_map.end()) {
230 throw Exception("Interface %s already registered (%s)", interface->uid(), hint);
231 }
232 }
233 InterfaceQueue::iterator i;
234 for (i = bbil_queue_.begin(); i != bbil_queue_.end(); ++i) {
235 if ((i->type == type) && (*(i->interface) == *interface)) {
236 bbil_queue_.erase(i);
237 break;
238 }
239 }
240 QueueEntry qe = {type, op, interface};
241 bbil_queue_.push_back(qe);
242}
243
244/** Add an interface to the data modification watch list.
245 * @param interface interface to watch for data modifications.
246 */
247void
249{
250 bbil_queue_add(DATA, true, bbil_maps_.data, interface, "data");
251}
252
253/** Add an interface to the message received watch list.
254 * @param interface interface to watch for messages
255 */
256void
258{
259 if (!interface->is_writer()) {
260 throw Exception("Message received events can only be watched "
261 "on writing interface instances (%s)",
262 interface->uid());
263 }
264 bbil_queue_add(MESSAGES, true, bbil_maps_.messages, interface, "messages");
265}
266
267/** Add an interface to the reader addition/removal watch list.
268 * This method does not mean that you add interfaces that you opened for reading
269 * but that you add an interface that you want to be informed for when reader
270 * addition/removal happens.
271 * @param interface interface to watch for addition/removal of readers
272 */
273void
275{
276 bbil_queue_add(READER, true, bbil_maps_.reader, interface, "reader");
277}
278
279/** Add an interface to the writer addition/removal watch list.
280 * This method does not mean that you add interfaces that you opened for writing
281 * but that you add an interface that you want to be informed for when writer
282 * addition/removal happens.
283 * @param interface interface to watch for addition/removal of writers
284 */
285void
287{
288 bbil_queue_add(WRITER, true, bbil_maps_.writer, interface, "writer");
289}
290
291/** Remove an interface to the data modification watch list.
292 * Only remove interfaces from the list when not currently registered to
293 * the BlackBoard or chaos and confusion will come upon you.
294 * @param interface interface to watch for data modifications.
295 */
296void
298{
299 bbil_queue_add(DATA, false, bbil_maps_.data, interface, "data");
300}
301
302/** Remove an interface to the message received watch list.
303 * Only remove interfaces from the list when not currently registered to
304 * the BlackBoard or chaos and confusion will come upon you.
305 * @param interface interface to watch for messages
306 */
307void
309{
310 bbil_queue_add(MESSAGES, false, bbil_maps_.messages, interface, "messages");
311}
312
313/** Remove an interface to the reader addition/removal watch list.
314 * Only remove interfaces from the list when not currently registered to
315 * the BlackBoard or chaos and confusion will come upon you.
316 * @param interface interface to watch for addition/removal of readers
317 */
318void
320{
321 bbil_queue_add(READER, false, bbil_maps_.reader, interface, "reader");
322}
323
324/** Remove an interface to the writer addition/removal watch list.
325 * Only remove interfaces from the list when not currently registered to
326 * the BlackBoard or chaos and confusion will come upon you.
327 * @param interface interface to watch for addition/removal of writers
328 */
329void
331{
332 bbil_queue_add(WRITER, false, bbil_maps_.writer, interface, "writer");
333}
334
336BlackBoardInterfaceListener::bbil_acquire_queue() noexcept
337{
338 bbil_queue_mutex_->lock();
339 return bbil_queue_;
340}
341
342void
343BlackBoardInterfaceListener::bbil_release_queue(BlackBoard::ListenerRegisterFlag flag) noexcept
344{
345 bbil_maps_mutex_->lock();
346
347 InterfaceQueue::iterator i = bbil_queue_.begin();
348 while (i != bbil_queue_.end()) {
349 if (i->op) { // add
350 switch (i->type) {
351 case DATA:
352 if (flag & BlackBoard::BBIL_FLAG_DATA) {
353 bbil_maps_.data[i->interface->uid()] = i->interface;
354 i = bbil_queue_.erase(i);
355 } else
356 ++i;
357 break;
358
359 case MESSAGES:
361 bbil_maps_.messages[i->interface->uid()] = i->interface;
362 i = bbil_queue_.erase(i);
363 } else
364 ++i;
365 break;
366
367 case READER:
368 if (flag & BlackBoard::BBIL_FLAG_READER) {
369 bbil_maps_.reader[i->interface->uid()] = i->interface;
370 i = bbil_queue_.erase(i);
371 } else
372 ++i;
373 break;
374
375 case WRITER:
376 if (flag & BlackBoard::BBIL_FLAG_WRITER) {
377 bbil_maps_.writer[i->interface->uid()] = i->interface;
378 i = bbil_queue_.erase(i);
379 } else
380 ++i;
381 break;
382
383 default: ++i; break;
384 }
385 } else { // remove
386 switch (i->type) {
387 case DATA:
388 if (flag & BlackBoard::BBIL_FLAG_DATA) {
389 bbil_maps_.data.erase(i->interface->uid());
390 i = bbil_queue_.erase(i);
391 } else
392 ++i;
393 break;
394
395 case MESSAGES:
397 bbil_maps_.messages.erase(i->interface->uid());
398 i = bbil_queue_.erase(i);
399 } else
400 ++i;
401 break;
402
403 case READER:
404 if (flag & BlackBoard::BBIL_FLAG_READER) {
405 bbil_maps_.reader.erase(i->interface->uid());
406 i = bbil_queue_.erase(i);
407 } else
408 ++i;
409 break;
410
411 case WRITER:
412 if (flag & BlackBoard::BBIL_FLAG_WRITER) {
413 bbil_maps_.writer.erase(i->interface->uid());
414 i = bbil_queue_.erase(i);
415 } else
416 ++i;
417 break;
418
419 default: ++i; break;
420 }
421 }
422 }
423
424 bbil_maps_mutex_->unlock();
425 bbil_queue_mutex_->unlock();
426}
427
428const BlackBoardInterfaceListener::InterfaceMaps &
429BlackBoardInterfaceListener::bbil_acquire_maps() noexcept
430{
431 bbil_maps_mutex_->lock();
432 return bbil_maps_;
433}
434
435void
436BlackBoardInterfaceListener::bbil_release_maps() noexcept
437{
438 bbil_queue_mutex_->lock();
439
440 InterfaceMap::iterator i;
441 for (i = bbil_maps_.data.begin(); i != bbil_maps_.data.end(); ++i) {
442 QueueEntry qe = {DATA, true, i->second};
443 bbil_queue_.push_back(qe);
444 }
445 for (i = bbil_maps_.messages.begin(); i != bbil_maps_.messages.end(); ++i) {
446 QueueEntry qe = {MESSAGES, true, i->second};
447 bbil_queue_.push_back(qe);
448 }
449 for (i = bbil_maps_.reader.begin(); i != bbil_maps_.reader.end(); ++i) {
450 QueueEntry qe = {READER, true, i->second};
451 bbil_queue_.push_back(qe);
452 }
453 for (i = bbil_maps_.writer.begin(); i != bbil_maps_.writer.end(); ++i) {
454 QueueEntry qe = {WRITER, true, i->second};
455 bbil_queue_.push_back(qe);
456 }
457
458 bbil_maps_.data.clear();
459 bbil_maps_.messages.clear();
460 bbil_maps_.reader.clear();
461 bbil_maps_.writer.clear();
462
463 bbil_queue_mutex_->unlock();
464 bbil_maps_mutex_->unlock();
465}
466
467Interface *
468BlackBoardInterfaceListener::bbil_find_interface(const char *iuid, InterfaceMap &map)
469{
470 MutexLocker lock(bbil_maps_mutex_);
471 InterfaceMap::iterator i;
472 if ((i = map.find((char *)iuid)) != map.end()) {
473 return i->second;
474 } else {
475 return NULL;
476 }
477}
478
479/** Get interface instance for given UID.
480 * A data modification notification is about to be triggered. For this the
481 * interface instance that has been added to the event listener is determined.
482 * @param iuid interface unique ID
483 * @return interface instance, NULL if not in list (non-fatal error)
484 */
485Interface *
487{
488 return bbil_find_interface(iuid, bbil_maps_.data);
489}
490
491/** Get interface instance for given UID.
492 * A message received notification is about to be triggered. For this the
493 * interface instance that has been added to the event listener is determined.
494 * @param iuid interface unique ID
495 * @return interface instance, NULL if not in list (non-fatal error)
496 */
497Interface *
499{
500 return bbil_find_interface(iuid, bbil_maps_.messages);
501}
502
503/** Get interface instance for given UID.
504 * A reader notification is about to be triggered. For this the
505 * interface instance that has been added to the event listener is determined.
506 * @param iuid interface unique ID
507 * @return interface instance, NULL if not in list (non-fatal error)
508 */
509Interface *
511{
512 return bbil_find_interface(iuid, bbil_maps_.reader);
513}
514
515/** Get interface instance for given UID.
516 * A writer notification is about to be triggered. For this the
517 * interface instance that has been added to the event listener is determined.
518 * @param iuid interface unique ID
519 * @return interface instance, NULL if not in list (non-fatal error)
520 */
521Interface *
523{
524 return bbil_find_interface(iuid, bbil_maps_.writer);
525}
526
527} // end namespace fawkes
Interface * bbil_reader_interface(const char *iuid) noexcept
Get interface instance for given UID.
virtual void bb_interface_writer_added(Interface *interface, Uuid instance_serial) noexcept
A writing instance has been opened for a watched interface.
@ MESSAGES
Message received event entry.
@ DATA
Data changed event entry.
void bbil_add_reader_interface(Interface *interface)
Add an interface to the reader addition/removal watch list.
void bbil_remove_reader_interface(Interface *interface)
Remove an interface to the reader addition/removal watch list.
void bbil_add_message_interface(Interface *interface)
Add an interface to the message received watch list.
void bbil_remove_writer_interface(Interface *interface)
Remove an interface to the writer addition/removal watch list.
virtual bool bb_interface_message_received(Interface *interface, Message *message) noexcept
BlackBoard message received notification.
virtual void bb_interface_data_refreshed(Interface *interface) noexcept
BlackBoard data refreshed notification.
Interface * bbil_message_interface(const char *iuid) noexcept
Get interface instance for given UID.
void bbil_remove_message_interface(Interface *interface)
Remove an interface to the message received watch list.
virtual void bb_interface_data_changed(Interface *interface) noexcept
BlackBoard data changed notification.
void bbil_add_writer_interface(Interface *interface)
Add an interface to the writer addition/removal watch list.
virtual void bb_interface_writer_removed(Interface *interface, Uuid instance_serial) noexcept
A writing instance has been closed for a watched interface.
virtual void bb_interface_reader_added(Interface *interface, Uuid instance_serial) noexcept
A reading instance has been opened for a watched interface.
const char * bbil_name() const
Get BBIL name.
Interface * bbil_writer_interface(const char *iuid) noexcept
Get interface instance for given UID.
std::list< QueueEntry > InterfaceQueue
Queue of additions/removal of interfaces.
void bbil_remove_data_interface(Interface *interface)
Remove an interface to the data modification watch list.
virtual void bb_interface_reader_removed(Interface *interface, Uuid instance_serial) noexcept
A reading instance has been closed for a watched interface.
Interface * bbil_data_interface(const char *iuid) noexcept
Get interface instance for given UID.
void bbil_add_data_interface(Interface *interface)
Add an interface to the data modification watch list.
BlackBoardInterfaceListener(const char *name_format,...)
Constructor.
virtual ~BlackBoardInterfaceListener()
Destructor.
ListenerRegisterFlag
Flags to constrain listener registration/updates.
Definition: blackboard.h:87
@ BBIL_FLAG_READER
consider reader events
Definition: blackboard.h:90
@ BBIL_FLAG_DATA
consider data events
Definition: blackboard.h:88
@ BBIL_FLAG_WRITER
consider writer events
Definition: blackboard.h:91
@ BBIL_FLAG_MESSAGES
consider message received events
Definition: blackboard.h:89
Base class for exceptions in Fawkes.
Definition: exception.h:36
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:80
bool is_writer() const
Check if this is a writing instance.
Definition: interface.cpp:445
const char * uid() const
Get unique identifier of interface.
Definition: interface.cpp:686
Base class for all messages passed through interfaces in Fawkes BlackBoard.
Definition: message.h:44
Mutex locking helper.
Definition: mutex_locker.h:34
Mutex mutual exclusion lock.
Definition: mutex.h:33
void lock()
Lock this mutex.
Definition: mutex.cpp:87
void unlock()
Unlock the mutex.
Definition: mutex.cpp:131
System ran out of memory and desired operation could not be fulfilled.
Definition: system.h:32
A convenience class for universally unique identifiers (UUIDs).
Definition: uuid.h:29
Fawkes library namespace.
InterfaceMap writer
Writer event subscriptions.
InterfaceMap messages
Message received event subscriptions.
InterfaceMap data
Data event subscriptions.
InterfaceMap reader
Reader event subscriptions.