Fawkes API
Fawkes Development Version
|
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 ¬_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