OMNeT++ 基础语法:仿真概念和简单模块

仿真概念

OMNeT++ 中的离散事件

一个离散时间系统是指一个系统的状态改变方式是离散的。简而言之,系统状态的修改仅在事件发生时进行。离散时间系统可以使用离散事件模拟进行仿真。以计算机网络为例,计算机网络通常被看做是离散时间系统,他的一些离散事件包括了:

  • 数据包传输的开始
  • 数据包传输的结束
  • 重传超时到期

这意味着在数据包传输开始和数据包传输结束等两个事件之间,数据包的状态仍然是正在传输。其中,事件发生的时间通常被称作事件时间戳,在 OMNeT++ 中称为到达时间。模拟程序运行的时间称作模拟时间或虚拟时间。相对地,CPU 被消耗的时间称作真实时间或 CPU 时间。

事件和事件执行顺序

OMNeT++ 使用消息描述事件,每一个事件都通过一个 cMessage 类或其子类的实例来表示,从源模块发送到另一个目的模块,在这个过程中,事件将要发生的地方就是消息的目的模块,事件发生的模拟时间就是消息到达时间。此外,源模块也可以给自己发送一个消息。

事件按照到达时间顺序从 FES 中消耗以保持因果关系。举例说明,对给定的两条消息:

  1. 到达时间较早的消息先被执行
  2. 若到达时间相等,受限制性具有较高调度优先级(较小数值)的消息
  3. 如果优先级相同,则较早发送/计划的消息先被执行

其中,调度优先级是用户分配的整型消息属性。OMNeT++ 的模拟时间存储在 SimTime 类型的变量 simtime_t 中。SimTime 类以 64 位整数存储仿真时间,且不存在从 SimTimedouble 类型的隐式类型转换。如果需要进行转换,可以使用 dbl() 函数或者 SIMTIME_DBL() 宏。

SimTime 的其他有用方法包括 str(),将值作为字符串返回;parse(),将字符串转换为 SimTimeraw(),返回底层的 64 位整数;getScaleExp(),返回全局比例指数;isZero(),测试模拟时间是否为0;getMaxTime(),返回可以以当前比例指数表示的最大模拟时间。零和最大仿真时间也可以通过 SIMTIME_ZEROSIMTIME_MAX 宏访问。

FES

FES(Future Event Set) 的实现是离散事件模拟器性能的关键因素。在 OMNeT++ 中,FES 是可替换的,默认的 FES 实现使用二进制堆作为数据结构。二进制堆通常被认为是离散事件模拟的最佳 FES 算法,因为它为大多数工作负载提供了良好、平衡的性能。

包传输模型

时延、比特误差率、数据速率

传播延迟是消息通过信道到达目的地的时间段,以秒为单位计算;比特误差率对消息的传播有影响,是一个比特被错误传播的几率;数据速率的单位为 bit/second

消息的发送时间

消息的发送时间与传输的第一个比特和接收的最后一个比特相关,如下图所示。

消息的发送时间

上述模型不能模拟所有的协议。在 Token RingFDDI 协议中,如果一条消息沿途经过一系列的信道和模块,每个模块都将等待消息的最后一个比特到达后再开始发送。

简单模块的定义

(1)直接定义一个 CSimpleModule 的子类;

(2)使用 define_Module()define_Module_Like() 宏进行注册。其作用是声明一个 simple module 类型并且建立与对应 NED 文件的关联性。类名与 NED 定义的 SimpleModule 名相同时使用 define_Module();当需要为一个 NED 描述的 SimpleModule 提供不同的实现时使用 define_Module_Line()

(3)实现模块类的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// file: HelloModule.cc
#include <omnetpp.h>
using namespace omnetpp;

class HelloModule : public cSimpleModule
{
protected:
virtual void initialize();
virtual void handleMessage(cMessage *msg);
};

// register module class with OMNeT++
Define_Module(HelloModule);

void HelloModule::initialize()
{
EV << "Hello World!\n";
}

void HelloModule::handleMessage(cMessage *msg)
{
delete msg; // just discard everything we receive
}

// 相关的 NED 文件
// file: HelloModule.ned
simple HelloModule
{
gates:
input in;
}

(4)建立构造函数

使用宏 Module_Class_Members(classname, baseclass, stacksize); 。若使用 activity() 函数,则模块以协同模式执行,在内存中划分出一个独立的栈空间。如果栈空间为 0,则需要使用 handleMessage()。当初始化过程需要有变量参与时,就需要自己重写构造函数。

简单模块中的主要成员函数

activity()

activity() 可以使用户像编写一个进程一样编写简单模块、等待消息、延缓执行时间等。拥有这个函数的简单模块作为一系列协同程序协同进行,因此又被称为协同多任务。用户可以手动设置模块栈空间(一般为 16K)。如果模块存在递归或本地变量占用空间较大的时候,可以使用更大的栈空间。

handleMessage()

每当模块接收到消息时,系统都会以消息作为参数调用 handleMessage() 函数。需要注意的特点:

  • 每个 message/eventhandleMessage() 进行调用
  • 需要在 initialize() 函数中初始化变量,且无法调用如 wait()receive() 等一些基于协同的函数
  • SimpleModulestacksize 一定要设置为 0

handleMessage() 函数在事件处理过程中被调用。对每一个简单模块而言,用户都需要重新定义该函数。在该函数中,可以使用一些消息相关的函数如发送信息 send()、自发信息 scheduleAt()、删除自发信息 cancelEvent() 等。

Initialize()

Initialize() 函数在初始化消息被放入 FES(Future Event Set)后和初始化消息被执行前被调用以初始化成员变量。复合模块的初始化优先于其子模块。

Finish()

循环结束(FES 没有模拟事件时)后程序正常终止时被调用,模块的调用顺序与 Initialize() 相反。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 lgc0208@foxmail.com
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信