进入官网下载界面,链接如下:OMNeT++官网,选择合适的操作系统和版本下载。(以windows为例)。
下载后将其解压至指定文件夹,双击文件mingwenv.cmd,按照其实操作即可完成按照。(待其出现输入命令行的提示后,先后输入./configure和make指令,时间花费比较长,需要耐心等待)
在完成上述步骤后,在ide中点开界面就可打开OMNet++界面。
在进入界面时,需要新建文件夹指定工作空间,(操作与eclipse相同),系统默认的空间为simple。
在打开OMNeT++图像界面以后,就可以开始新建自己的项目了。
在菜单栏中一次点击File->New->OMNeT++Project,出现如下界面:
输入项目名称以后,点击Next,选择Empty Project即可完成。项目的存储空间就是在打开OMNeT++时选定的工作空间。
需要完整地运行一个OMNeT++仿真程序,需要有.ned、.cc、.ini三个文件,分别表示网络的拓扑结构、运行程序(C++)和网络的运行入口。
对于初学者,可以先在安装OMNeT的simple目录下对实例进行学习,下面的部分以tictoc的18个简单实例学习为主。
1.tictoc1
这是刚刚接触OMNeT++的第一个实例,前面提到运行一个完整的OMNeT程序需要.ned、.cc、.ini三个文件。在此之前,先打开tictoc1.ned、txc1.cc、omnetpp.ini。
在tictoc1.ned中,可以看到如下图所示的拓扑结构:(在网络Tictoc1中,节点tic和toc的位置可以随意拖动)
在点击source后可以看到用NED语言编写的代码,如下:
simple Txc1 { @display("i=block/routing"); //位置 gates: input in; //输入门 output out; //输出门 } network Tictoc1 //网络结构 { @display("bgb=383,273"); //颜色 submodules: tic: Txc1 { //tic节点 @display("p=70,50"); } toc: Txc1 { //toc节点 @display("p=210,50"); } connections: tic.out --> { delay = 100ms; } --> toc.in; //网络的连接 tic.in <-- { delay = 100ms; } <-- toc.out; }
代码中,simple定义了一个网络节点类型,该节点包括一个输入门in和一个输出门ou。 随后定义了名为Tictoc1的网络,网络包括两个节点tic与toc,节点的类型都是Txc1,然后connections部分定义了两个节点之间的连接:tic的out门连接到toc的in门,传输延迟为100ms;tic的in门连接到toc的out门,传输延迟是100ms。
在txc1.cc中,定义了网络之间运行的信息传输以及消息处理方法,代码如下:
#include
#include using namespace omnetpp; class Txc1 : public cSimpleModule //定义类,名字为网络节点,代表这类节点的信息处理方式 { protected: virtual void initialize() override; //初始化函数 virtual void handleMessage(cMessage *msg) override; //在收到消息后的处理方式 }; Define_Module(Txc1); void Txc1::initialize() { if (strcmp("tic", getName()) == 0) { cMessage *msg = new cMessage("tictocMsg"); //新建一个消息 send(msg, "out"); //消息从out门发出 } } void Txc1::handleMessage(cMessage *msg) { send(msg, "out"); // 在收到消息后,直接从out门输出 } 在代码中已经有较为详细的注释,由于.cc文件是基于C++编写,using namespace是使用名字空间、strcmp是C语言中的字符串比较函数,若两个字符串相等,则输出0。其中initialize和handleMessage两个函数分别是初始化和消息处理。Define_Module()是对类和节点进行注册,这个步骤必不可少!
在.ini文件中,需要在Network中添加网络(运行tictoc时不需要再添加),添加后代码中会出现如下结果:
(更复杂的网络可以从此注入参数,今后再说)
在完成上述三个步骤以后,直接运行.ned文件即可进入仿真。进入仿真的界面如下:
点击run即可运行。若要单步运行,可以点击左侧的STEP;若要快速运行,可以点击FAST。
2.tictoc2
tictoc2跟tictoc1对比,只是多了如下代码:
EV << "Sending initial message\n";
EV类似于C++总的cout,用于输出。
tictoc2在运行时,能看见输出的信息。如下所示:
3.tictoc3
跟tictoc2相比,在定义类的部分新增了count属性,用于控制消息传递的次数。消息每转发一次,count值减少1,当减少到0时即删除消息,由于无消息此时程序运行结束。在处理消息的函数中,代码如下:
void Txc3::handleMessage(cMessage *msg) { // Increment counter and check value. counter--; if (counter == 0) { // If counter is zero, delete message. If you run the model, you'll // find that the simulation will stop at this point with the message // "no more events". EV << getName() << "'s counter reached zero, deleting message\n"; delete msg; } else { EV << getName() << "'s counter is " << counter << ", sending back message\n"; send(msg, "out"); } }
4.tictoc4
跟tictoc3比起来,在网络节点定义时,增加了两个参数,代码如下:
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: input in; output out; }
其中sendMsgOnInit用于表征是否在初始化时发送消息,limit用于限制消息发送的次数,在.cc文件中直接将limit的值赋予count。
在.ini文件中,注册的信息如下:
直接对toc节点的limit值赋予5。
5.tictoc5
跟tictoc4相比,tictoc5在定义网络节点类型时,使用了继承,将toc和tic定义为了不同的节点类型。如下:
simple Txc5 { parameters: bool sendMsgOnInit = default(false); int limit = default(2); @display("i=block/routing"); gates: input in; output out; } simple Tic5 extends Txc5 { parameters: @display("i=,cyan"); sendMsgOnInit = true; // Tic modules should send a message on init }
具有继承关系的节点类型,在使用Define_Module()进行注册时,只需要其父类即可。
6.tictoc6
tictoc6跟前面的五个例子相比,在编写.cc文件时,加入了网络节点类型的参数;引入了构造函数和析构函数;并且加入了自发送消息模型。
类的定义代码如下:
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: Txc6(); //构造函数 virtual ~Txc6(); //析构函数 protected: virtual void initialize() override; virtual void handleMessage(cMessage *msg) override; };
新增加的event和tictocMeg属性分别用于自发送消息和发送消息。构造函数与析构函数的使用与C++的使用方法类似。
在文件中,还加入了自发送消息,其目的是监测消息发送是否超时,代码如下:
if (strcmp("tic", getName()) == 0) { EV << "Scheduling first send to t=5.0s\n"; tictocMsg = new cMessage("tictocMsg"); scheduleAt(5.0, event); //发送自传消息 }
在initialize函数中,tic节点在5.0s时对自己发送一条消息,消息的内容是event。在处理消息函数handleMessage中,若收到的消息是event时,处理的代码如下:
if (msg == event) { EV << "Wait period is over, sending back message\n"; send(tictocMsg, "out"); tictocMsg = nullptr; }
即:再次将tictocMsg中的内容发出。
7.tictoc7
tictoc6跟前面的例子相比,在编写.ned文件时,为网络节点类型增加了delay参数,用于表证从接收到消息到消息转发的延时。而delay具体数值通过.ini产生,方式包括平均分布和正态分布。在编写.cc文件时,考虑了0.1的概率丢包的情况。
.ini文件中,产生delay值的代码如下:
[Config Tictoc7] network = Tictoc7 # argument to exponential() is the mean; truncnormal() returns values from # the normal distribution truncated to nonnegative values Tictoc7.tic.delayTime = exponential(3s) Tictoc7.toc.delayTime = truncnormal(3s,1s)
.cc文件中,handleMessage函数代码如下:
void Txc7::handleMessage(cMessage *msg) { if (msg == event) { EV << "Wait period is over, sending back message\n"; send(tictocMsg, "out"); tictocMsg = nullptr; } else { if (uniform(0, 1) < 0.1) { //有0.1的概率发送丢包 EV << "\"Losing\" message\n"; delete msg; } else { simtime_t delay = par("delayTime"); EV << "Message arrived, starting to wait " << delay << " secs...\n"; tictocMsg = msg; scheduleAt(simTime()+delay, event); } } }
在收到消息后,先判断是否为自发消息enent,若是再进行消息tictocMsg发送(通过这个方式实现接受到发送的延时控制);若不是则以0.1的概率进行模拟丢包,若没有丢包则在延时后进行自发送消息,以触发msg==enent进行tictocmsg发送。
8.tictoc8
tictoc8先是分别定义了tic和toc网络节点类型,因此在.cc文件中对其进行分别定义和注册。 然后对tic节点结合自发送消息实现了定时重播的功能。在初始阶段发出消息的同时在1s后给自己发送一个消息,若在这之前收到过消息则取消自发送并再发送一个自发送消息;若没有收到则在1s后会收到自发送消息,此时再进行消息发送和自发送消息,达到超时重传的目的。代码如下:
void Tic8::handleMessage(cMessage *msg) { if (msg == timeoutEvent) { EV << "Timeout expired, resending message and restarting timer\n"; cMessage *newMsg = new cMessage("tictocMsg"); send(newMsg, "out"); scheduleAt(simTime()+timeout, timeoutEvent); } else { EV << "Timer cancelled.\n"; cancelEvent(timeoutEvent); delete msg; cMessage *newMsg = new cMessage("tictocMsg"); send(newMsg, "out"); scheduleAt(simTime()+timeout, timeoutEvent); } }
提示:toc的处理函数在后面部分定义,有0.1的概率丢包,但发生丢包时可以观察到tic的超时重传功能。
9.tictoc9
在tictoc8的基础上,实现了发送不同消息的功能。即新增加了generateNewMessage和sendCopyOf两个函数,第一个函数实现新消息的产生(按照时间序列,语法类似C++),第二个实现消息的发送。具体的代码如下:
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"); }
10.tictoc10
在前面的例子中,网路定义时都是只具有一个输入门和一个输出门和两个节点,在tictoc10中以数组的形式实现了一个节点类型多个门以及定义多个相同的节点类型。.end部分的定义代码如下:
simple Txc10 { parameters: @display("i=block/routing"); gates: input in[]; // declare in[] and out[] to be vector gates output out[]; } network Tictoc10 { @display("bgb=300,258"); submodules: tic[6]: Txc10 { @display("p=124,110;is=vl"); } 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++; }
实现了0/1、1/2、1/4、3/4、4/5直接的连接。
在.cc文件中,主要的变动是加入了forwardMessage函数,代码如下:
void Txc10::forwardMessage(cMessage *msg) { int n = gateSize("out"); int k = intuniform(0, n-1); EV << "Forwarding message " << msg << " on port out[" << k << "]\n"; send(msg, "out", k); }
在节点的所有输出门中,随机挑选一个进行消息发送。
11.tictoc11
在tictoc中,使用了信道模型。其余部分较tictoc10无改动,但是在运行以后可以看到六个节点之间的信息传输。
网络部分定义代码如下:
network Tictoc11 { types: channel Channel extends ned.DelayChannel { delay = 100ms; } submodules: tic[6]: Txc11; 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++; }
12.tictoc12
在前面的实例中,网络节点类型定义部分都是分别定义了输入门input和输出门output,在tictoc中,直接将二者合二为一inout门,定义部分的代码如下:
simple Txc12 { parameters: @display("i=block/routing"); gates: inout gate[]; // declare two way connections } network Tictoc12 { @display("bgb=550,332"); types: channel Channel extends ned.DelayChannel { delay = 100ms; } submodules: tic[6]: Txc12 { @display("p=165,143"); } 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++; }
在connections的连接上相当于两个节点的in和out门相互连接,在发出信息时只需要选择out门,代码如下:
// $o and $i suffix is used to identify the input/output part of a two way gate send(msg, "gate$o", k);
13.tictoc13
在前面的例子中,两个网络节点之间传输的信息都是系统定义的,只包含一条string类型语句的消息,在tictoc13中,使用了自己定义的消息类型。消息类型的定义在tictoc.msg文件中,代码如下所示:
message TicTocMsg13 { int source; int destination; int hopCount = 0; }
网路的定义与tictoc12一致,在编译完成消息文件后,系统会自动产生tictoc13_m.cc和tictoc13_m.h两个文件,其中.h文件中包含了如何获取有关message的相关信息,在编写.cc文件时需要导入。
#include "tictoc13_m.h"
在.cc文件中,generateMessage函数使用了获取message信息的相关函数,包括设置相关参数,还使用了intuniform随机产生目的地,代码如下:
TicTocMsg13 *Txc13::generateMessage() { int src = getIndex(); // our module index int n = getVectorSize(); // module vector size int dest = intuniform(0, n-2); //随机产生目的地 if (dest >= src) dest++; char msgname[20]; sprintf(msgname, "tic-%d-to-%d", src, dest); TicTocMsg13 *msg = new TicTocMsg13(msgname); msg->setSource(src); msg->setDestination(dest); return msg; }
在.cc文件中还使用了获取message信息的相关函数,在tictoc13_m.cc中可以获得相关函数的具体信息,这部分内容读者需要耐心学习,这里不再赘述。
14.tictoc14
在tictoc14中,新增了两个参数numSent和numReveived分别用于记录发送和收到的消息数目,并将其显示在仿真界面中,refreshDisplay()函数的代码如下:
void Txc14::refreshDisplay() const { char buf[40]; sprintf(buf, "rcvd: %ld sent: %ld", numReceived, numSent); getDisplayString().setTagArg("t", 0, buf); }
运行的效果图如下所示:
15.tictoc15
tictoc15在tictoc14的基础上添加了cHistogram hopCountStats;cOutVector hopCountVector;两个参数,用于记录消息在传播过程中经历的转播次数(跳数)并将其转化为统计图。最后的finish()函数是在仿真程序运行结束时,显示仿真过程的统计数据。代码如下:
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"); }
运行的效果如下所示:
点击左侧的Tictoc15,然后选中一个需要观察的节点,再双击HopCount(cOutVector)就可看见一个统计图(一定要运行到足够多的数据才能观察到,可以点击FAST)
在仿真结束后(点击STOP),在点击任务栏中如下所示按钮就可以看见运行的数据统计结果:
结果如下:
16.tictoc16
在tictoc16中使用了另一个参数 simsignal_t arrivalSignal;使用signal的形式记录消息转发的跳数,在.ini文件中加入一下信息:
[Config Tictoc16] network = Tictoc16 **.tic[2].hopCount.result-recording-modes = +histogram **.tic[0..2].hopCount.result-recording-modes = -vector
在tic[2]中可以看见统计图,打开方法与tictoc15类似,运行效果如下所示:
17.tictoc17
在tictoc中添加了在仿真界面的展示信息,核心代码如下:
if (hasGUI()) { char label[50]; // Write last hop count to string sprintf(label, "last hopCount = %d", hopcount); // Get pointer to figure cCanvas *canvas = getParentModule()->getCanvas(); cTextFigure *textFigure = check_and_cast
(canvas->getFigure("lasthopcount")); // Update figure text textFigure->setText(label); } 运行的结果如下所示:
18.tictoc18
tictoc18通过.ini文件进行了仿真时间限制、参数限制以及每一步的重复次数设置,代码如下:
[Config TicToc18] network = TicToc18 sim-time-limit = 250000s **.tic[2].hopCount.result-recording-modes = +histogram **.tic[*].hopCount.result-recording-modes = +vector *.numCentralNodes = ${N=2..100 step 2} repeat = 4
此外,tictoc18的改动在网络的显示形状上,将网络的拓扑结构改造成哑铃形。若在网络定义中将numCentralNodes的值改为2效果更加明显,网络部分定义代码如下:
simple Txc18 extends Txc16 { } network TicToc18 { parameters: int numCentralNodes = default(2); @display("bgb=604,416"); types: channel Channel extends ned.DelayChannel { delay = 100ms; } submodules: tic[numCentralNodes+4]: Txc18; connections: // connect the 2 nodes in one side to the central nodes tic[0].gate++ <--> Channel <--> tic[2].gate++; tic[1].gate++ <--> Channel <--> tic[2].gate++; // connect the central nodes together for i=2..numCentralNodes+1 { tic[i].gate++ <--> Channel <--> tic[i+1].gate++; } // connect the 2 nodes on the other side to the central nodes tic[numCentralNodes+2].gate++ <--> Channel <--> tic[numCentralNodes+1].gate++; tic[numCentralNodes+3].gate++ <--> Channel <--> tic[numCentralNodes+1].gate++; }
运行的效果如下所示: