|
Main /
CollectingGlobalStatisticsProblemHow do I collect global statistics in a model? I read some warning agains global variables in the Manual and it recommends using a separate module instead, but I have no idea how to do that. SolutionBased on the mailing list post "RE: Singleton Class or a Statistics Collector Module" on 25 Nov 2003 (online)
Jump to preferred solution, or read the full text below. To start with, global variables aren't very good for use in the model, because they don't get reset when you restart the simulation in Tkenv. The same problem occurs with singletons, which are basically global variables put in the fancy way. Let's see an example:
class ProtocolX : public cSimpleModule
{
protected:
static long totalPkDropped; // counter
virtual void handleMessage(cMessage *msg);
};
long ProtocolX::totalPkDropped = 0; // definition and initialization -- WRONG!
void ProtocolX::handleMessage(cMessage *msg)
{
...
totalPkDropped++;
...
}
The problem is, when you restart the simulation in Tkenv, the counter will start counting where it left off last time, not from zero. C++ initializes global vars at the start of the program, and it has no idea that you also want to reset it when OMNeT++ rebuilds a network. To solve this, you can initialize the variable in the module's A similar problem occurs when you want to record the variables at the end of the simulation. If you just put the code into This yields the following code:
class ProtocolX : public cSimpleModule
{
protected:
static bool statsAlreadyRecorded;
static long totalPkDropped;
virtual void initialize();
virtual void handleMessage(cMessage *msg);
virtual void finish()
};
bool ProtocolX::statsAlreadyRecorded;
long ProtocolX::totalPkDropped;
void ProtocolX::initialize()
{
statsAlreadyRecorded = false;
totalPkDropped = 0;
}
void ProtocolX::handleMessage(cMessage *msg)
{
...
totalPkDropped++;
...
}
void ProtocolX::finish()
{
if (!statsAlreadyRecorded) {
recordScalar("total pks dropped", totalPkDropped);
statsAlreadyRecorded = true;
}
}
But even so, a drawback is that
A slightly more complex but superior solution is to use a a single global module, described below. Using a single global moduleA good way is to introduce a single global module, encapsulate the variables into it as private or protected data members, and expose them via public methods. Other modules can then call these public methods to get or set the values. How does it look like in practice? You could define a module called e.g.
class StatisticsCollector : public cSimpleModule
{
...
public:
void addEndToEndDelay(double d);
void incNumPacketsDropped();
...
};
Define_Module(StatisticsCollector);
Add this definition to your ned file. simple StatisticsCollector endsimple Add a module of this type to your submodules list
submodules:
statisticsCollector: StatisticsCollector;
Then from other modules you can get a pointer to it using
const char *statsModulePath = par("statsModulePath");
cModule *modp = simulation.moduleByPath(statsModulePath);
StatisticsCollector *stats = check_and_cast<StatisticsCollector *>(modp);
Then you could use your double eed = ... stats->addEndToEndDelay(eed); ... If you use classes such as Now if you want to collect separate statistics for "subnet A" and "subnet B", you just create two instances of net.subnetA.**.protX.statsModulePath = "net.subnetA.statisticsCollector" net.subnetB.**.protX.statsModulePath = "net.subnetB.statisticsCollector" And to make the whole thing look nice, it's possible to use a Tkenv plugin which displays a custom panel with whatever data needs to be displayed. (If someone needs this, please drop a note here, and I'll add more info. Andras) --- NotesI am interested in the plugin mentioned which displays the information of collecting statistic. Michael |