When you evaluate the performance of a protocol, you need information on internal state changes of your protocol, maybe even from protocols that you use. You could monitor these changes using e.g. vector files from within you protocol and remove these monitors once you are done. Another way is to use a blackboard. The state changes are published on it, and the monitors subscribe to these values. This allows other researchers to tap your protocol for performance evaluation, while imposing nearly no runtime penalty when the information is not needed.
Maybe even more importantly, the blackboard allows you to exchange information between layers, without passing pointers to the modules around. Some items might not only be interesting for the layer they are created/changed in. The physical layer for example (snrEval in the MF) can sense whether a channel is busy or not. If the MAC protocol is based on carrier sense it needs the information the physical layer has. The Blackboard is a module where the corresponding information can be published and then is accessible for any module interested in it.
The BasicModule provides everything necessary to interact with the blackboard. It is derived from ImNotifiable - a pure virtual base class that allows the blackboard to inform your module of changes - and contains a pointer named bb to the blackboard module.
void YourClass::initialize(int stage) { BaseClass::initialize(stage); if (stage == 0){ SomeBBItem item; catItem = bb->subscribe(this, &item, -1); ... } else if(stage == 1) { ...
In case you want to subscribe to a parameter, i.e. you want to be informed each time the content/value of that parameter changes, you have to call the Blackboard function subscribe(). You have to include a pointer to your module, a pointer to an object of class SomeBBItem, and a scope. This function returns an integer that is unique for the class SomeBBItem. The section 6.2 shows how to use this parameter. The Blackboard uses the this pointer to notify the module of published changes. The object pointer & item helps the Blackboard to learn something about the changes that you are subscribing. The Blackboard uses it to establish the connection between the catItem and the &item. The last parameter of the function determines a scope, it needs a more detailed explanation. A change can be published by several modules within a host. Consider the RSSI and a host with more than one network card. Each of these cards will publish a separate RSSI - but it will be of the same class and can hence not be distinguished. However, the meaning of the RSSI is constrained to one card (its scope): it makes no sense for a MAC protocol of one card to take the RSSI of the other card into consideration. The solution is to include the scope into the publication and let the Blackboard do some filtering. In the example code the subscriber uses -1 as the scope - a wildcard that subscribes him to all changes published by any module. If you want to subscribe to some specific stuff, you must make sure that the scope (for network cards usually the module id of the card) under which the parameter is published, matches the subscribed scope.
bb->unsubscribe(this, catItem);Your module will not get notifications for this parameter anymore.
void YourClass::receiveBBItem(int category, \ const BBItem *details, int scopeModuleId) { // in case you want to handle messages here Enter_Method_Silent(); // in case not you but your base class subscribed: BaseClass::receiveBBItem(category, details, scopeModuleId); // react on the stuff that you subscribed if(category == catItem) { someBBItemPtr = static_cast<const SomeBBItem *>(details); // do something } }
The parameters of this function have already been explained in section 6.1. The first is the category, the integer that the subscribe function returned, the second is a pointer to an object of the class that you subscribed to and the third is the scope of this parameter. You should place the Enter_Method or Enter_Method_Silent macros at the beginning. This allows you to schedule or cancel messages, besides doing some animation. You should also inform the base class. This pattern is probably familiar from the initialize function. Now your base class will be informed about all changes it subscribed (and some of them are not interesting to YourClass) and the changes that YourClass subscribed. This has two implications: if you forget this line, the simulation may stop completely or behave strange. Secondly, YourClass must gracefully handle any items that it did not subscribe. To support you in that task, the item that you receive from the Blackboard is read-only.
In the next step, the published item is handled. It is easy to determine its type: just check (may be in a switch statement) the category and cast it to the right class. Since the association between a class and its category is fixed, a static cast is safe and fast. Now YourClass can interpret the content and do something about it.
class MissedLunches : public BBItem { BBITEM_METAINFO(BBItem); protected: int counter; public: double getMissedLunches () const { return counter; } void setMissedLunches(int c) { counter = c; } void addMissedLunch() { counter++; } MissedLunches(int c=0) : BBItem(), counter(c) { // constructor }; std::string info() const { // for inspection std::ostringstream ost; ost << " You missed " << counter << " lunches."; return ost.str(); } };This associates the meaning ``missed lunch'' with an integer. At least, this is how most humans would interpret the name of the class. The class is derived directly from BBItem (which is derived from cPolymorphic), the base class for all items that can be published on the blackboard.
For advanced users: Of course it could also refine a class (say
MissedMeals) and have a different parent class. The call to
BBITEM_METAINFO(BBItem);
helps the Blackboard to track the
inheritance tree. If MissedLunches would have been derived from
MissedMeals this should read BBITEM_METAINFO(MissedMeals);
.
This trick allows the Blackboard to deliver MissedLunches to all
subscribers of MissedMeals.
After this you simply go ahead with a standard C++ class definition, and do what you like. The info function is optional, but helpful with debugging.
void YourPublisher::initialize(int stage) { BasicModule::initialize(stage); if(stage == 0) { // initialize it missedLunch.setCounter(0); // get the category missedLunchCat = bb->getCategory(&missedLunch); } else if(stage == 1) { bb->publishBBItem(missedLunchCat, \ &missedLunch, parentModule()->id()); } }
You should initialize missedLunches properly. The next step is to
figure the category out, or put differently establish the connection
between a category integer and a class. Now you are all set to publish
it. We recommend that you publish your data in stage 1, this allows
all subscribers to initialize the copies they might have. Publishing
is a simple call to bb->publishBBItem
. The first parameter is
the category, the second a pointer to the published object that
carries all the information that you want to publish, and the third is
the scope. parentModule()->id()
may not be useful as a scope
in your case. There is no default or wildcard - you have to think
about a reasonable and easy to understand scope.
Actually, we already covered how to publish a change when the simulation runs. Let us assume that YourPublisher tracks missed lunches somehow:
void YourPublisher::handleMissedLunch() { // update information missedLunch.addMissedLunch(); // publish it. bb->publishBBItem(missedLunchCat, \ &missedLunch, parentModule()->id()); }
YourPublisher simply has to update the information, and call the publish function again. That's it.