这一部分是官方案例介绍
1、Introduction
学习自:Introduction - OMNeT++ Technical Articles
本教程是基于Tictoc的仿真案例,这些案例我们可以在omnet安装目录下的sample/tictoc下找到。
2、Part 1 - Getting Started
The model
一开始,让我们组织起一个只有两个节点的network。这两个节点做了一些简单的事情:一个节点创造了一个包,之后这个包将在这两个节点间来回传递。我们称这两个节点为tic和toc。之后我们将逐步加强我们的模型,用以介绍omnet++的特性。
以下是从零开始实现我们的第一个仿真模型的步骤:
Step1、配置工程
- 打开IDE
- 左上角File->New->OMNeT++ Project,进入向导对话框;
- 输入tictoc作为工程名,创建一个empty project,然后点击Finish。
这样一个新工程就被创建了,我们可以在Project Explorer中看到它。(需要注意的是,一些版本的omnet++中会产生package.ned文件,我们不需要它,可以删除。)
在这个例子中,工程只有单个目录。在大型仿真中,工程的内容通常分布于src和simulations目录下,也可能是它们的子目录。
Step2、NED文件
omnet++使用NED文件来定义功能组件,并将它们组织成更大的单元,比如一整个网络。我们通过添加NED文件来实现我们的model。
- 在Project Explorer中,右键点击tictoc(即项目文件名),New->NED,给这个NED文件命名为tictoc1.ned
- IDE的NED编辑器有两种编辑模式Design、Source。在Design中,通过编辑器右边的Palette完成编辑;在Source中,通过直接编写NED代码完成编辑。在其中一个编辑器中的修改内容将会立刻反应到另一个编辑器中,所以在这两个编辑器中编辑都可以。(由于NED文件时是一般文本文件,所以我们甚至可以用外部文本编辑器来编辑,只是我们看不到语法高亮、内容复制、交叉引用和其它IDE特性等。)
- 进入Source模式下,输入以下内容:
simple Txc1 { gates: input in; output out; } //两个Txc1的实例(tic和toc) //tic toc互相传递消息 network Tictoc1 { submodules: tic:Txc1; toc:Txc1; connections: tic.out --> { delay = 100ms; } --> toc.in; tic.in <-- {delay=100ms;} <-- toc.out; }
当我们写完这些内容时,切换到Design模式下,可以看到如下变化:
程序第一块描述了Txc1,一个Simple module。Simple module是NED中的原子模块。它们也是活跃的组件,具体行为在C++中实现。上文代码也告诉了我们Txc1的特性——Txc1有一个input gate叫in,output gate叫out。
第二块描述了Tictoc1,一个network。Tictoc1由两个子模块tic和toc组织而成,这两个子模块都是Txc1的子模块。tic的output gate与toc的input gate是互联的,反之亦然。在这两种方式的传播过程中,会有100ms的延迟。关于NED
关于NED语言的语法和细节描述,我们可以在OMNeT++ Simulation Manual下找到。
Step3、C++文件
我们现在需要用C++实现Txc1这个simple module的功能。
- New->Source File创建一个txc1.cc文件
- 在文件中输入以下代码:
#include<string.h> #include<omnetpp.h> using namespace omnetpp; /** * Txc1继承自cSimpleModule。在Tictoc1网络中 * tic和toc模块都是Txc1实例,它们在仿真一开始就被 * omnet++创建了 */ class Txc1 :public cSimpleModule { protected: //以下虚函数标志了算法 virtual void initialize() override; virtual void handleMessage(cMessage *msg) override; }; //module class需要用omnet++注册 Define_Module(Txc1); void Txc1::initialize(){ //初始化函数在仿真开始时被调用 //为了开启tic-toc-tic-toc过程, //其中的一个模块需要发送第一个信息,假设这个模块是tic if(strcmp("tic",getName())==0){ //在"out" gate中生成并发送第一条消息; //"tictocMsg"是任意一个String,只不过这个String //标识了这个消息对象的名字 cMessage *msg=new cMessage("tictocMsg"); send(msg,"out"); } } void Txc1::handleMessage(cMessage *msg){ //当一条消息到达一个module之后,调用handleMessage()方法 //在这里的消息响应函数中,我们仅仅是把消息发送到其他的module,通过gate ‘out‘ //由于tic和toc都会做同样的事情,因此消息实际上是在这两者之间不停反弹的 send(msg,"out"); }
NED中的simple module与C++的class相对应,即NED文件中的Txc1与cc文件中的Txc1相对应。Txc1 class继承自omnet++的cSimpleModule类,并且需要在cc文件中用Define_Module(ned_module)进行模块绑定。
我们需要在cc文件中对两个方法cSimpleModule:initialize()和handleMessage()进行函数重载。在仿真过程中调用这两个方法的时机:第一个方法在仿真开始时只调用一次;第二个方法在有消息到达module时被调用。
在initialize()中,我们构造了一个message对象(cMessage),然后通过gate out发送出去。由于该gate是和其他module的input gate相连接的,因此仿真内核会通过handleMessage()的参数该消息传送到其他module——在经过了NED文件中设置的100ms的传播延迟之后。另一个模块在经历另一个100ms延迟之后将会传送回来。
消息Messages(包、帧、jobs等)和事件Events(定时器、超时等)都是用cMessage对象表示的的。在我们发送(send)或者调度(schedule)它们后,它们将被仿真内核的“scheduled event”或者“future events” List接收并保持,直到指定时间到达或者它们通过handleMessage()把消息传送到了别的module。
需要注意的是,仿真的整个过程将不会停止,而是永远进行下去。你可以从GUI中终止它。(你也可以在配置文件中指定仿真时间限制或者CPU时间限制)
Step4、omnetpp.ini文件
为了能够正常运行仿真程序,我们需要构造omnetpp.ini文件。这个文件告诉仿真程序哪个网络需要仿真,向model传递的参数,指明随机数生成器的种子等等。
- New->ini File;创建omnet.ini文件。
- 新文件将在Inifile 编辑器中打开。就像NED编辑器一样,Ini编辑器也有两种模式——Form和Source,当然在这两种模式下的编辑结果也是相同的。前者更适合配置仿真内核,后者适合设置模块参数。
转到Source模式后,写下如下代码:[General] network = Tictoc1
我们可以Form模式下确认这个结果
tictoc2和以后的例子,都会共享一个公共的omnetpp.ini文件
我们现在创建了第一个Model,接下来就是配置和运行它了。
3、Part 2 - Running the Simulation
Step1、启动仿真程序
当我们完成了章节2中的所有步骤之后,我们可以在选择omnetpp.ini文件后(无论是在编辑区域还是Project Explorer均可),再点击Run按钮启动一个仿真程序。
IDE将会自动构造我们的项目。我们可以在菜单中选择Project->Build All或者快捷键CTRL+B手动触发所有Module的Build。
Step2、运行仿真程序
在成功构造和启动仿真程序后,我们将会看到一个新的GUI窗口出现。这个窗口属于Qtenv——omnet++的运行时仿真GUI。你也会看到一个network,它包含了tik和tok,它们以动画的形式显示在主区域上。
点击Run按钮开始运行仿真过程。我们将会看到tic与toc不停地与另一个module交换消息的动画流。
toobar中显示了当前的仿真时间。这其实是个虚拟时间,与程序执行的实际时间没关系。实际上,现实世界中的仿真时间取决于硬件速度和仿真模型本身的算法复杂度。
需要注意的是,仿真过程中,节点处理消息的时间是0s。整个过程中唯一使仿真时间流逝的是连接过程中的传播时延。
我们可以通过图形工具栏中的按钮加快或者减慢动画速度。
其中的各种快捷键:
- F8:停止仿真
- F4:运行单步
- F5:运行(有动画)
- F6:运行(无动画、速度极快)
- F7:运行(无动画、急速、无信息跟踪)
主窗口下方的状态栏中标识了运行时的信息,包括事件号和事件时间。
调试
点击工具栏中的Debug进入调试模式,之后仿真程序将在调试模式下进行。
运行时错误(Runtime errors)
经常使用调试功能来追踪运行时错误。
我们可以用以下语句试验一下:
在txc1.cc中,复制handleMessage()中的send()行,之后代码变为:
void Txc1::handleMessage(cMessage *msg){ send(msg,"out"); send(msg,"out"); }
当我们尝试运行仿真程序时,就会得到一个错误消息:
现在,在Debug模式下运行仿真程序。由于debug-on-errors选项默认开启,因此仿真程序将会在调试过程中停止。我们就可以通过追踪函数栈来定位错误。
断点
通过在代码框最左边双击或者右键Toggle Breakpoint构建断点。所有断点可以在Breakpoints视图中查看
结果图表可视化
omnet++仿真内核可以在一个event log file(事件日志文件)中记录下消息交互。为了记录下事件日志,在RUN/Debug对话中勾选Record eventlog。此外,我们也可以在ini文件文件中指定record-eventlog = true;或者,在运行完程序后的Qtenv图像环境下点击Record按钮。