Skip to main content

Exploring the code

Let's explore the code you just imported. When you expand all of the folders, you will notice two folders with files inside: SRC and SIMULATIONS. Following the modular architecture of OMNeT++, all of the network descriptions (SingleNodeNetwork.ned), parameter settings (omnetpp.ini), etc. are in the simulations folder. All of the simple modules with their descriptions and implementation are under the src folder. In our case, Node.h and Node.cc are the implementation files of the module Node and Node.ned is the NED definition of this module.

OMNeT++ uses the NED language to describe the individual modules, including their connections to other modules and their parameters. In this way, you separate the abstract network structure definition from the actual behavior implementation and the kernel can follow the NED instructions to construct your nodes and the whole network. No need to patch the kernel or to re-compile the complete simulator only because you added a few lines to your nodes' behavior...

The whole simulation process in OMNeT++ is roughly organized into four steps:
OMNET.png
Let us have a closer look at each individual file:

Node.ned

package singlenodetutorial;
simple Node
{
    parameters:
        @display("p=100,100;b=60,10,rect,blue,black,2");
}

Note: You might need to switch to "source" in the IDE to view this code.

This file pretty much tells OMNeT++ to create a simple module with the name Node, which has only one parameter defined to set up the display of this module in the simulation view. That line starting with @display is pretty cryptic, but we don't need to fully understand it. At closer inspection, you will figure out that it says something like: it is a rectangle, positioned at 100,100 with a size of 60x10, the inner part is blue, the outer line is black and the outer line is 2 px thick. This explains also how our node looked like when we started the simulation earlier.

Later on, we will define many more things here: gates to connect to other nodes, further parameters, and, for compound modules, what other modules are included and how. For now, we are happy to play with the colours.

Node.h
#ifndef __SINGLENODETUTORIAL_NODE_H_
#define __SINGLENODETUTORIAL_NODE_H_
#include <omnetpp.h>
using namespace omnetpp;
class Node : public cSimpleModule
{
  protected:
    virtual void initialize();
    virtual void handleMessage(cMessage *msg);
};
#endif
This is the header of the module Node. Note three things:


  1. The header defines the usage of the namespace omnetpp - without it, you cannot use any kernel functions.
  2. The module Node inherits directly from the Class cSimpleModule. This is very important.
  3. There are two virtual functions you need to overwrite: initialize() and handleMessage(). The first one is executed before the simulation starts. The other is called during simulation every time an event for this module (Node) has to be executed.
This is usually the place where non-proficient C++ users start sweating. If terms like inheritance and virtual functions sound more like Klingon than any human language, it is time to refresh your OOP. Here is a good tutorial.

Node.cc
Last but not least, the implementation if those two functions (any much more) can be found in Node.cc.

#include "Node.h"
Define_Module(Node);
void Node::initialize()
{
    // create a simple message and send it to ourselves at simulation time 10.
    cMessage *msg = new cMessage("changeColorToRed");
    scheduleAt(10, msg);
}
void Node::handleMessage(cMessage *msg)
{
    // In this scenario, we can only receive one possible message - the one
    // we created in initialize()

    // when we receive it, simply change the color of the node from blue to red
    cDisplayString& dispStr = getDisplayString();
    dispStr.parse("p=100,100;b=60,10,rect,red,black,2");
    delete msg;
}

Note again three things:
  1. Define_module(Node) makes it possible for OMNeT++ to find your module. Without this line, your simulation will say "No module definition found".
  2. Again, the initialize() function. It can be empty, but at least one simple module in your network (we have only one...) must schedule at least one event to kick-start the simulation. You remember, no events, no simulation.
  3. The handleMessage() function is called by the kernel when a message arrives at this module. It is a little bit like your mailbox - you receive a message and need to do something about it.
The behavior of our module is simple. At initialization time, it schedules a self message (an instance of the class cMessage) with the name changeColorToRed for simulation time 10. How do I know it is a self-message? I use the function scheduleAt, which does not take an address - thus, it is me, the same node who scheduled the event will also receive it.

When the message is received, handleMessage will be called with exactly that message as a parameter. Since we only have this one single message, we know what to do: change the color to red! This is done again with an equally cryptic line like before.

Important: once you are finished with that message, delete it (you created it in the initialization method, you must delete it too - no garbage collection).

That's all! Next, we will play a little bit with the code.