Fawkes API  Fawkes Development Version
interface_listener.cpp
00001  
00002 /***************************************************************************
00003  *  interface_listener.cpp - BlackBoard event listener
00004  *
00005  *  Created: Wed Nov 08 10:00:34 2007
00006  *  Copyright  2007-2008  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <blackboard/interface_listener.h>
00025 #include <core/exceptions/system.h>
00026 #include <core/threading/mutex_locker.h>
00027 #include <interface/interface.h>
00028 #include <cstdlib>
00029 #include <cstring>
00030 #include <cstdio>
00031 
00032 namespace fawkes {
00033 #if 0 /* just to make Emacs auto-indent happy */
00034 }
00035 #endif
00036 
00037 /** @class BlackBoardInterfaceListener <blackboard/interface_listener.h>
00038  * BlackBoard interface listener.
00039  * Derive this class if you want to be notified of specific BlackBoard 
00040  * events regarding instances of interfaces.
00041  *
00042  * The bb_interface_* methods are called during the appropriate operation. The
00043  * operation that you carry out in this event handler really has to damn fast, or
00044  * the performance of the whole system will suffer severely. For this reason use
00045  * this notification facility only rarely and only register for the appropriate
00046  * events.
00047  *
00048  * This class provides the basic infrastructure that can be used to build
00049  * your own event handler. During the life time of your event handler your
00050  * first add all the interfaces to the appropriate structures that you want
00051  * to listen for and add the interface types where you want to be notified
00052  * of creation events.
00053  *
00054  * The reader/writer added/removed and data changed notifications act upon a
00055  * specific interface. Any modification done with any instance of the interface
00056  * is reported to you. The interface creation notification deals only
00057  * with types of interfaces. There is no interface deletion notification because
00058  * the general idea is that you opened the interface by yourself for reading and
00059  * thus the deletion will not happen before you close the interface.
00060  *
00061  * You will not be notified if you change data of the interface that you registered
00062  * for or remove your own reading/writing instance of an interface.
00063  *
00064  * Here is a simple life cycle of a BlackBoard interface listener:
00065  * First you create your interface that you want to listen for.
00066  * The protected methods bbil_add_data_interface(), bbil_add_reader_interface(),
00067  * bbil_add_writer_interface() and bbil_add_interface_create_type() have to
00068  * be called with the appropriate interfaces <i>before</i> the event handler is
00069  * actually registered with the interface manager! From
00070  * now on will be called for the all registered events.
00071  * In the end you unregister the event listener and <i>then</i> close any
00072  * interface that you had registered before.
00073  *
00074  * It is important that you first unregister as an event handler before closing
00075  * the interface. Otherwise it could happen that you close the interface and
00076  * the instance is deleted and afterwards an event for that very interface
00077  * happens. A warning is reported via the LibLogger whenever you forget this.
00078  *
00079  * @author Tim Niemueller
00080  * @see BlackBoardInterfaceManager::register_listener()
00081  * @see BlackBoardInterfaceManager::unregister_listener()
00082  */
00083 
00084 /** Constructor.
00085  * @param name_format format of name to identify the listener,
00086  * see sprintf for supported tokens
00087  */
00088   BlackBoardInterfaceListener::BlackBoardInterfaceListener(const char *name_format, ...)
00089 {
00090   va_list arg;
00091   va_start(arg, name_format);
00092   if (vasprintf(&__name, name_format, arg) == -1) {
00093     throw OutOfMemoryException("BlackBoardInterfaceListener ctor: vasprintf() failed");
00094   }
00095   va_end(arg);
00096 
00097   __bbil_queue_mutex = new Mutex();
00098   __bbil_maps_mutex = new Mutex();
00099 }
00100 
00101 
00102 /** Destructor. */
00103 BlackBoardInterfaceListener::~BlackBoardInterfaceListener()
00104 {
00105   free(__name);
00106 
00107   delete __bbil_queue_mutex;
00108   delete __bbil_maps_mutex;
00109 }
00110 
00111 
00112 /** Get BBIL name.
00113  * @return BBIL name
00114  */
00115 const char *
00116 BlackBoardInterfaceListener::bbil_name() const
00117 {
00118   return __name;
00119 }
00120 
00121 
00122 /** BlackBoard data changed notification.
00123  * This is called whenever the data in an interface that you registered for is
00124  * modified. This happens if a writer calls the Interface::write() method.
00125  * @param interface interface instance that you supplied to bbil_add_data_interface()
00126  */
00127 void
00128 BlackBoardInterfaceListener::bb_interface_data_changed(Interface *interface) throw()
00129 {
00130 }
00131 
00132 
00133 /** BlackBoard message received notification.
00134  * This is called whenever a message is received for this interface. This method is
00135  * only called for writing instances of an interface, never on reading instances.
00136  * If you have processed the message already, you can order that the message is not
00137  * enqueued by returning false. Returning true will enqueue the message as usual.
00138  * You should only do very (very!) quick tasks directly in this method, as it is
00139  * out of the regular thread context and can harm performance of other plugins and
00140  * the system as a whole. Note that if you decide to return false the message is
00141  * not referenced. If you want to keep it longer you have to ref() it by yourself.
00142  * An example where this would really make sense is a "STOP" message for the motor,
00143  * which needs to be processed ASAP and maybe even waiting a couple of miliseconds
00144  * for the next cycle is not acceptable.
00145  * @param interface interface instance that you supplied to bbil_add_message_interface()
00146  * @param message the message that was sent
00147  * @return true to get the message enqueued afterwards as usual, false to prevent
00148  * queuing of the message.
00149  */
00150 bool
00151 BlackBoardInterfaceListener::bb_interface_message_received(Interface *interface,
00152                                                            Message *message) throw()
00153 {
00154   return true;
00155 }
00156 
00157 
00158 /** A reading instance has been opened for a watched interface.
00159  * This is called whenever a reading instance of the interface you are watching
00160  * is opened.
00161  * @param interface interface instance that you supplied to bbil_add_reader_interface()
00162  * @param instance_serial the instance serial of the reading instance that has just been
00163  * added.
00164  */
00165 void
00166 BlackBoardInterfaceListener::bb_interface_reader_added(Interface *interface,
00167                                                        unsigned int instance_serial) throw()
00168 {
00169 }
00170 
00171 
00172 /** A reading instance has been closed for a watched interface.
00173  * This is called whenever a reading instance of an interface you are watching
00174  * is closed.
00175  * @param interface interface instance that you supplied to bbil_add_reader_interface()
00176  * @param instance_serial the instance serial of the reading instance that has just been
00177  * removed.
00178  */
00179 void
00180 BlackBoardInterfaceListener::bb_interface_reader_removed(Interface *interface,
00181                                                          unsigned int instance_serial) throw()
00182 {
00183 }
00184 
00185 
00186 /** A writing instance has been opened for a watched interface.
00187  * This is called whenever a writing instance of the interface you are watching
00188  * is opened.
00189  * @param interface interface instance that you supplied to bbil_add_writer_interface()
00190  * @param instance_serial the instance serial of the writing instance that has just been
00191  * added.
00192  */
00193 void
00194 BlackBoardInterfaceListener::bb_interface_writer_added(Interface *interface,
00195                                                        unsigned int instance_serial) throw()
00196 {
00197 }
00198 
00199 
00200 /** A writing instance has been closed for a watched interface.
00201  * This is called whenever a writing instance of an interface you are watching
00202  * is closed.
00203  * @param interface interface instance that you supplied to bbil_add_writer_interface()
00204  * @param instance_serial the instance serial of the writing instance that has just been
00205  * removed.
00206  */
00207 void
00208 BlackBoardInterfaceListener::bb_interface_writer_removed(Interface *interface,
00209                                                          unsigned int instance_serial) throw()
00210 {
00211 }
00212 
00213 
00214 void
00215 BlackBoardInterfaceListener::bbil_queue_add(QueueEntryType type, bool op,
00216                                             InterfaceMap &not_in_map,
00217                                             Interface *interface,
00218                                             const char *hint)
00219 {
00220   MutexLocker lock(__bbil_queue_mutex);
00221 
00222   if (op) {
00223     if (not_in_map.find(interface->uid()) != not_in_map.end() ) {
00224       throw Exception("Interface %s already registered (%s)",
00225                       interface->uid(), hint);
00226     }
00227   }
00228   InterfaceQueue::iterator i;
00229   for (i = __bbil_queue.begin(); i != __bbil_queue.end(); ++i) {
00230     if ((i->type == type) && (*(i->interface) == *interface)) {
00231       __bbil_queue.erase(i);
00232       break;
00233     }
00234   }
00235   QueueEntry qe = { type, op, interface };
00236   __bbil_queue.push_back(qe);
00237 }
00238 
00239 
00240 /** Add an interface to the data modification watch list.
00241  * @param interface interface to watch for data modifications.
00242  */
00243 void
00244 BlackBoardInterfaceListener::bbil_add_data_interface(Interface *interface)
00245 {
00246   bbil_queue_add(DATA, true, __bbil_maps.data, interface, "data");
00247 }
00248 
00249 /** Add an interface to the message received watch list.
00250  * @param interface interface to watch for messages
00251  */
00252 void
00253 BlackBoardInterfaceListener::bbil_add_message_interface(Interface *interface)
00254 {
00255   if ( ! interface->is_writer() ) {
00256     throw Exception("Message received events can only be watched "
00257                     "on writing interface instances (%s)", interface->uid());
00258   }
00259   bbil_queue_add(MESSAGES, true, __bbil_maps.messages, interface, "messages");
00260 }
00261 
00262 
00263 /** Add an interface to the reader addition/removal watch list.
00264  * This method does not mean that you add interfaces that you opened for reading
00265  * but that you add an interface that you want to be informed for when reader
00266  * addition/removal happens.
00267  * @param interface interface to watch for addition/removal of readers
00268  */
00269 void
00270 BlackBoardInterfaceListener::bbil_add_reader_interface(Interface *interface)
00271 {
00272   bbil_queue_add(READER, true, __bbil_maps.reader, interface, "reader");
00273 }
00274 
00275 
00276 /** Add an interface to the writer addition/removal watch list.
00277  * This method does not mean that you add interfaces that you opened for writing
00278  * but that you add an interface that you want to be informed for when writer
00279  * addition/removal happens.
00280  * @param interface interface to watch for addition/removal of writers
00281  */
00282 void
00283 BlackBoardInterfaceListener::bbil_add_writer_interface(Interface *interface)
00284 {
00285   bbil_queue_add(WRITER, true, __bbil_maps.writer, interface, "writer");
00286 }
00287 
00288 
00289 
00290 /** Remove an interface to the data modification watch list.
00291  * Only remove interfaces from the list when not currently registered to
00292  * the BlackBoard or chaos and confusion will come upon you.
00293  * @param interface interface to watch for data modifications.
00294  */
00295 void
00296 BlackBoardInterfaceListener::bbil_remove_data_interface(Interface *interface)
00297 {
00298   bbil_queue_add(DATA, false, __bbil_maps.data, interface, "data");
00299 }
00300 
00301 /** Remove an interface to the message received watch list.
00302  * Only remove interfaces from the list when not currently registered to
00303  * the BlackBoard or chaos and confusion will come upon you.
00304  * @param interface interface to watch for messages
00305  */
00306 void
00307 BlackBoardInterfaceListener::bbil_remove_message_interface(Interface *interface)
00308 {
00309   bbil_queue_add(MESSAGES, false, __bbil_maps.messages, interface, "messages");
00310 }
00311 
00312 
00313 /** Remove an interface to the reader addition/removal watch list.
00314  * Only remove interfaces from the list when not currently registered to
00315  * the BlackBoard or chaos and confusion will come upon you.
00316  * @param interface interface to watch for addition/removal of readers
00317  */
00318 void
00319 BlackBoardInterfaceListener::bbil_remove_reader_interface(Interface *interface)
00320 {
00321   bbil_queue_add(READER, false, __bbil_maps.reader, interface, "reader");
00322 }
00323 
00324 
00325 /** Remove an interface to the writer addition/removal watch list.
00326  * Only remove interfaces from the list when not currently registered to
00327  * the BlackBoard or chaos and confusion will come upon you.
00328  * @param interface interface to watch for addition/removal of writers
00329  */
00330 void
00331 BlackBoardInterfaceListener::bbil_remove_writer_interface(Interface *interface)
00332 {
00333   bbil_queue_add(WRITER, false, __bbil_maps.writer, interface, "writer");
00334 }
00335 
00336 
00337 const BlackBoardInterfaceListener::InterfaceQueue &
00338 BlackBoardInterfaceListener::bbil_acquire_queue() throw()
00339 {
00340   __bbil_queue_mutex->lock();
00341   return __bbil_queue;
00342 }
00343 
00344 void
00345 BlackBoardInterfaceListener::bbil_release_queue(BlackBoard::ListenerRegisterFlag flag) throw()
00346 {
00347   __bbil_maps_mutex->lock();
00348 
00349   InterfaceQueue::iterator i = __bbil_queue.begin();
00350   while (i != __bbil_queue.end()) {
00351     if (i->op) { // add
00352       switch (i->type) {
00353       case DATA:
00354         if (flag & BlackBoard::BBIL_FLAG_DATA) {
00355           __bbil_maps.data[i->interface->uid()] = i->interface;
00356           i = __bbil_queue.erase(i);
00357         } else ++i;
00358         break;
00359 
00360       case MESSAGES:
00361         if (flag & BlackBoard::BBIL_FLAG_MESSAGES) {
00362           __bbil_maps.messages[i->interface->uid()] = i->interface;
00363           i = __bbil_queue.erase(i);
00364         } else ++i;
00365         break;
00366 
00367       case READER:
00368         if (flag & BlackBoard::BBIL_FLAG_READER) {
00369           __bbil_maps.reader[i->interface->uid()] = i->interface;
00370           i = __bbil_queue.erase(i);
00371         } else ++i;
00372         break;
00373 
00374       case WRITER:
00375         if (flag & BlackBoard::BBIL_FLAG_WRITER) {
00376           __bbil_maps.writer[i->interface->uid()] = i->interface;
00377           i = __bbil_queue.erase(i);
00378         } else ++i;
00379         break;
00380 
00381       default:
00382         ++i;
00383         break;
00384       }
00385     } else { // remove
00386       switch (i->type) {
00387       case DATA:
00388         if (flag & BlackBoard::BBIL_FLAG_DATA) {
00389           __bbil_maps.data.erase(i->interface->uid());
00390           i = __bbil_queue.erase(i);
00391         } else ++i;
00392         break;
00393 
00394       case MESSAGES:
00395         if (flag & BlackBoard::BBIL_FLAG_MESSAGES) {
00396           __bbil_maps.messages.erase(i->interface->uid());
00397           i = __bbil_queue.erase(i);
00398         } else ++i;
00399         break;
00400 
00401       case READER:
00402         if (flag & BlackBoard::BBIL_FLAG_READER) {
00403           __bbil_maps.reader.erase(i->interface->uid());
00404           i = __bbil_queue.erase(i);
00405         } else ++i;
00406         break;
00407 
00408       case WRITER:
00409         if (flag & BlackBoard::BBIL_FLAG_WRITER) {
00410           __bbil_maps.writer.erase(i->interface->uid());
00411           i = __bbil_queue.erase(i);
00412         } else ++i;
00413         break;
00414 
00415       default:
00416         ++i;
00417         break;
00418       }
00419     }
00420   }
00421 
00422   __bbil_maps_mutex->unlock();
00423   __bbil_queue_mutex->unlock();
00424 }
00425 
00426 
00427 const BlackBoardInterfaceListener::InterfaceMaps &
00428 BlackBoardInterfaceListener::bbil_acquire_maps() throw()
00429 {
00430   __bbil_maps_mutex->lock();
00431   return __bbil_maps;
00432 }
00433 
00434 void
00435 BlackBoardInterfaceListener::bbil_release_maps() throw()
00436 {
00437   __bbil_queue_mutex->lock();
00438 
00439   InterfaceMap::iterator i;
00440   for (i = __bbil_maps.data.begin(); i != __bbil_maps.data.end(); ++i) {
00441     QueueEntry qe = { DATA, true, i->second };
00442     __bbil_queue.push_back(qe);
00443   }
00444   for (i = __bbil_maps.messages.begin(); i != __bbil_maps.messages.end(); ++i) {
00445     QueueEntry qe = { MESSAGES, true, i->second };
00446     __bbil_queue.push_back(qe);
00447   }
00448   for (i = __bbil_maps.reader.begin(); i != __bbil_maps.reader.end(); ++i) {
00449     QueueEntry qe = { READER, true, i->second };
00450     __bbil_queue.push_back(qe);
00451   }
00452   for (i = __bbil_maps.writer.begin(); i != __bbil_maps.writer.end(); ++i) {
00453     QueueEntry qe = { WRITER, true, i->second };
00454     __bbil_queue.push_back(qe);
00455   }
00456 
00457   __bbil_maps.data.clear();
00458   __bbil_maps.messages.clear();
00459   __bbil_maps.reader.clear();
00460   __bbil_maps.writer.clear();
00461 
00462   __bbil_queue_mutex->unlock();
00463   __bbil_maps_mutex->unlock();
00464 }
00465 
00466 
00467 Interface *
00468 BlackBoardInterfaceListener::bbil_find_interface(const char *iuid,
00469                                                  InterfaceMap &map)
00470 {
00471   MutexLocker lock(__bbil_maps_mutex);
00472   InterfaceMap::iterator i;
00473   if ((i = map.find((char *)iuid)) != map.end()) {
00474     return i->second;
00475   } else {
00476     return NULL;
00477   }
00478 }
00479 
00480 
00481 /** Get interface instance for given UID.
00482  * A data modification notification is about to be triggered. For this the
00483  * interface instance that has been added to the event listener is determined.
00484  * @param iuid interface unique ID
00485  * @return interface instance, NULL if not in list (non-fatal error)
00486  */
00487 Interface *
00488 BlackBoardInterfaceListener::bbil_data_interface(const char *iuid) throw()
00489 {
00490   return bbil_find_interface(iuid, __bbil_maps.data);
00491 }
00492 
00493 
00494 /** Get interface instance for given UID.
00495  * A message received notification is about to be triggered. For this the
00496  * interface instance that has been added to the event listener is determined.
00497  * @param iuid interface unique ID
00498  * @return interface instance, NULL if not in list (non-fatal error)
00499  */
00500 Interface *
00501 BlackBoardInterfaceListener::bbil_message_interface(const char *iuid) throw()
00502 {
00503   return bbil_find_interface(iuid, __bbil_maps.messages);
00504 }
00505 
00506 
00507 /** Get interface instance for given UID.
00508  * A reader notification is about to be triggered. For this the
00509  * interface instance that has been added to the event listener is determined.
00510  * @param iuid interface unique ID
00511  * @return interface instance, NULL if not in list (non-fatal error)
00512  */
00513 Interface *
00514 BlackBoardInterfaceListener::bbil_reader_interface(const char *iuid) throw()
00515 {
00516   return bbil_find_interface(iuid, __bbil_maps.reader);
00517 }
00518 
00519 
00520 /** Get interface instance for given UID.
00521  * A writer notification is about to be triggered. For this the
00522  * interface instance that has been added to the event listener is determined.
00523  * @param iuid interface unique ID
00524  * @return interface instance, NULL if not in list (non-fatal error)
00525  */
00526 Interface *
00527 BlackBoardInterfaceListener::bbil_writer_interface(const char *iuid) throw()
00528 {
00529   return bbil_find_interface(iuid, __bbil_maps.writer);
00530 }
00531 
00532 } // end namespace fawkes