33
Tic-Toc Tutorial Contents TicToc Tutorial for OMNeT++ This short tutorial to OMNeT++ guides you through an example of modeling and simulation, showing you along the way some of the commonly used OMNeT++ features. The tutorial is based on the Tictoc example simulation, which you can find in the samples/tictoc directory of your OMNeT++ installation, so you can try out immediately how the examples work. However, you'll find the tutorial much more useful if you actually carry out at least the first steps described here. Note We assume here that you have a working OMNeT++ installation. We also assume that you have a good C++ knowledge, and you are in general familiar with C/C++ development (editing source files, compiling, debugging etc.) on your operating system. (The latter two are out of our scope here – there are excellent books, tutorials on the web, etc. if you need to update your knowledge on that.) We highly recommend to use the OMNeT++ Integrated Development Environment for editing and building your simulations. To make the examples easier to follow, all source code in here is cross-linked to the OMNeT++ API documentation. This document and the TicToc model are an expanded version of the original TicToc tutorial from Ahmet Sekercioglu (Monash University). Contents 1. Getting started 2. Enhancing the 2-node TicToc 3. Turning it into a real network 4. Adding statistics collection 5. Visualizing the results with the OMNeT++ IDE NEXT: 1. Getting started Generated on Thu Apr 14 2016 13:41:03 for Tictoc Tutorial by 1.8.11

Tic-Toc Tutorial...Tic-Toc Tutorial Contents TicToc Tutorial for OMNeT++ This short tutorial to OMNeT++ guides you through an example of modeling and simulation, showing you along

  • Upload
    others

  • View
    26

  • Download
    0

Embed Size (px)

Citation preview

Tic-Toc Tutorial

Contents

TicToc Tutorial for OMNeT++

This short tutorial to OMNeT++ guides you through an example of modeling and simulation, showing you

along the way some of the commonly used OMNeT++ features.

The tutorial is based on the Tictoc example simulation, which you can find in the samples/tictoc directory

of your OMNeT++ installation, so you can try out immediately how the examples work. However, you'll find

the tutorial much more useful if you actually carry out at least the first steps described here.

Note

We assume here that you have a working OMNeT++ installation. We also assume that you have a

good C++ knowledge, and you are in general familiar with C/C++ development (editing source files,

compiling, debugging etc.) on your operating system. (The latter two are out of our scope here – there

are excellent books, tutorials on the web, etc. if you need to update your knowledge on that.) We highly

recommend to use the OMNeT++ Integrated Development Environment for editing and building your

simulations.

To make the examples easier to follow, all source code in here is cross-linked to the OMNeT++ API

documentation.

This document and the TicToc model are an expanded version of the original TicToc tutorial from Ahmet

Sekercioglu (Monash University).

Contents

1. Getting started

2. Enhancing the 2-node TicToc

3. Turning it into a real network

4. Adding statistics collection

5. Visualizing the results with the OMNeT++ IDE

NEXT: 1. Getting started

Generated on Thu Apr 14 2016 13:41:03 for Tictoc Tutorial by 1.8.11

1. Getting started

UP: Contents

Since the most common application area of OMNeT++ is the simulation of telecommunications networks,

we'll borrow our topic from there. For a start, let us begin with a "network" that consists of two nodes. The

nodes will do something simple: one of the nodes will create a packet, and the two nodes will keep passing

the same packet back and forth. We'll call the nodes "tic" and "toc".

Here are the steps you take to implement your first simulation from scratch:

1. Create a working directory called tictoc, and cd to this directory.

2. Describe your example network by creating a topology file. Topology files are written in the NED language

(Network Definition). A NED topology file is a text file that identifies the network's nodes and the links

between them. You can create it with your favourite text editor.

Note

You can find a detailed description of the NED language in Section 3 of the OMNeT++ manual (it's also

in the doc directory of your OMNeT++ installation).

Let's call it tictoc1.ned:

simple Txc1{ gates: input in; output out;}

//// Two instances (tic and toc) of Txc1 connected both ways.// Tic and toc will pass messages to one another.//network Tictoc1{ submodules: tic: Txc1; toc: Txc1; connections: tic.out --> { delay = 100ms; } --> toc.in; tic.in <-- { delay = 100ms; } <-- toc.out;}

The file is best read from the bottom up. Here's what it says:

Tictoc1 is a network, which is assembled from two submodules, tic and toc. tic and toc are instances of

