The NED file will need a few changes. First of all, the Txc module will need to have multiple input and output gates:
simple Txc9 gates: in: in[]; // declare in[] and out[] to be vector gates out: out[]; endsimple
The [ ] turns the gates into gate vectors. The size of the vector (the number of gates) will be determined where we use Txc to build the network.
module Tictoc9 submodules: tic: Txc9[6]; // we'll have 6 Txc modules display: "i=block/process"; connections: tic[0].out++ --> delay 100ms --> tic[1].in++; tic[0].in++ <-- delay 100ms <-- tic[1].out++; tic[1].out++ --> delay 100ms --> tic[2].in++; tic[1].in++ <-- delay 100ms <-- tic[2].out++; tic[1].out++ --> delay 100ms --> tic[4].in++; tic[1].in++ <-- delay 100ms <-- tic[4].out++; tic[3].out++ --> delay 100ms --> tic[4].in++; tic[3].in++ <-- delay 100ms <-- tic[4].out++; tic[4].out++ --> delay 100ms --> tic[5].in++; tic[4].in++ <-- delay 100ms <-- tic[5].out++; endmodule
Here we created 6 modules as a module vector, and connected them.
The resulting topology looks like this:
In this version, tic[0] will generate the message to be sent around. This is done in initialize(), with the help of the index() function which returns the index of the module in the vector.
The meat of the code is the forwardMessage() function which we invoke from handleMessage() whenever a message arrives at the node. It draws a random gate number (size() is the size of the gate vector), and sends out message on that gate.
void Txc9::forwardMessage(cMessage *msg) { // In this example, we just pick a random gate to send it on. // We draw a random number between 0 and the size of gate `out[]'. int n = gate("out")->size(); int k = intuniform(0,n-1); ev << "Forwarding message " << msg << " on port out[" << k << "]\n"; send(msg, "out", k); }
When the message arrives at tic[3], its handleMessage() will delete the message.
See the full code in txc9.cc.
Exercise: you'll notice that this simple "routing" is not very efficient: often the packet keeps bouncing between two nodes for a while before it is sent to a different direction. This can be improved somewhat if nodes don't send the packet back to the sender. Implement this. Hints: cMessage::arrivalGate(), cGate::index(). Note that if the message didn't arrive via a gate but was a self-message, then arrivalGate() returns NULL.
Sources: tictoc9.ned, txc9.cc, omnetpp.ini
The best way is to subclass cMessage and add destination as a data member. Hand-coding the message class is usually tedious because it contains a lot of boilerplate code, so we let OMNeT++ generate the class for us. The message class specification is in tictoc10.msg:
message TicTocMsg10 { fields: int source; int destination; int hopCount = 0; }
The makefile is set up so that the message compiler, opp_msgc is invoked and it generates tictoc10_m.h and tictoc10_m.cc from the message declaration. They will contain a generated TicTocMsg10 class subclassed from cMessage; the class will have getter and setter methods for every field.
We'll include tictoc10_m.h into our C++ code, and we can use TicTocMsg10 as any other class.
#include "tictoc10_m.h"
For example, we use the following lines in generateMessage() to create the message and fill its fields.
TicTocMsg10 *msg = new TicTocMsg10(msgname); msg->setSource(src); msg->setDestination(dest); return msg;
Then, handleMessage() begins like this:
void Txc10::handleMessage(cMessage *msg) { TicTocMsg10 *ttmsg = check_and_cast<TicTocMsg10 *>(msg); if (ttmsg->getDestination()==index())
In the argument to handleMessage(), we get the message as a cMessage * pointer. However, we can only access its fields defined in TicTocMsg10 if we cast msg to TicTocMsg10 *. Plain C-style cast ((TicTocMsg10 *)msg
) is not safe because if the message is not a TicTocMsg10 after all the program will just crash, causing an error which is difficult to explore.
C++ offers a solution which is called dynamic_cast. Here we use check_and_cast<>() which is provided by OMNeT++: it tries to cast the pointer via dynamic_cast, and if it fails it stops the simulation with an error message, similar to the following:
In the next line, we check if the destination address is the same as the node's address. The index()
member function returns the index of the module in the submodule vector (remember, in the NED file we declarared it as tic: Txc10[6]
, so our nodes have addresses 0..5).
To make the model execute longer, after a message arrives to its destination the destination node will generate another message with a random destination address, and so forth. Read the full code: txc10.cc.
When you run the model, it'll look like this:
You can double-click on the messages to open an inspector for them. (You'll either have to temporarily stop the simulation for that, or to be very fast in handling the mouse). The inspector window displays lots of useful information; the message fields can be seen on the Contents page.
Sources: tictoc10.ned, tictoc10.msg, txc10.cc, omnetpp.ini
Exercise: In this model, there is only one message underway at any given moment: nodes only generate a message when another message arrives at them. We did it this way to make it easier to follow the simulation. Change the module class so that instead, it generates messages periodically. The interval between messages should be a module parameter, returning exponentially distributed random numbers.
NEXT: 4. Adding statistics collection