introduce
在 Omnet++安装完成后,samples/tictoc 中有该例子的完整文件,你可以立刻运行该文件看他是怎么工作的,不过更推荐按接下来的步骤一步步完成该项目的构建。
需求:
- 安装好了Omnet++
- 拥有良好的C++知识、C++开发经验
- To make the examples easier to follow, all source code in here is cross-linked to the OMNeT++ API documentation.
开始
模型介绍
创建一个有两个节点的网络,其中一个节点会创建一个数据包,两个节点会来回传递相同的数据包,这两个节点分别叫做 tic和toc ,接着我们会逐渐拓展这个模型,在这个过程中介绍Omnet++的特征。
构建项目
如果有一些版本生成了 package.ned 文件,要将其删除
本项目所有文件都包含在一个文件夹中,对于一些更大规模的文件,可能会创建多个子文件夹。
添加 NED 文件
OMNeT++ 使用 NED 文件来定义组件并将它们组装成更大的单元 就像网络一样。
创建名为 tictoc1.ned的ned文件
创建后,可以在 OMNeT++ IDE 的 “Editor”(编辑器)区域中编辑该文件。 OMNeT++ IDE 的 NED 编辑器有两种模式:Design 和 Source; 在它们之间使用编辑器底部的选项卡可以进行切换。在“设计”模式下, 可以使用鼠标和右侧的调色板以图形方式编辑拓扑。 在源模式下,NED源码可以直接编辑为文本。 在一种模式下完成的更改将立即反映在另一种模式中,因此您可以 在编辑过程中自由切换模式,并在任何模式下进行更改。
选择Source模式,将如下代码输入进去:
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;
}
再返回到 Design模式,可以看到如下类似的图像
文件中的第一个块声明 Txc1作为简单的模块类型。 简单模块在NED级别上是原子的。它们也是有源成分, 他们的行为是在 C++ 中实现的。该声明还说,Txc1有一个名为 in 的输入门和一个名为 out 的输出门。
第二个块 Tictoc1 声明为网络。 Tictoc1由两个子模块 tic和 toc 组装而成 ,两者都是模块 Txc1的实例。tic的输出门连接到toc的输入门,反之亦然。 双向传播延迟为 100 毫秒。
注:ned语言更详细的介绍见ned语言 ,在安装完Omnet++后再 doc文件夹下也可以找到这些文档
添加C++ 文件
现在通过添加C++文件来编程Txc1模块的功能,创建一个名为 txc1.cc的文件
将如下代码输入到 Txc.cc中
#include <string.h>
#include <omnetpp.h>
using namespace omnetpp;
/**
* Derive the Txc1 class from cSimpleModule. In the Tictoc1 network,
* both the `tic' and `toc' modules are Txc1 objects, created by OMNeT++
* at the beginning of the simulation.
*/
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
}
类Txc1 代表了基础模块 Txc1 ,Txc1是 Omnet++中 cSimpleModule 的子类,并且使用Define_Module() 函数宏注册到 Omner++中 。
如果忘记使用 Define_Module() 将自己新构建的模块宏注册到 Omnet++中,将会得到如下类似的报错: “Error: Class ‘Txc1’ not found – perhapsits code was not linked in, or the class wasn’t registered with Register_Class(), or inthe case of modules and channels, with Define_Module()/Define_Channel()”
在Txc1中重定义了函数 initialize() 和handleMessage() ,他们从仿真内核调用 ,第一个函数仅仿真开始时调用一次,第二个函数当有一个消息到达该模块时就会进行调用。
在 initialize() 函数中,创建了一个消息对象 cMessage ,并通过门 out 发送出去. 如果这个门连接到其他模块的 input门 ,仿真内核将传输这个消息到另一个模块的 handleMessage() ,根据 NED文件中的链接有100ms的传播时延。另一个模块将会将其传回,构成一个乒乓一般的传播。
消息(数据包、帧、作业等)和事件(计时器、超时)都是由 OMNeT++ 中的 cMessage 对象(或其子类)表示。 发送或调度它们后,它们将保持在仿真内核的“计划事件”或“未来事件”列表中,直到时间到了,他们才会通过 handleMessage()传输到模块。
注意:这个仿真没有内置停止条件,但可以哦那个GUI中停止。
添加 omnetpp.ini
为了运行这个仿真,我们要创建一个 omnetpp.ini文件,该文件告诉仿真程序想要仿真的网络是哪一个(NED文件可能包括多个网络),通过该文件还可以传递参数到模型中,显式的为随机数生成器指明seeds等。
与 NED的编辑器类似,ini文件的编辑器也有两个模式,Form和Source ,前者适合配置仿真内核,后者适合输入仿真参数。
现在选择Source,输入如下代码:
[General]
network = Tictoc1
现在已经完成了第一个模型的创建,接下来准备编译和运行该模型。
总结
通过该流程的学习,我们了解到一个模型的创建包含了三类主要文件 : ned 、cpp和ini 。 ned文件构建模块和网络,可以通过图形化界面或文本输入构建 ,cpp文件编程各个模块的具体功能 ,ini文件确定运行那个网络或传递参数等。
除了学习到创建各个文件的用途、创建方式、命名规则等。还学习到cMessage 、initialize()函数、handleMessage()函数相关的知识。进一步学习ned语言可以通过官方文档。在cpp文件中使用 omnetpp.h头文件和 omnetpp命名空间,可以进一步学习。
运行仿真
启动仿真程序
选择 omnetpp.ini文件 ,点击 Run按钮即可开始仿真
运行仿真
构建并启动仿真后,会弹出一个新界面
该窗口属于 Qtenv,是OMNeT++仿真运行时主要的 GUI。
在图形化窗口中,可以看见 tic和toc
点击最上面一栏的Run可以运行仿真,可以看见tic和toc相互交换信息的过程。
主窗口工具栏显示当前仿真时间。这是虚拟时间, 它与程序所需的实际(或挂钟)执行时间无关 。 实际上,在现实世界的一秒钟内可以模拟多少秒 很大程度上取决于硬件的速度,甚至更多地取决于硬件的性质和 仿真模型本身的复杂性。
比如硬件较差的情况下,仿真中只过去了一秒而实际时间耗费了五分钟。
请注意,节点处理消息所需的模拟时间为零。 在这个模型中,唯一使仿真时间流逝的是 连接上的传播延迟。
通过顶部的滑块,可以减慢或加速动画的速度。
F8 :停止仿真
F4:单步执行
F5:有动画仿真
F6:无动画仿真
F7:快速模式 ,将完全关闭追踪特征
可以多次运行该仿真来探索各个按钮的功能,直接点X可以退出该仿真
调试
模拟只是一个 C++ 程序,因此,它通常需要 在开发过程中进行调试。在本节中,我们将了解 调试的基础知识,以帮助您完成这项重要任务。
点击调试按钮开始进行调试
这将导致仿真程序在调试器下启动 (通常为 gdb)。IDE 也将切换到 “Debug perspective”, 即将其各种窗格和视图重新排列为更好的布局 适合调试。您可以使用工具栏上的“终止”按钮(红色方块)结束调试会话。
运行时错误
最常见的debug是在运行时出错,我们现在模拟一个错误,在txc1.cc的handleMessage()函数中的 多加一个send()函数,如下所示:
void Txc1::handleMessage(cMessage *msg)
{
//...
send(msg, "out"); // send out the message
send(msg, "out"); // THIS SHOULD CAUSE AN ERROR
}
此时再运行会报如下的错:
现在,在调试模式下运行仿真。由于debug-on-errors选项 默认情况下启用,仿真程序将在调试器中停止。
可以通过检查堆栈跟踪(嵌套的列表)来定位错误
注:右上角的两个小按钮可以切换debug和普通模式
你可以看到是omnet++的breakIntoDebuggerIfRequested()方法激活了调试器。然后,你需要搜索一个看起来熟悉的函数,即模型的一部分。在我们的例子中,这就是Txc1::handleMessage()。选择这一行将在编辑器区域中显示相应的源代码,并允许您在variables视图中检查变量的值。这些信息将帮助您确定错误的原因并修复它。
崩溃
让我们来制造一个崩溃,先恢复之前对handleMessage()函数的编辑,假装我们在发送消息之前忘记创建它,在initialize()函数中修改原来的行为:
cMessage *msg; // no initialization!
send(msg, "out");
此时运行该仿真,将会造成崩溃。
该报错也可能是:Simulation terminated with exit code: 139
然后在debug模式下运行该代码,然后就可以定位到出错的位置,同时可以查看变量窗口中的变量,帮助进一步修复错误
断点
您还可以手动在代码中放置断点。断点将停止执行,并允许您检查变量、逐行执行代码或恢复执行(直到下一个断点)。
双击编辑器中左侧的gutter,或者从上下文菜单中选择Toggle breakpoint,可以将断点置于源代码中的特定行。可以在breakpoints视图中检查活动(和不活动)断点的列表。
调试下一事件
如果您做过前面的练习,您一定已经注意到,在Txc1简单模块中的每个事件上都触发了断点。在现实生活中,通常只有在模块的第357个事件时才会出现错误,所以理想情况下,应该从这个时候开始调试。要点击简历356次才能找到错误的位置是很不方便的。一种可能的解决方案是向断点添加条件或忽略计数(请参阅上下文菜单中的断点属性)。然而,可能有一个更方便的解决方案。
在Qtenv中,使用Run Until获取要调试的事件。然后,从菜单中选择“模拟->调试下一个事件”。这将在调试器中在下一个事件的handleMessage()开始处触发一个断点,你可以开始调试该事件。
调试/运行 日志
重新启动仿真
当你用IDE工具栏上的Run或Debug按钮启动模拟程序时,与启动相关的设置会保存在启动配置中。启动配置可以在运行/调试配置对话框中查看,可以通过单击运行(调试)工具栏按钮旁边的向下小箭头来打开菜单,并选择运行(调试)配置…在里面。在相同的菜单中,您还可以单击启动配置的名称(例如tictoc),同时按住Ctrl键打开相应配置的对话框。
该对话框允许您激活各种启动设置。
序列图可视化
omnet++仿真内核可以将仿真过程中的消息交换记录到事件日志文件中。要启用记录事件日志,请在启动配置对话框中选中Record eventlog复选框。或者,您可以在omnetpp.ini中指定Record -eventlog = true,甚至在启动后在Qtenv图形运行时环境中使用Record按钮,
稍后可以使用IDE中的序列图工具分析日志文件。项目文件夹下的results目录中包含 .elog 文件。在omnet++ IDE中双击它会打开序列图工具,以及窗口底部的event log选项卡。
注意:产生的日志文件可能会非常大,所以最好只在特别需要时使用该功能
下图是使用序列图工具创建的,显示了消息如何在网络中的不同节点之间路由。在这种情况下,图表非常简单,但当你有一个复杂的模型时,序列图在调试、探索或记录模型的行为方面非常有价值。