bitmaps/block/process.gif
), and paint it cyan for tic and yellow for toc. This is achieved by adding display strings to the NED file. The i=
tag in the display string specifies the icon.
submodules: tic: Txc2; display: "i=block/process,cyan"; // Note "i=<icon>,<color>" toc: Txc2; display: "i=block/process,gold"; // Here too connections:
You can see the result here:
We also modify the C++ file to add some debug messages to Txc1 by writing to the OMNeT++ ev
object like this:
ev << "Sending initial message\n";
and
ev << "Received message `" << msg->name() << "', sending it out again\n";
When you run the simulation in the OMNeT++ GUI Tkenv, the following output will appear in the main text window:
You can also open separate output windows for tic and toc by right-clicking on their icons and choosing Module output from the menu. This feature will be useful when you have a large model ("fast scrolling logs syndrome") and you're interested only in the log messages of specific module.
Sources: tictoc2.ned, txc2.cc, omnetpp.ini
We add the counter as a class member:
class Txc3 : public cSimpleModule { private: int counter; // Note the counter here protected:
We set the variable to 10 in initialize() and decrement in handleMessage(), that is, on every message arrival. After it reaches zero, the simulation will run out of events and terminate.
Note the
WATCH(counter);
line in the source: this makes it possible to see the counter value in Tkenv. Double-click on tic's icon, then choose the Contents page from the inspector window that pops up.
As you continue running the simulation, you can follow as the counter keeps decrementing until it reaches zero.
Sources: tictoc3.ned, txc3.cc, omnetpp.ini
Module parameters have to be declared in the NED file. The data type can be numeric, string, bool, or xml (the latter is for easy access to XML config files), among others.
simple Txc4 parameters: limit: numeric const; // declare the new parameter gates:
We also have to modify the C++ code to read the parameter in initialize(), and assign it to the counter.
counter = par("limit");
Now, we can assign the parameters in the NED file or from omnetpp.ini. Assignments in the NED file take precedence. Typically you'll want to leave most parameter assigments to omnetpp.ini because it makes the model a lot more flexible.
Here, we assign one parameter in the NED file:
// Adding module parameters. // module Tictoc4 submodules: tic: Txc4; parameters: limit = 8; // tic's limit is 8 display: "i=block/process,cyan"; toc: Txc4; // note that we leave toc's limit unbound here display: "i=block/process,gold"; connections:
and the other in omnetpp.ini:
tictoc4.toc.limit = 5
Note that because omnetpp.ini supports wildcards, and parameters assigned from NED files take precedence over the ones in omnetpp.ini, we could have used
tictoc4.t*c.limit=5
or
tictoc4.*.limit=5
or even
**.limit=5
with the same effect. (The difference between * and ** is that * will not match a dot and ** will.)
In Tkenv, you can inspect module parameters either in the object tree on the left-hand side of the main window, or in the Parameters page of the module inspector (opened via double-clicking on the module icon).
The module with the smaller limit will delete the message and thereby conclude the simulation.
Sources: tictoc4.ned, txc4.cc, omnetpp.ini
We added two cMessage * variables, event
and tictocMsg
to the class, to remember the message we use for timing and message whose processing delay we are simulating.
class Txc5 : public cSimpleModule { private: cMessage *event; // pointer to the event object which we'll use for timing cMessage *tictocMsg; // variable to remember the message until we send it back public:
We "send" the self-messages with the scheduleAt() function, specifying when it should be delivered back to the module.
scheduleAt(simTime()+1.0, event);
In handleMessage() now we have to differentiate whether a new message has arrived via the input gate or the self-message came back (timer expired). Here we are using
if (msg==event)
but we could have written
if (msg->isSelfMessage())
as well.
We have left out the counter, to keep the source code small.
The result of running the simulation can be seen below.
Sources: tictoc5.ned, txc5.cc, omnetpp.ini
// The "delayTime" module parameter can be set to values like // "exponential(5)" (tictoc6.ned, omnetpp.ini), and then here // we'll get a different delay every time. double delay = par("delayTime"); ev << "Message arrived, starting to wait " << delay << " secs...\n"; tictocMsg = msg; scheduleAt(simTime()+delay, event);
In addition, we'll "lose" (delete) the packet with a small (hardcoded) probability.
if (uniform(0,1) < 0.1) { ev << "\"Losing\" message\n"; delete msg; }
We'll assign the parameters in omnetpp.ini:
tictoc6.tic.delayTime = exponential(3) tictoc6.toc.delayTime = truncnormal(3,1)
You can try that no matter how many times you re-run the simulation (or restart it, Simulate|Rebuild network menu item), you'll get exactly the same results. This is because OMNeT++ uses a deterministic algorithm (by default the Mersenne Twister RNG) to generate random numbers, and initializes it to the same seed. This is important for reproducible simulations. You can experiment with different seeds if you add the following lines to omnetpp.ini:
[General] seed-0-mt=532569 # or any other 32-bit value
From the syntax you have probably guessed that OMNeT++ supports more than one RNGs. That's right, however, all models in this tutorial use RNG 0.
Exercise: Try other distributions as well.
Sources: tictoc6.ned, txc6.cc, omnetpp.ini
Here's toc's code:
void Toc7::handleMessage(cMessage *msg) { if (uniform(0,1) < 0.1) { ev << "\"Losing\" message.\n"; bubble("message lost"); // making animation more informative... delete msg; } else
Thanks to the bubble() call in the code, toc'll display a callout whenever it drops the message.
So, tic will start a timer whenever it sends the message. When the timer expires, we'll assume the message was lost and send another one. If toc's reply arrives, the timer has to be cancelled. The timer will be (what else?) a self-message.
scheduleAt(simTime()+timeout, timeoutEvent);
Cancelling the timer will be done with the cancelEvent() call. Note that this does not prevent us from being able to reuse the same timeout message over and over.
cancelEvent(timeoutEvent);
You can read Tic's full source in txc7.cc.
Sources: tictoc7.ned, txc7.cc, omnetpp.ini
What we do here is keep the original packet and send only copies of it. We delete the original when toc's acknowledgement arrives. To make it easier to visually verify the model, we'll include a message sequence number the message names.
In order to avoid handleMessage() growing too large, we'll put the corresponding code into two new functions, generateNewMessage() and sendCopyOf() and call them from handleMessage().
The functions:
cMessage *Tic8::generateNewMessage() { // Generate a message with a different name every time. char msgname[20]; sprintf(msgname, "tic-%d", ++seq); cMessage *msg = new cMessage(msgname); return msg; }
void Tic8::sendCopyOf(cMessage *msg) { // Duplicate message and send the copy. cMessage *copy = (cMessage *) msg->dup(); send(copy, "out"); }
Sources: tictoc8.ned, txc8.cc, omnetpp.ini
NEXT: 3. Turning it into a real network