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 
33 namespace 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  */
111 const char *
113 {
114  return name_;
115 }
116 
117 /** BlackBoard data changed notification.
118  * This is called whenever the data in an interface that you registered for is
119  * modified. This happens if a writer calls the Interface::write() method.
120  * @param interface interface instance that you supplied to bbil_add_data_interface()
121  */
122 void
124 {
125 }
126 
127 /** BlackBoard message received notification.
128  * This is called whenever a message is received for this interface. This method is
129  * only called for writing instances of an interface, never on reading instances.
130  * If you have processed the message already, you can order that the message is not
131  * enqueued by returning false. Returning true will enqueue the message as usual.
132  * You should only do very (very!) quick tasks directly in this method, as it is
133  * out of the regular thread context and can harm performance of other plugins and
134  * the system as a whole. Note that if you decide to return false the message is
135  * not referenced. If you want to keep it longer you have to ref() it by yourself.
136  * An example where this would really make sense is a "STOP" message for the motor,
137  * which needs to be processed ASAP and maybe even waiting a couple of miliseconds
138  * for the next cycle is not acceptable.
139  * @param interface interface instance that you supplied to bbil_add_message_interface()
140  * @param message the message that was sent
141  * @return true to get the message enqueued afterwards as usual, false to prevent
142  * queuing of the message.
143  */
144 bool
146  Message * message) throw()
147 {
148  return true;
149 }
150 
151 /** A reading instance has been opened for a watched interface.
152  * This is called whenever a reading instance of the interface you are watching
153  * is opened.
154  * @param interface interface instance that you supplied to bbil_add_reader_interface()
155  * @param instance_serial the instance serial of the reading instance that has just been
156  * added.
157  */
158 void
160  unsigned int instance_serial) throw()
161 {
162 }
163 
164 /** A reading instance has been closed for a watched interface.
165  * This is called whenever a reading instance of an interface you are watching
166  * is closed.
167  * @param interface interface instance that you supplied to bbil_add_reader_interface()
168  * @param instance_serial the instance serial of the reading instance that has just been
169  * removed.
170  */
171 void
173  unsigned int instance_serial) throw()
174 {
175 }
176 
177 /** A writing instance has been opened for a watched interface.
178  * This is called whenever a writing instance of the interface you are watching
179  * is opened.
180  * @param interface interface instance that you supplied to bbil_add_writer_interface()
181  * @param instance_serial the instance serial of the writing instance that has just been
182  * added.
183  */
184 void
186  unsigned int instance_serial) throw()
187 {
188 }
189 
190 /** A writing instance has been closed for a watched interface.
191  * This is called whenever a writing instance of an interface you are watching
192  * is closed.
193  * @param interface interface instance that you supplied to bbil_add_writer_interface()
194  * @param instance_serial the instance serial of the writing instance that has just been
195  * removed.
196  */
197 void
199  unsigned int instance_serial) throw()
200 {
201 }
202 
203 void
204 BlackBoardInterfaceListener::bbil_queue_add(QueueEntryType type,
205  bool op,
206  InterfaceMap & not_in_map,
207  Interface * interface,
208  const char * hint)
209 {
210  MutexLocker lock(bbil_queue_mutex_);
211 
212  if (op) {
213  if (not_in_map.find(interface->uid()) != not_in_map.end()) {
214  throw Exception("Interface %s already registered (%s)", interface->uid(), hint);
215  }
216  }
217  InterfaceQueue::iterator i;
218  for (i = bbil_queue_.begin(); i != bbil_queue_.end(); ++i) {
219  if ((i->type == type) && (*(i->interface) == *interface)) {
220  bbil_queue_.erase(i);
221  break;
222  }
223  }
224  QueueEntry qe = {type, op, interface};
225  bbil_queue_.push_back(qe);
226 }
227 
228 /** Add an interface to the data modification watch list.
229  * @param interface interface to watch for data modifications.
230  */
231 void
233 {
234  bbil_queue_add(DATA, true, bbil_maps_.data, interface, "data");
235 }
236 
237 /** Add an interface to the message received watch list.
238  * @param interface interface to watch for messages
239  */
240 void
242 {
243  if (!interface->is_writer()) {
244  throw Exception("Message received events can only be watched "
245  "on writing interface instances (%s)",
246  interface->uid());
247  }
248  bbil_queue_add(MESSAGES, true, bbil_maps_.messages, interface, "messages");
249 }
250 
251 /** Add an interface to the reader addition/removal watch list.
252  * This method does not mean that you add interfaces that you opened for reading
253  * but that you add an interface that you want to be informed for when reader
254  * addition/removal happens.
255  * @param interface interface to watch for addition/removal of readers
256  */
257 void
259 {
260  bbil_queue_add(READER, true, bbil_maps_.reader, interface, "reader");
261 }
262 
263 /** Add an interface to the writer addition/removal watch list.
264  * This method does not mean that you add interfaces that you opened for writing
265  * but that you add an interface that you want to be informed for when writer
266  * addition/removal happens.
267  * @param interface interface to watch for addition/removal of writers
268  */
269 void
271 {
272  bbil_queue_add(WRITER, true, bbil_maps_.writer, interface, "writer");
273 }
274 
275 /** Remove an interface to the data modification watch list.
276  * Only remove interfaces from the list when not currently registered to
277  * the BlackBoard or chaos and confusion will come upon you.
278  * @param interface interface to watch for data modifications.
279  */
280 void
282 {
283  bbil_queue_add(DATA, false, bbil_maps_.data, interface, "data");
284 }
285 
286 /** Remove an interface to the message received watch list.
287  * Only remove interfaces from the list when not currently registered to
288  * the BlackBoard or chaos and confusion will come upon you.
289  * @param interface interface to watch for messages
290  */
291 void
293 {
294  bbil_queue_add(MESSAGES, false, bbil_maps_.messages, interface, "messages");
295 }
296 
297 /** Remove an interface to the reader addition/removal watch list.
298  * Only remove interfaces from the list when not currently registered to
299  * the BlackBoard or chaos and confusion will come upon you.
300  * @param interface interface to watch for addition/removal of readers
301  */
302 void
304 {
305  bbil_queue_add(READER, false, bbil_maps_.reader, interface, "reader");
306 }
307 
308 /** Remove an interface to the writer addition/removal watch list.
309  * Only remove interfaces from the list when not currently registered to
310  * the BlackBoard or chaos and confusion will come upon you.
311  * @param interface interface to watch for addition/removal of writers
312  */
313 void
315 {
316  bbil_queue_add(WRITER, false, bbil_maps_.writer, interface, "writer");
317 }
318 
320 BlackBoardInterfaceListener::bbil_acquire_queue() throw()
321 {
322  bbil_queue_mutex_->lock();
323  return bbil_queue_;
324 }
325 
326 void
327 BlackBoardInterfaceListener::bbil_release_queue(BlackBoard::ListenerRegisterFlag flag) throw()
328 {
329  bbil_maps_mutex_->lock();
330 
331  InterfaceQueue::iterator i = bbil_queue_.begin();
332  while (i != bbil_queue_.end()) {
333  if (i->op) { // add
334  switch (i->type) {
335  case DATA:
336  if (flag & BlackBoard::BBIL_FLAG_DATA) {
337  bbil_maps_.data[i->interface->uid()] = i->interface;
338  i = bbil_queue_.erase(i);
339  } else
340  ++i;
341  break;
342 
343  case MESSAGES:
344  if (flag & BlackBoard::BBIL_FLAG_MESSAGES) {
345  bbil_maps_.messages[i->interface->uid()] = i->interface;
346  i = bbil_queue_.erase(i);
347  } else
348  ++i;
349  break;
350 
351  case READER:
352  if (flag & BlackBoard::BBIL_FLAG_READER) {
353  bbil_maps_.reader[i->interface->uid()] = i->interface;
354  i = bbil_queue_.erase(i);
355  } else
356  ++i;
357  break;
358 
359  case WRITER:
360  if (flag & BlackBoard::BBIL_FLAG_WRITER) {
361  bbil_maps_.writer[i->interface->uid()] = i->interface;
362  i = bbil_queue_.erase(i);
363  } else
364  ++i;
365  break;
366 
367  default: ++i; break;
368  }
369  } else { // remove
370  switch (i->type) {
371  case DATA:
372  if (flag & BlackBoard::BBIL_FLAG_DATA) {
373  bbil_maps_.data.erase(i->interface->uid());
374  i = bbil_queue_.erase(i);
375  } else
376  ++i;
377  break;
378 
379  case MESSAGES:
380  if (flag & BlackBoard::BBIL_FLAG_MESSAGES) {
381  bbil_maps_.messages.erase(i->interface->uid());
382  i = bbil_queue_.erase(i);
383  } else
384  ++i;
385  break;
386 
387  case READER:
388  if (flag & BlackBoard::BBIL_FLAG_READER) {
389  bbil_maps_.reader.erase(i->interface->uid());
390  i = bbil_queue_.erase(i);
391  } else
392  ++i;
393  break;
394 
395  case WRITER:
396  if (flag & BlackBoard::BBIL_FLAG_WRITER) {
397  bbil_maps_.writer.erase(i->interface->uid());
398  i = bbil_queue_.erase(i);
399  } else
400  ++i;
401  break;
402 
403  default: ++i; break;
404  }
405  }
406  }
407 
408  bbil_maps_mutex_->unlock();
409  bbil_queue_mutex_->unlock();
410 }
411 
412 const BlackBoardInterfaceListener::InterfaceMaps &
413 BlackBoardInterfaceListener::bbil_acquire_maps() throw()
414 {
415  bbil_maps_mutex_->lock();
416  return bbil_maps_;
417 }
418 
419 void
420 BlackBoardInterfaceListener::bbil_release_maps() throw()
421 {
422  bbil_queue_mutex_->lock();
423 
424  InterfaceMap::iterator i;
425  for (i = bbil_maps_.data.begin(); i != bbil_maps_.data.end(); ++i) {
426  QueueEntry qe = {DATA, true, i->second};
427  bbil_queue_.push_back(qe);
428  }
429  for (i = bbil_maps_.messages.begin(); i != bbil_maps_.messages.end(); ++i) {
430  QueueEntry qe = {MESSAGES, true, i->second};
431  bbil_queue_.push_back(qe);
432  }
433  for (i = bbil_maps_.reader.begin(); i != bbil_maps_.reader.end(); ++i) {
434  QueueEntry qe = {READER, true, i->second};
435  bbil_queue_.push_back(qe);
436  }
437  for (i = bbil_maps_.writer.begin(); i != bbil_maps_.writer.end(); ++i) {
438  QueueEntry qe = {WRITER, true, i->second};
439  bbil_queue_.push_back(qe);
440  }
441 
442  bbil_maps_.data.clear();
443  bbil_maps_.messages.clear();
444  bbil_maps_.reader.clear();
445  bbil_maps_.writer.clear();
446 
447  bbil_queue_mutex_->unlock();
448  bbil_maps_mutex_->unlock();
449 }
450 
451 Interface *
452 BlackBoardInterfaceListener::bbil_find_interface(const char *iuid, InterfaceMap &map)
453 {
454  MutexLocker lock(bbil_maps_mutex_);
455  InterfaceMap::iterator i;
456  if ((i = map.find((char *)iuid)) != map.end()) {
457  return i->second;
458  } else {
459  return NULL;
460  }
461 }
462 
463 /** Get interface instance for given UID.
464  * A data modification notification is about to be triggered. For this the
465  * interface instance that has been added to the event listener is determined.
466  * @param iuid interface unique ID
467  * @return interface instance, NULL if not in list (non-fatal error)
468  */
469 Interface *
471 {
472  return bbil_find_interface(iuid, bbil_maps_.data);
473 }
474 
475 /** Get interface instance for given UID.
476  * A message received notification is about to be triggered. For this the
477  * interface instance that has been added to the event listener is determined.
478  * @param iuid interface unique ID
479  * @return interface instance, NULL if not in list (non-fatal error)
480  */
481 Interface *
483 {
484  return bbil_find_interface(iuid, bbil_maps_.messages);
485 }
486 
487 /** Get interface instance for given UID.
488  * A reader notification is about to be triggered. For this the
489  * interface instance that has been added to the event listener is determined.
490  * @param iuid interface unique ID
491  * @return interface instance, NULL if not in list (non-fatal error)
492  */
493 Interface *
495 {
496  return bbil_find_interface(iuid, bbil_maps_.reader);
497 }
498 
499 /** Get interface instance for given UID.
500  * A writer notification is about to be triggered. For this the
501  * interface instance that has been added to the event listener is determined.
502  * @param iuid interface unique ID
503  * @return interface instance, NULL if not in list (non-fatal error)
504  */
505 Interface *
507 {
508  return bbil_find_interface(iuid, bbil_maps_.writer);
509 }
510 
511 } // end namespace fawkes
fawkes::Mutex::lock
void lock()
Lock this mutex.
Definition: mutex.cpp:87
fawkes::BlackBoardInterfaceListener::InterfaceQueue
std::list< QueueEntry > InterfaceQueue
Queue of additions/removal of interfaces.
Definition: interface_listener.h:63
fawkes::BlackBoard::BBIL_FLAG_MESSAGES
@ BBIL_FLAG_MESSAGES
consider message received events
Definition: blackboard.h:89
fawkes::BlackBoard::BBIL_FLAG_DATA
@ BBIL_FLAG_DATA
consider data events
Definition: blackboard.h:88
fawkes::Mutex
Mutex mutual exclusion lock.
Definition: mutex.h:33
fawkes::BlackBoardInterfaceListener::~BlackBoardInterfaceListener
virtual ~BlackBoardInterfaceListener()
Destructor.
Definition: interface_listener.cpp:100
fawkes::BlackBoardInterfaceListener::bb_interface_message_received
virtual bool bb_interface_message_received(Interface *interface, Message *message)
BlackBoard message received notification.
Definition: interface_listener.cpp:145
fawkes::Interface::is_writer
bool is_writer() const
Check if this is a writing instance.
Definition: interface.cpp:438
fawkes::Message
Base class for all messages passed through interfaces in Fawkes BlackBoard.
Definition: message.h:45
fawkes::BlackBoardInterfaceListener::DATA
@ DATA
Data changed event entry.
Definition: interface_listener.h:48
fawkes::BlackBoardInterfaceListener::bbil_remove_reader_interface
void bbil_remove_reader_interface(Interface *interface)
Remove an interface to the reader addition/removal watch list.
Definition: interface_listener.cpp:303
fawkes::MutexLocker
Mutex locking helper.
Definition: mutex_locker.h:34
fawkes::BlackBoardInterfaceListener::bbil_reader_interface
Interface * bbil_reader_interface(const char *iuid)
Get interface instance for given UID.
Definition: interface_listener.cpp:494
fawkes::BlackBoardInterfaceListener::BlackBoardInterfaceListener
BlackBoardInterfaceListener(const char *name_format,...)
Constructor.
Definition: interface_listener.cpp:86
fawkes::Mutex::unlock
void unlock()
Unlock the mutex.
Definition: mutex.cpp:131
fawkes::BlackBoard::BBIL_FLAG_READER
@ BBIL_FLAG_READER
consider reader events
Definition: blackboard.h:90
fawkes::BlackBoardInterfaceListener::bbil_remove_writer_interface
void bbil_remove_writer_interface(Interface *interface)
Remove an interface to the writer addition/removal watch list.
Definition: interface_listener.cpp:314
fawkes::BlackBoardInterfaceListener::bbil_message_interface
Interface * bbil_message_interface(const char *iuid)
Get interface instance for given UID.
Definition: interface_listener.cpp:482
fawkes::BlackBoardInterfaceListener::bb_interface_reader_added
virtual void bb_interface_reader_added(Interface *interface, unsigned int instance_serial)
A reading instance has been opened for a watched interface.
Definition: interface_listener.cpp:159
fawkes::BlackBoardInterfaceListener::READER
@ READER
Reader event entry.
Definition: interface_listener.h:50
fawkes::BlackBoardInterfaceListener::bb_interface_writer_added
virtual void bb_interface_writer_added(Interface *interface, unsigned int instance_serial)
A writing instance has been opened for a watched interface.
Definition: interface_listener.cpp:185
fawkes
Fawkes library namespace.
fawkes::BlackBoardInterfaceListener::bbil_remove_message_interface
void bbil_remove_message_interface(Interface *interface)
Remove an interface to the message received watch list.
Definition: interface_listener.cpp:292
fawkes::BlackBoard::ListenerRegisterFlag
ListenerRegisterFlag
Flags to constrain listener registration/updates.
Definition: blackboard.h:87
fawkes::BlackBoardInterfaceListener::bb_interface_data_changed
virtual void bb_interface_data_changed(Interface *interface)
BlackBoard data changed notification.
Definition: interface_listener.cpp:123
fawkes::BlackBoardInterfaceListener::bbil_add_message_interface
void bbil_add_message_interface(Interface *interface)
Add an interface to the message received watch list.
Definition: interface_listener.cpp:241
fawkes::Interface
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:79
fawkes::BlackBoardInterfaceListener::bbil_remove_data_interface
void bbil_remove_data_interface(Interface *interface)
Remove an interface to the data modification watch list.
Definition: interface_listener.cpp:281
fawkes::BlackBoardInterfaceListener::bbil_writer_interface
Interface * bbil_writer_interface(const char *iuid)
Get interface instance for given UID.
Definition: interface_listener.cpp:506
fawkes::Interface::uid
const char * uid() const
Get unique identifier of interface.
Definition: interface.cpp:677
fawkes::BlackBoardInterfaceListener::InterfaceMaps::messages
InterfaceMap messages
Message received event subscriptions.
Definition: interface_listener.h:72
fawkes::BlackBoardInterfaceListener::WRITER
@ WRITER
Writer event entry.
Definition: interface_listener.h:51
fawkes::BlackBoard::BBIL_FLAG_WRITER
@ BBIL_FLAG_WRITER
consider writer events
Definition: blackboard.h:91
fawkes::BlackBoardInterfaceListener::InterfaceMaps::reader
InterfaceMap reader
Reader event subscriptions.
Definition: interface_listener.h:73
fawkes::BlackBoardInterfaceListener::bb_interface_writer_removed
virtual void bb_interface_writer_removed(Interface *interface, unsigned int instance_serial)
A writing instance has been closed for a watched interface.
Definition: interface_listener.cpp:198
fawkes::BlackBoardInterfaceListener::bbil_data_interface
Interface * bbil_data_interface(const char *iuid)
Get interface instance for given UID.
Definition: interface_listener.cpp:470
fawkes::BlackBoardInterfaceListener::bb_interface_reader_removed
virtual void bb_interface_reader_removed(Interface *interface, unsigned int instance_serial)
A reading instance has been closed for a watched interface.
Definition: interface_listener.cpp:172
fawkes::BlackBoardInterfaceListener::InterfaceMaps::writer
InterfaceMap writer
Writer event subscriptions.
Definition: interface_listener.h:74
fawkes::BlackBoardInterfaceListener::bbil_add_reader_interface
void bbil_add_reader_interface(Interface *interface)
Add an interface to the reader addition/removal watch list.
Definition: interface_listener.cpp:258
fawkes::OutOfMemoryException
System ran out of memory and desired operation could not be fulfilled.
Definition: system.h:32
fawkes::BlackBoardInterfaceListener::MESSAGES
@ MESSAGES
Message received event entry.
Definition: interface_listener.h:49
fawkes::BlackBoardInterfaceListener::bbil_add_data_interface
void bbil_add_data_interface(Interface *interface)
Add an interface to the data modification watch list.
Definition: interface_listener.cpp:232
fawkes::BlackBoardInterfaceListener::bbil_name
const char * bbil_name() const
Get BBIL name.
Definition: interface_listener.cpp:112
fawkes::BlackBoardInterfaceListener::bbil_add_writer_interface
void bbil_add_writer_interface(Interface *interface)
Add an interface to the writer addition/removal watch list.
Definition: interface_listener.cpp:270
fawkes::BlackBoardInterfaceListener::InterfaceMaps::data
InterfaceMap data
Data event subscriptions.
Definition: interface_listener.h:71
fawkes::Exception
Base class for exceptions in Fawkes.
Definition: exception.h:36