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