the same module type called Txc1. We connect tic's output gate (named out) to toc's input gate

(named in), and vica versa (network ... { ... }). There will be a 100ms propagation delay both

ways;

Txc1 is a simple module type (which means it is atomic on NED level, and will be implemented in

C++). Txc1 has one input gate named in, and one output gate named out (simple ... { ... }).

Note

You can find more information on Simple modules in Section 4 of the OMNeT++ manual.

3. We now need to implement the functionality of the simple module Txc1. This is achieved by writing a C++

file txc1.cc :

#include <string.h>#include <omnetpp.h>

using namespace omnetpp;

class Txc1 : public cSimpleModule{ protected: // The following redefined virtual function holds the algorithm. virtual void initialize() override; virtual void handleMessage(cMessage *msg) override;};

// The module class needs to be registered with OMNeT++Define_Module(Txc1);

void Txc1::initialize(){ // Initialize is called at the beginning of the simulation. // To bootstrap the tic-toc-tic-toc process, one of the modules needs // to send the first message. Let this be `tic'.

// Am I Tic or Toc? if (strcmp("tic", getName()) == 0) { // create and send first message on gate "out". "tictocMsg" is an // arbitrary string which will be the name of the message object. cMessage *msg = new cMessage("tictocMsg"); send(msg, "out"); }}

void Txc1::handleMessage(cMessage *msg){ // The handleMessage() method is called whenever a message arrives // at the module. Here, we just send it to the other module, through // gate `out'. Because both `tic' and `toc' does the same, the message // will bounce between the two. send(msg, "out"); // send out the message}

The Txc1 simple module type is represented by the C++ class Txc1, which has to be subclassed from

cSimpleModule, and registered in OMNeT++ with the Define_Module() macro.

We redefine two methods from cSimpleModule: initialize() and handleMessage(). They are invoked from the

simulation kernel: the first one only once, and the second one whenever a message arrives at the module.

In initialize() we create a message object (cMessage), and send it out on gate out. Since this gate is

connected to the other module's input gate, the simulation kernel will deliver this message to the other

module in the argument to handleMessage() – after a 100ms propagation delay assigned to the link in the

NED file. The other module just sends it back (another 100ms delay), so it will result in a continuous

ping-pong.

Messages (packets, frames, jobs, etc) and events (timers, timeouts) are all represented by cMessage objects

(or its subclasses) in OMNeT++. After you send or schedule them, they will be held by the simulation kernel

in the "scheduled events" or "future events" list until their time comes and they are delivered to the modules

via handleMessage().

Note that there is no stopping condition built into this simulation: it would continue forever. You will be able to

stop it from the GUI. (You could also specify a simulation time limit or CPU time limit in the configuration file,

but we don't do that in the tutorial.)

4. We now create the Makefile which will help us to compile and link our program to create the executable

tictoc:

$ opp_makemake

This command should have now created a Makefile in the working directory tictoc.

Note

Windows+MSVC users: the command is opp_nmakemake, and it will create Makefile.vc. Note:

MSVC is only supported in the commercial version OMNEST.

5. Let's now compile and link our very first simulation by issuing the make command:

$ make

Note

Windows+MSVC: type nmake -f Makefile.vc. If you get 'nmake' is not recognized as an internal

or external command..., find vcvars32.bat somewhere in the MSVC directories, and run it first in

every command window in which you want to compile. Note: MSVC is only supported in the

commercial version OMNEST.

If there are compilation errors, you need to rectify those and repeat the make until you get an error-free

compilation and linking.

Note

It might be more convenient to use the OMNeT++ IDE for the tutorial. Creating a new project, adding

files and building your project is fully automated in the IDE. There is no need to manually create the

Makefile either.

6. If you start the executable now, it will complain that it cannot find the file omnetpp.ini, so you have to create

one. omnetpp.ini tells the simulation program which network you want to simulate (yes, several networks can

live in the same simulation program), you can pass parameters to the model, explicitly specify seeds for the

random number generators etc.

Create the following very simple omnetpp.ini:

[General]network = Tictoc1

tictoc2 and further steps will all share the following omnetpp.ini:

# This file is shared by all tictoc simulations.# Lines beginning with `#' are comments

[General]# nothing here

[Config Tictoc1]network = Tictoc1

[Config Tictoc2]network = Tictoc2

[Config Tictoc3]network = Tictoc3

[Config Tictoc4]network = Tictoc4Tictoc4.toc.limit = 5

[Config Tictoc5]network = Tictoc5**.limit = 5

[Config Tictoc6]network = Tictoc6

[Config Tictoc7]network = Tictoc7# argument to exponential() is the mean; truncnormal() returns values from# the normal distribution truncated to nonnegative valuesTictoc7.tic.delayTime = exponential(3s)Tictoc7.toc.delayTime = truncnormal(3s,1s)

[Config Tictoc8]network = Tictoc8

[Config Tictoc9]network = Tictoc9

[Config Tictoc10]network = Tictoc10

[Config Tictoc11]network = Tictoc11

[Config Tictoc12]network = Tictoc12

[Config Tictoc13]network = Tictoc13

[Config Tictoc14]network = Tictoc14

[Config Tictoc15]network = Tictoc15record-eventlog = true

[Config Tictoc16]network = Tictoc16**.tic[1].hopCount.result-recording-modes = +histogram**.tic[0..2].hopCount.result-recording-modes = -vector

7. Once you complete the above steps, you launch the simulation by issuing this command:

$ ./tictoc

and hopefully you should now get the OMNeT++ simulation window.

Note

To run the simulation from the IDE, just right click on omnetpp.ini and select Run as, then OMNeT++

Simulation.

8. Press the Run button on the toolbar to start the simulation. What you should see is that tic and toc are

exchanging messages with each other.

The main window toolbar displays the simulated time. This is virtual time, it has nothing to do with the actual

(or wall-clock) time that the program takes to execute. Actually, how many seconds you can simulate in one

real-world second depends highly on the speed of your hardware and even more on the nature and

complexity of the simulation model itself.

Note that it takes zero simulation time for a node to process the message. The only thing that makes the

simulation time pass in this model is the propagation delay on the connections.

9. You can play with slowing down the animation or making it faster with the slider at the top of the graphics

window. You can stop the simulation by hitting F8 (equivalent to the STOP button on the toolbar), single-step

through it (F4), run it with (F5) or without (F6) animation. F7 (express mode) completely turns off tracing

features for maximum speed. Note the event/sec and simsec/sec gauges on the status bar of the main

window (only visible when the simulation is running in fast or express mode).

10. You can exit the simulation program by clicking its Close icon or choosing File|Exit.

Note

There are some typical mistakes that people who are new to OMNeT++ make:

- By default, the module type name must be the same in both the .ned and the .cc files. It is a common

mistake to either forget the Define_Module() macro, or mistype the module name so it is not the same

in the .ned and .cc files. These mistakes yield the following errors, respectively:

Error in module (omnetpp::cModule) Tictoc1 (id=1) during network setup: Class "Txc1" not found –

perhaps its code was not linked in, or the class wasn't registered with Register_Class(), or in the case

of modules and channels, with Define_Module()/Define_Channel().

or

Error in module (omnetpp::cModule) TicToc1 (id=1) during network setup: Submodule tic: cannot

resolve module type 'Txc1' (not in the loaded NED files?), at /omnetpp-5.0/samples/tictoc

/tictoc1.ned:26.

- Also, you must run opp_makemake and make after adding a .cc file to the project. Failing to do so

will be indicated by the first error message above. If you use the OMNeT++ IDE, this is done

automatically.

Sources: tictoc1.ned, txc1.cc, omnetpp.ini

NEXT: 2. Enhancing the 2-node TicToc

Generated on Thu Apr 14 2016 13:41:03 for Tictoc Tutorial by 1.8.11

2. Enhancing the 2-node TicToc

PREV: 1. Getting started UP: Contents

Step 2: Refining the graphics, and addingdebugging output

Here we make the model look a bit prettier in the GUI. We assign the "block/routing" icon (the file

images/block/routing.png), 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.

// Here we make the model look a bit prettier in the GUI. We assign the// "block/routing" icon to the simple module. All submodules of type// Txc2 will use this icon by default//simple Txc2{ parameters: @display("i=block/routing"); // add a default icon gates: input in; output out;}

//// Make the two module look a bit different with colorization effect.// Use cyan for `tic', and yellow for `toc'.//network Tictoc2{ submodules: tic: Txc2 { parameters: @display("i=,cyan"); // do not change the icon (first arg of i=)

just colorize it } toc: Txc2 { parameters: @display("i=,gold"); // here too } connections: tic.out --> { delay = 100ms; } --> toc.in; tic.in <-- { delay = 100ms; } <-- toc.out;}

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->getName() << "', sending it out again\n";

When you run the simulation in the OMNeT++ runtime environment, the following output will appear in the log

window:

You can also open separate output windows for tic and toc by right-clicking on their icons and choosing

Component log 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

Step 3: Adding state variables

In this step we add a counter to the module, and delete the message after ten exchanges.

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 the graphical runtime environment.

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

Step 4: Adding parameters

In this step you'll learn how to add input parameters to the simulation: we'll turn the "magic number" 10 into a

parameter and add a boolean parameter to decide whether the module should send out the first message in

its initialization code (whether this is a tic or a toc module).

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: bool sendMsgOnInit = default(false); // whether the module should send out

a message on initialization int limit = default(2); // another parameter with a default value @display("i=block/routing"); gates:

We also have to modify the C++ code to read the parameter in initialize(), and assign it to the counter.

counter = par("limit");

We can use the second parameter to decide whether to send initial message:

if (par("sendMsgOnInit").boolValue() == true) {

Now, we can assign the parameters in the NED file or from omnetpp.ini. Assignments in the NED file take

precedence. You can define default values for parameters if you use the default(...) syntax in the NED

file. In this case you can either set the value of the parameter in omnetpp.ini or use the default value provided

by the NED file.

Here, we assign one parameter in the NED file:

network Tictoc4{ submodules: tic: Txc4 { parameters: sendMsgOnInit = true; @display("i=,cyan"); } toc: Txc4 { parameters: sendMsgOnInit = false; @display("i=,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 the graphical runtime environment, 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

Step 5: Using inheritance

If we take a closer look at the NED file we will realize that tic and toc differs only in their parameter values

and their display string. We can create a new simple module type by inheriting from an other one and

specifying or overriding some of its parameters. In our case we will derive two simple module types (Tic and

Toc). Later we can use these types when defining the submodules in the network.

Deriving from an existing simple module is easy. Here is the base module:

simple Txc5{ parameters: bool sendMsgOnInit = default(false); int limit = default(2); @display("i=block/routing"); gates: input in; output out;}

And here is the derived module. We just simply specify the parameter values and add some display

properties.

simple Tic5 extends Txc5{ parameters: @display("i=,cyan"); sendMsgOnInit = true; // Tic modules should send a message on init}

The Toc module looks similar, but with different parameter values.

simple Toc5 extends Txc5{ parameters: @display("i=,gold"); sendMsgOnInit = false; // Toc modules should NOT send a message on init}

Note

The C++ implementation is inherited from the base simple module (Txc4).

Once we created the new simple modules, we can use them as submodule types in our network:

network Tictoc5{ submodules: tic: Tic5; // the limit parameter is still unbound here. We will get it

from the ini file toc: Toc5; connections:

As you can see, the network definition is much shorter and simpler now. Inheritance allows you to use

common types in your network and avoid redundant definitions and parameter settings.

Step 6: Modeling processing delay

In the previous models, tic and toc immediately sent back the received message. Here we'll add some timing:

tic and toc will hold the message for 1 simulated second before sending it back. In OMNeT++ such timing is

achieved by the module sending a message to itself. Such messages are called self-messages (but only

because of the way they are used, otherwise they are ordinary message objects).

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 Txc6 : 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.

While running the simulation you will see the following log output:

Sources: tictoc6.ned, txc6.cc, omnetpp.ini

Step 7: Random numbers and parameters

In this step we'll introduce random numbers. We change the delay from 1s to a random value which can be

set from the NED file or from omnetpp.ini. Module parameters are able to return random variables; however,

to make use of this feature we have to read the parameter in handleMessage() every time we use it.

// The "delayTime" module parameter can be set to values like // "exponential(5)" (tictoc7.ned, omnetpp.ini), and then here // we'll get a different delay every time. simtime_t 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:

Tictoc7.tic.delayTime = exponential(3s)Tictoc7.toc.delayTime = truncnormal(3s,1s)

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: tictoc8.ned, txc7.cc, omnetpp.ini

Step 8: Timeout, cancelling timers

In order to get one step closer to modelling networking protocols, let us transform our model into a

stop-and-wait simulation. This time we'll have separate classes for tic and toc. The basic scenario is similar to

the previous ones: tic and toc will be tossing a message to one another. However, toc will "lose" the message

with some nonzero probability, and in that case tic will have to resend it.

Here's toc's code:

void Toc8::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 txc8.cc.

Sources: tictoc8.ned, txc8.cc, omnetpp.ini

Step 9: Retransmitting the same message

In this step we refine the previous model. There we just created another packet if we needed to retransmit.

This is OK because the packet didn't contain much, but in real life it's usually more practical to keep a copy of

the original packet so that we can re-send it without the need to build it again. Keeping a pointer to the sent

message - so we can send it again - might seem easier, but when the message is destroyed at the other

node the pointer becomes invalid.

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 *Tic9::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 Tic9::sendCopyOf(cMessage *msg){ // Duplicate message and send the copy. cMessage *copy = (cMessage *)msg->dup(); send(copy, "out");}

Sources: tictoc9.ned, txc9.cc, omnetpp.ini

NEXT: 3. Turning it into a real network

Generated on Thu Apr 14 2016 13:41:03 for Tictoc Tutorial by 1.8.11

3. Turning it into a real network

PREV: 2. Enhancing the 2-node TicToc UP: Contents

Step 10: More than two nodes

Now we'll make a big step: create several tic modules and connect them into a network. For now, we'll keep it

simple what they do: one of the nodes generates a message, and the others keep tossing it around in

random directions until it arrives at a predetermined destination node.

The NED file will need a few changes. First of all, the Txc module will need to have multiple input and output

gates:

simple Txc10{ parameters: @display("i=block/routing"); gates: input in[]; // declare in[] and out[] to be vector gates output out[];}

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.

network Tictoc10{ submodules: tic[6]: Txc10; 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++;}

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 getIndex() 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, and sends out message on that gate.

void Txc10::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 = gateSize("out"); 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 txc10.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::getArrivalGate(), cGate::getIndex().

Note that if the message didn't arrive via a gate but was a self-message, then getArrivalGate() returns NULL.

Sources: tictoc10.ned, txc10.cc, omnetpp.ini

Step 11: Channels and inner type definitions

Our new network definition is getting quite complex and long, especially the connections section. Let's try to

simplify it. The first thing we notice is that the connections always use the same delay parameter. It is

possible to create types for the connections (they are called channels) similarly to simple modules. We

should create a channel type which specifies the delay parameter and we will use that type for all

connections in the network.

network Tictoc11{ types: channel Channel extends ned.DelayChannel { delay = 100ms; } submodules:

As you have noticed we have defined the new channel type inside the network definition by adding a types

section. This type definition is only visible inside the network. It is called as a local or inner type. You can use

simple modules as inner types too, if you wish.

Note

We have created the channel by specializing the built-in DelayChannel. (built-in channels can be found

inside the ned package. Thats why we used the full type name ned.DelayChannel) after the

extends keyword.

Now let's check how the connections section changed.

connections: tic[0].out++ --> Channel --> tic[1].in++; tic[0].in++ <-- Channel <-- tic[1].out++;

tic[1].out++ --> Channel --> tic[2].in++; tic[1].in++ <-- Channel <-- tic[2].out++;

tic[1].out++ --> Channel --> tic[4].in++; tic[1].in++ <-- Channel <-- tic[4].out++;

tic[3].out++ --> Channel --> tic[4].in++; tic[3].in++ <-- Channel <-- tic[4].out++;

tic[4].out++ --> Channel --> tic[5].in++; tic[4].in++ <-- Channel <-- tic[5].out++;}

As you see we just specify the channel name inside the connection definition. This allows to easily change

the delay parameter for the whole network.

Sources: tictoc11.ned, txc11.cc, omnetpp.ini

Step 12: Using two-way connections

If we check the connections section a little more, we will realize that each node pair is connected with two

connections. One for each direction. OMNeT++ 4 supports two way connections, so let's use them.

First of all, we have to define two-way (or so called inout) gates instead of the separate input and output

gates we used previously.

simple Txc12{ parameters: @display("i=block/routing"); gates: inout gate[]; // declare two way connections}

The new connections section would look like this:

connections: tic[0].gate++ <--> Channel <--> tic[1].gate++; tic[1].gate++ <--> Channel <--> tic[2].gate++; tic[1].gate++ <--> Channel <--> tic[4].gate++; tic[3].gate++ <--> Channel <--> tic[4].gate++; tic[4].gate++ <--> Channel <--> tic[5].gate++;}

We have modified the gate names so we have to make some modifications to the C++ code.

void Txc12::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 `gate[]'. int n = gateSize("gate"); int k = intuniform(0, n-1);

EV << "Forwarding message " << msg << " on gate[" << k << "]\n"; // $o and $i suffix is used to identify the input/output part of a two way gate send(msg, "gate$o", k);}

Note

The special $i and $o suffix after the gate name allows us to use the connection's two direction

separately.

Sources: tictoc12.ned, txc12.cc, omnetpp.ini

Step 13: Defining our message class

In this step the destination address is no longer hardcoded tic[3] – we draw a random destination, and we'll

add the destination address to the message.

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 tictoc13.msg:

message TicTocMsg13{ int source; int destination; int hopCount = 0;}

Note

See Section 6 of the OMNeT++ manual for more details on messages.

The makefile is set up so that the message compiler, opp_msgc is invoked and it generates tictoc13_m.h and

tictoc13_m.cc from the message declaration (The file names are generated from the tictoc13.msg file name,

not the message type name). They will contain a generated TicTocMsg13 class subclassed from cMessage;

the class will have getter and setter methods for every field.

We'll include tictoc13_m.h into our C++ code, and we can use TicTocMsg13 as any other class.

#include "tictoc13_m.h"

For example, we use the following lines in generateMessage() to create the message and fill its fields.

TicTocMsg13 *msg = new TicTocMsg13(msgname); msg->setSource(src);

msg->setDestination(dest); return msg;

Then, handleMessage() begins like this:

void Txc13::handleMessage(cMessage *msg){ TicTocMsg13 *ttmsg = check_and_cast<TicTocMsg13 *>(msg);

if (ttmsg->getDestination() == getIndex()) {

In the argument to handleMessage(), we get the message as a cMessage * pointer. However, we can only

access its fields defined in TicTocMsg13 if we cast msg to TicTocMsg13 *. Plain C-style cast ((TicTocMsg13

*)msg) is not safe because if the message is not a TicTocMsg13 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 getIndex()

member function returns the index of the module in the submodule vector (remember, in the NED file we

declarared it as tic: Txc13[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: txc13.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: tictoc13.ned, tictoc13.msg, txc13.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

Generated on Thu Apr 14 2016 13:41:03 for Tictoc Tutorial by 1.8.11

4. Adding statistics collection

PREV: 3. Turning it into a real network UP: Contents

Step 14: Displaying the number of packetssent/received

To get an overview at runtime how many messages each node sent or received, we've added two counters to

the module class: numSent and numReceived.

class Txc14 : public cSimpleModule{ private: long numSent; long numReceived;

protected:

They are set to zero and WATCH'ed in the initialize() method. Now we can use the Find/inspect objects

dialog (Inspect menu; it is also on the toolbar) to learn how many packets were sent or received by the

various nodes.

It's true that in this concrete simulation model the numbers will be roughly the same, so you can only learn

from them that intuniform() works properly. But in real-life simulations it can be very useful that you can

quickly get an overview about the state of various nodes in the model.

It can be also arranged that this info appears above the module icons. The t= display string tag specifies the

text; we only need to modify the displays string during runtime. The following code does the job:

void Txc14::refreshDisplay() const{ char buf[40]; sprintf(buf, "rcvd: %ld sent: %ld", numReceived, numSent); getDisplayString().setTagArg("t", 0, buf);}

And the result looks like this:

Sources: tictoc14.ned, tictoc14.msg, txc14.cc, omnetpp.ini

Step 15: Adding statistics collection

The OMNeT++ simulation kernel can record a detailed log about your message exchanges automatically by

setting the

record-eventlog = true

configuration option in the omnetpp.ini file. This log file can be later displayed by the IDE (see: Sequence

charts end event logs).

Note

The resulting log file can be quite large, so enable this feature only if you really need it.

The previous simulation model does something interesting enough so that we can collect some statistics. For

example, you may be interested in the average hop count a message has to travel before reaching its

destination.

We'll record in the hop count of every message upon arrival into an output vector (a sequence of (time,value)

pairs, sort of a time series). We also calculate mean, standard deviation, minimum, maximum values per

node, and write them into a file at the end of the simulation. Then we'll use tools from the OMNeT++ IDE to

analyse the output files.

For that, we add an output vector object (which will record the data into Tictoc15-0.vec) and a histogram

object (which also calculates mean, etc) to the class.

class Txc15 : public cSimpleModule{ private: long numSent; long numReceived; cLongHistogram hopCountStats; cOutVector hopCountVector;

protected:

When a message arrives at the destination node, we update the statistics. The following code has been

added to handleMessage():

hopCountVector.record(hopcount); hopCountStats.collect(hopcount);

hopCountVector.record() call writes the data into Tictoc15-0.vec. With a large simulation model or long

execution time, the Tictoc15-0.vec file may grow very large. To handle this situation, you can specifically

disable/enable vector in omnetpp.ini, and you can also specify a simulation time interval in which you're

interested (data recorded outside this interval will be discarded.)

When you begin a new simulation, the existing Tictoc15-0.vec/sca file gets deleted.

Scalar data (collected by the histogram object in this simulation) have to be recorded manually, in the finish()

function. finish() gets invoked on successful completion of the simulation, i.e. not when it's stopped with an

error. The recordScalar() calls in the code below write into the Tictoc15-0.sca file.

void Txc15::finish(){ // This function is called by OMNeT++ at the end of the simulation. EV << "Sent: " << numSent << endl; EV << "Received: " << numReceived << endl; EV << "Hop count, min: " << hopCountStats.getMin() << endl; EV << "Hop count, max: " << hopCountStats.getMax() << endl; EV << "Hop count, mean: " << hopCountStats.getMean() << endl; EV << "Hop count, stddev: " << hopCountStats.getStddev() << endl;

recordScalar("#sent", numSent); recordScalar("#received", numReceived);

hopCountStats.recordAs("hop count");}

The files are stored in the results/ subdirectory.

You can also view the data during simulation. To do that, right click on a module, and choose Open

Details. In the module inspector's Contents page you'll find the hopCountStats and hopCountVector

objects. To open their inspectors, right click on cLongHistogram hopCountStats or cOutVector

HopCount, and click Open Graphical View.

The inspector:

They will be initially empty – run the simulation in Fast (or even Express) mode to get enough data to be

displayed. After a while you'll get something like this:

When you think enough data has been collected, you can stop the simulation and then we'll analyse the

result files (Tictoc15-0.vec and Tictoc15-0.sca) off-line. You'll need to choose Simulate|Call finish()

from the menu (or click the corresponding toolbar button) before exiting – this will cause the finish() functions

to run and data to be written into Tictoc15-0.sca.

Sources: tictoc15.ned, tictoc15.msg, txc15.cc, omnetpp.ini

Step 16: Statistic collection without modifying

your model

In the previous step we have added statistic collection to our model. While we can compute and save any

value we wish, usually it is not known at the time of writing the model, what data the enduser will need.

OMNeT++ 4.1 provides an additional mechanism to record values and events. Any model can emit 'signals'

that can carry a value or an object. The model writer just have to decide what signals to emit, what data to

attach to them and when to emit them. The enduser can attach 'listeners' to these signals that can process or

record these data items. This way the model code does not have to contain any code that is specific to the

statistics collection and the enduser can freely add additional statistics without even looking into the C++

code.

We will re-write the statistic collection introduced in the last step to use signals. First of all, we can safely

remove all statistic related variables from our module. There is no need for the cOutVector and

cLongHistogram classes either. We will need only a single signal that carries the hopCount of the

message at the time of message arrival at the destination.

First we need to define our signal. The arrivalSignal is just an identifier that can be used later to easily

refer to our signal.

class Txc16 : public cSimpleModule{ private: simsignal_t arrivalSignal;

protected:

We must register all signals before using them. The best place to do this is the initialize() method of

the module.

void Txc16::initialize(){ arrivalSignal = registerSignal("arrival"); // Module 0 sends the first message if (getIndex() == 0) {

Now we can emit our signal, when the message has arrived to the destination node.

void Txc16::handleMessage(cMessage *msg){ TicTocMsg16 *ttmsg = check_and_cast<TicTocMsg16 *>(msg);

if (ttmsg->getDestination() == getIndex()) { // Message arrived int hopcount = ttmsg->getHopCount(); // send a signal emit(arrivalSignal, hopcount);

EV << "Message " << ttmsg << " arrived after " << hopcount << " hops.\n";

As we do not have to save or store anything manually, the finish() method can be deleted. We no longer

need it.

The last step is that we have to define the emitted signal also in the NED file. Declaring signals in the NED

file allows you to have all information about your module in one place. You will see the parameters it takes, its

input and output gates, and also the signals and statistics it provides.

simple Txc16

{ parameters: @signal[arrival](type="long"); @statistic[hopCount](title="hop count"; source="arrival";

record=vector,stats; interpolationmode=none);

@display("i=block/routing");

Now we can define also a statistic that should be collected by default. Our previous example has collected

statistics (max,min,mean,count etc) about the hop count of the arriving messages, so let's collect the same

data here, too.

The source key specifies the signal we want our statistic to attach to. The record key can be used to tell

what should be done with the received data. In our case we sepcify that each value must be saved in a

vector file (vector) and also we need to calculate min,max,mean,count etc. (stats). (NOTE: stats is just a

shorthand for min, max, mean, sum, count etc.) With this step we have finished our model.

Now we have just realized that we would like to see a histogram of the hopCount on the tic[1] module. On the

other hand we are short on disk storage and we are not interested having the vector data for the first three

module tic 0,1,2. No problem. We can add our histogram and remove the unneeded vector recording without

even touching the C++ or NED files. Just open the INI file and modify the statistic recording:

[Config Tictoc16]network = Tictoc16**.tic[1].hopCount.result-recording-modes = +histogram**.tic[0..2].hopCount.result-recording-modes = -vector

We can configure a wide range of statistics without even looking into the C++ code, provided that the original

model emits the necessary signals for us.

Now that we have collected our statistics, let's see and analyse them in the IDE.

Sources: tictoc16.ned, tictoc16.msg, txc16.cc, omnetpp.ini

NEXT: 5. Visualizing the results with the OMNeT++ IDE

Generated on Thu Apr 14 2016 13:41:03 for Tictoc Tutorial by 1.8.11

5. Visualizing the results with the OMNeT++ IDE

PREV: 4. Adding statistics collection UP: Contents

Visualizing output scalars and vectors

The OMNeT++ IDE can help you to analyze your results. It supports filtering, processing and displaying

vector and scalar data, and can display histograms, too. The following diagrams have been created with the

Result Analysis tool of the IDE.

The results directory in the project folder contains .vec and .sca files, which are the files that store the

results in vector and scalar form, respectively. Vectors record data values as a function of time, while scalars

typically record aggregate values at the end of the simulation. To open the Result Analysis tool, double click

on either the .vec or the .sca files in the OMNeT++ IDE. Both files will be loaded by the Result Analysis tool.

You can find the Browse Data tab at the bottom of the Result Analysis tool panel. Here you can browse

results by type by switching the various tabs at the top of the tool panel, ie. Scalars, Vectors, or Histograms.

By default, all results of a result type are displayed. You can filter them by the module filter to view all or some

of the individual modules, or the statistic name filter to display different types of statistics, ie. mean, max, min,

standard deviation, etc. You can select some or all of the individual results by highlighting them. If you select

multiple results, they will be plotted on one chart. Right click and select Plot to display the figures.

Note

For further information about the charting and processing capabilities, please refer to the OMNeT++

Users Guide (you can find it in the doc directory of the OMNeT++ installation).

Our last model records the hopCount of a message each time the message reaches its destination. The

following plot shows these vectors for nodes 0 and 1.

If we apply a mean operation we can see how the hopCount in the different nodes converge to an average:

The next chart displays the mean and the maximum of the hopCount of the messages for each destination

node, based on the scalar data recorded at the end of the simulation.

The following diagram shows the histogram of hopCount's distribution.

Sequence charts end event logs

The OMNeT++ simulation kernel can record the message exchanges during the simulation into an event log

file. To enable recording the event log, you must specify record-eventlog = true in omnetpp.ini. Alternatively,

you can click on the Record button in the graphical runtime environment. This log file can be analyzed later

with the Sequence Chart tool. The results directory in the project folder contains the .elog file. Double

clicking on this in the OMNeT++ IDE opens the Sequence Chart tool, and the event log tab at the bottom of

the screen.

The following figure has been created with the Sequence Chart tool, and shows how the message is routed

between the different nodes in the network. In this instance the chart is very simple, but when you have a

complex model, sequence charts can be very valuable in debugging, exploring or documenting the model's

behaviour.

Conclusion

Hope you have found this tutorial a useful introduction into OMNeT++. Comments and suggestions will be

appreciated.

UP: Contents

Generated on Thu Apr 14 2016 13:41:03 for Tictoc Tutorial by 1.8.11