#include <Blackboard.h>
NOTE: BLACKBOARD IS NO LONGER USED BY THE INET FRAMEWORK -- SUCCESSOR IS NotificationBoard.
Blackboard makes it possible for several modules (representing e.g. protocol layers) to share information, in a publish-subscribe fashion. Participating modules or classes have to implement the BlackboardAccess interface (=have this abstract class as base class).
Anyone can publish data items on the blackboard. In order to allow for some type safety via dynamic_cast, items have to be subclassed from cPolymorphic.
Every item is published with a unique string label. The Blackboard makes no assumption about the format of the label, but it's generally a good idea to make it structured, e.g. with a dotted format ("nic1.linkstatus"). The label can be used by subscribers to identify an item ("I want to subscribe to nic1.linkstatus"). There are other ways to subscribe too, not only by label, e.g. one can browse through all Blackboard items, examine each (its label, C++ class, actual contents etc.) and decide individually whether to subscribe to it or not.
Labels are only used when publishing or subscribing to items. After that, items can be referred to using BBItemRefs. (BBItemRefs are sort of "handles" to blackboard items; think of file handles (fd's) or FILE* variables in Unix and C programming, or window handles (HWND) in the Windows API.) BBItemRefs allow very efficient (constant-time) access to Blackboard items.
Clients may browse blackboard contents and subscribe to items. After subscription, clients will receive notifications via BlackboardAccess callbacks whenever the subscribed item changes. Clients may also subscribe to get notified whenever items are published to or withdrawn from the blackboard.
Notifications are done with callback-like mechanism. Participating modules or classes have to subclass from the abstract base class BlackboardAccess and implement its methods (in Java, one would say clients should implement the BlackboardAccess interface.) This usually means multiple inheritance, but without the problems of multiple inheritance (Multiple inheritance is sometimes considered "evil", but never if used to do "mixins" as here.)
Some examples for usage. The first one, Foo demonstrates subscribe-by-name, the second one, Bar shows subscribe-when-published, and the third one, Zak does subscribe-by-browse.
// subscribe-by-name class Foo : public cSimpleModule, public BlackboardAccess { protected: BBItemRef ref; ... };
void Foo::initialize(int stage) { // to avoid a subscribe-BEFORE-publish a two stage // initialization should be used and all publish calls should // go into the first stage (stage 0) whereas you should subscribe // in the second stage (stage 1) if(stage==0) { ... } else if(stage==1) { ref = blackboard()->subscribe(this,"routingTable"); ... } }
void Foo::blackboardItemChanged(BBItemRef item) { if (item==ref) { RoutingTable *rt = check_and_cast<RoutingTable *>(ref->data()); ... } else ... }
// subscribe-when-published class Bar : public cSimpleModule, public BlackboardAccess { ... };
void Bar::initialize(int stage) { if(stage==0) { blackboard()->registerClient(this); // make sure we get what's already on the blackboard blackboard()->getBlackboardContent(this); } }
void Bar::blackboardItemPublished(BBItemRef item) { // if label begins with "nic." and it's a NetworkInterfaceData, subscribe if (!strncmp(item->label(),"nic.",4) && dynamic_cast<NetworkInterfaceData *>(item->data())) { // new network interface appeared, subscribe to it and do whatever // other actions are necessary blackboard()->subscribe(this, item); ... } }
// subscribe-by-browse class Zak : public cSimpleModule, public BlackboardAccess { ... };
void Zak::letsCheckWhatIsOnTheBlackboard() { // subscribe to all NetworkInterfaceData Blackboard *bb = blackboard(); for (Blackboard::iterator i=bb->begin(); i!=bb->end(); ++i) if (dynamic_cast<NetworkInterfaceData *>((*i)->data())) bb->subscribe(this, *i); }
Public Types | |
typedef std::vector< BlackboardAccess * > | SubscriberVector |
typedef BBItem * | BBItemRef |
Public Member Functions | |
Blackboard () | |
virtual | ~Blackboard () |
Methods for publishers | |
BBItemRef | publish (const char *label, cPolymorphic *item) |
void | withdraw (BBItemRef bbItem) |
void | changed (BBItemRef bbItem, cPolymorphic *item=NULL) |
Methods for subscribers | |
BBItemRef | subscribe (BlackboardAccess *bbClient, const char *label) |
BBItemRef | find (const char *label) |
BBItemRef | subscribe (BlackboardAccess *bbClient, BBItemRef bbItem) |
void | unsubscribe (BlackboardAccess *bbClient, BBItemRef bbItem) |
void | registerClient (BlackboardAccess *bbClient) |
void | removeClient (BlackboardAccess *bbClient) |
void | getBlackboardContent (BlackboardAccess *bbClient) |
iterator | begin () |
iterator | end () |
Protected Types | |
typedef std::map< std::string, BBItem * > | ContentsMap |
Protected Member Functions | |
virtual void | initialize () |
virtual void | handleMessage (cMessage *msg) |
Protected Attributes | |
bool | coreDebug |
Set debugging for the basic module. | |
class | Iterator |
ContentsMap | contents |
SubscriberVector | registeredClients |
Classes | |
class | BBItem |
class | iterator |
|
"Handle" to blackboard items. To get the data or the label, write bbref->data() and bbref->label(), respectively. BBItemRefs become stale when the item gets withdrawn (unpublished) from the blackboard. |
|
|
|
|
|
00037 { 00038 }
|
|
00041 { 00042 while (!contents.empty()) 00043 { 00044 ContentsMap::iterator i = contents.begin(); 00045 delete (*i).second; 00046 contents.erase(i); 00047 } 00048 }
|
|
As with standard C++ classes. 00312 {return iterator(contents.begin());}
|
|
Tell BB that an item has changed (typically called by publisher). When item pointer is omitted, it is assumed that the item object was updated "in place" (as opposed to being replaced by another object). 00110 { 00111 coreEV <<"enter changed; item: "<<bbItem->label()<<" changed -> notify subscribers\n"; 00112 00113 Enter_Method("changed(\"%s\", %s *ptr)", bbItem->label(), item->className()); 00114 00115 // update data pointer 00116 if (item) 00117 bbItem->_item = item; 00118 // notify subscribers 00119 SubscriberVector& vec = bbItem->subscribers; 00120 for (SubscriberVector::iterator i=vec.begin(); i!=vec.end(); ++i){ 00121 (*i)->blackboardItemChanged(bbItem); 00122 } 00123 }
|
|
As with standard C++ classes. 00317 {return iterator(contents.end());}
|
|
Find item with given label on the BB 00141 { 00142 ContentsMap::iterator k = contents.find(std::string(label)); 00143 return k==contents.end() ? NULL : (*k).second; 00144 }
|
|
Utility function: the client gets immediate notification with all items currently on clipboard (as if they were added just now). This may simplify initialization code in a subscribe-when-published style client. 00204 { 00205 Enter_Method("getBlackboardContent(this)"); 00206 00207 for (ContentsMap::iterator i=contents.begin(); i!=contents.end(); ++i) 00208 bbClient->blackboardItemPublished((*i).second); 00209 }
|
|
Does nothing. 00060 {
00061 error("Blackboard doesn't handle messages, it can be accessed via direct method calls");
00062 }
|
|
Initialize BB. 00051 { 00052 if(hasPar("coreDebug")) 00053 coreDebug = par("coreDebug").boolValue(); 00054 else 00055 coreDebug = false; 00056 WATCH_PTRMAP(contents); 00057 }
|
|
Publish new item on the BB, with the given label. 00065 { 00066 Enter_Method("publish(\"%s\", %s *ptr)", label, item->className()); 00067 00068 // check uniqueness of label 00069 ContentsMap::iterator k = contents.find(std::string(label)); 00070 if (k!=contents.end()) 00071 error("publish(): blackboard already contains an item with label `%s'", label); 00072 00073 // add to BB contents 00074 BBItem *bbitem = new BBItem(); 00075 bbitem->_item = item; 00076 bbitem->_label = label; 00077 contents[bbitem->_label] = bbitem; 00078 00079 coreEV <<"publish "<<label<<" on bb\n"; 00080 coreEV <<"bbItem->label: "<<bbitem->label()<<endl; 00081 // notify 00082 SubscriberVector& vec = registeredClients; 00083 for (SubscriberVector::iterator i=vec.begin(); i!=vec.end(); ++i){ 00084 (*i)->blackboardItemPublished(bbitem); 00085 } 00086 return bbitem; 00087 }
|
|
Generally subscribe to notifications about items being published to/withdrawn from BB. 00177 { 00178 Enter_Method("registerClient(this)"); 00179 00180 // check if already subscribed 00181 SubscriberVector& vec = registeredClients; 00182 if (std::find(vec.begin(), vec.end(), bbClient)!=vec.end()) 00183 return; // ok, already subscribed 00184 00185 // add subscriber 00186 vec.push_back(bbClient); 00187 }
|
|
Cancel subscription initiated by registerClient(). 00190 { 00191 Enter_Method("removeClient(this)"); 00192 00193 // check if subscribed 00194 SubscriberVector& vec = registeredClients; 00195 SubscriberVector::iterator k = std::find(vec.begin(), vec.end(), bbClient); 00196 if (k==vec.end()) 00197 return; // ok, not subscribed 00198 00199 // remove subscriber 00200 vec.erase(k); 00201 }
|
|
Subscribe to a BB item identified by item reference 00147 { 00148 Enter_Method("subscribe(this,\"%s\")", bbItem->label()); 00149 00150 // check if already subscribed 00151 SubscriberVector& vec = bbItem->subscribers; 00152 if (std::find(vec.begin(), vec.end(), bbClient)!=vec.end()) 00153 return bbItem; // already subscribed 00154 00155 // add subscriber 00156 vec.push_back(bbClient); 00157 00158 coreEV <<"sucessfully subscribed for item: "<<bbItem->label()<<endl; 00159 return bbItem; 00160 }
|
|
Subscribe to a BB item identified by a label 00126 { 00127 Enter_Method("subscribe(this,\"%s\")", label); 00128 00129 // look up item by label 00130 BBItemRef item = find(label); 00131 if (!item) 00132 error("subscribe(): item labelled `%s' not on blackboard", label); 00133 00134 // subscribe 00135 coreEV <<"subscribe for "<<label<<" on bb\n"; 00136 subscribe(bbClient, item); 00137 return item; 00138 }
|
|
Unsubcribe module from change notifications 00163 { 00164 Enter_Method("unsubscribe(this,\"%s\")", bbItem->label()); 00165 00166 // check if already subscribed 00167 SubscriberVector& vec = bbItem->subscribers; 00168 SubscriberVector::iterator k = std::find(vec.begin(), vec.end(), bbClient); 00169 if (k==vec.end()) 00170 return; // ok, not subscribed 00171 00172 // remove subscriber 00173 vec.erase(k); 00174 }
|
|
Withdraw (unpublish) item from the BB (typically called by publisher). 00090 { 00091 Enter_Method("withdraw(\"%s\")", bbItem->label()); 00092 00093 // find on BB 00094 ContentsMap::iterator k = contents.find(bbItem->_label); 00095 if (k==contents.end()) 00096 error("withdraw(): item labelled `%s' is not on clipboard (BBItemRef stale?)", bbItem->_label.c_str()); 00097 00098 // notify subscribers 00099 SubscriberVector& vec = bbItem->subscribers; 00100 for (SubscriberVector::iterator i=vec.begin(); i!=vec.end(); ++i) 00101 (*i)->blackboardItemWithdrawn(bbItem); 00102 00103 // remove 00104 contents.erase(k); 00105 bbItem->_item = NULL; // may make bogus code crash sooner 00106 delete bbItem; 00107 }
|
|
|
|
Set debugging for the basic module.
|
|
|
|
|