OMNeT++ tictoc 示例

概述

tictocOMNeT++ 官方给出的入门示例。示例模拟了一个 2 结点网络,数据包在这两个节点之间(tic 结点和 toc 结点之间来回传输。tictoc 示例下总共有 18 个小实验。

实验步骤

tictoc 1 为例。首先,需要激活 omnet 环境。通过在 omnetpp-5.6.1/ 目录下使用命令:

1
. setenv

随后,输入命令进入 tictoc 示例:

1
cd sample/tictoc/

进入之后,需要创建 tictoc1.nedtxc1.ccomnetpp.ini 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// tictoc1.ned

//定义节点 Txc1
simple Txc1
{
gates:
input in;
output out;
}

//定义网络 Tictoc1
network Tictoc1
{
submodules:
tic: Txc1;
toc: Txc1;
connections:
tic.out --> { delay = 100ms; } --> toc.in;
tic.in <-- { delay = 100ms; } <-- toc.out;
}
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
32
33
34
35
36
37
38
39
40
41
42
// txc1.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)
{
send(msg, "out"); // send out the message
}
1
2
3
# omnetpp.ini
[General]
network = Tictoc1

创建好需要的文件后,先输入命令创建 Makefile 文件:

1
opp_makemake

接下来,再输入 make 命令产生可执行文件 tictoc。最后开始仿真:

1
./tictoc -u Cmdenv

得到的结果如下图所示:

tictoc1

此时表示仿真已经正常运行。

示例笔记

tictoc 1

展示了网络、信道和结点的实例化方式以及两个结点相互通信的方式,具体方式如实验步骤章节所示。

tictoc 2

展示新命令:EV,相当于 C++ 中的 cout 命令。但该命令在命令行仿真环境中无法实现预期功能。

使用了 @display 命令对图形化窗口进行美化,但在命令行中没有太大意义。

tictoc 3

展示命令 watch,可以在图形化窗口中看到实时值,但在命令行中没有太大意义。

展示了一种停止仿真的方式:

1
delete msg;

tictoc 4

展示从 NED 文件中读取参数的方式:par()。例如 par("sendMsgOnInit").boolValue() 就是读取这个布尔型参数的值。

给参数赋初值可以在 NED 文件中完成,也可以在 omnetpp.ini 文件中完成。以给 limit 参数赋初值为例:

1
2
3
4
// omnetpp.ini
[Config Tictoc4]
network = Tictoc4
Tictoc4.toc.limit = 5
1
2
3
4
5
6
7
8
9
10
// tictoc4.ned
simple Txc4
{
parameters:
bool sendMsgOnInit = default(false);
int limit = default(2);
gates:
input in;
output out;
}

tictoc 5

tictoc 4 的基础上将 tictoc 分为两个不同的对象。

tictoc 6

展示了利用 scheduleAt() 函数实现的定时功能,初始5秒后开始发送,之后每次接收到间隔1秒自发送:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// txc6.cc
void Txc6::initialize() {
event = new cMessage("event");
tictocMsg = nullptr;
if (strcmp("tic", getName()) == 0) {
tictocMsg = new cMessage("tictocMsg");
scheduleAt(5.0, event);
}
}

void Txc6::handleMessage(cMessage *msg) {
if (msg == event) {
send(tictocMsg, "out");
tictocMsg = nullptr;
}
else {
tictocMsg = msg;
scheduleAt(simTime()+1.0, event); // simTime() 为当前仿真时间
}
}

tictoc 7

tictoc7.ned 文件内新增语句:

1
volatile double delayTime @unit(s);

定义了 delayTime,并且指明了单位为秒。此外,在 omnetpp.ini 文件中对 tictoc 的值进行了初始化:

1
2
3
4
[Config Tictoc7]
network = Tictoc7
Tictoc7.tic.delayTime = exponential(3s)
Tictoc7.toc.delayTime = truncnormal(3s,1s)

其中的时延都是随机值,第一个是均值为3s,第二个是正态分布。

该示例中还展示了概率丢包的设计方式,即在 txc7.cc 文件中的 handleMessage(cMessage *msg) 中设计:

1
2
3
4
5
6
7
8
9
if (uniform(0, 1) < 0.1) {
EV << "\"Losing\" message\n";
delete msg;
}
else {
simtime_t delay = par("delayTime");
tictocMsg = msg;
scheduleAt(simTime()+delay, event);
}

整个过程就是,首先延迟5秒发,收到后延迟随机时间回发,再收到后延迟随机时间发,并且期间还有一定概率数据丢失。

tictoc 8

首先该示例将 tictoc 定义成了两个类,使得这两个模块可以实现不同的功能或者不同的输入输出。

此外,该示例展示了丢包重传的方式,即在 txc8.cc 文件中使用:

1
2
3
4
5
6
if (msg == timeoutEvent) {
EV << "Timeout expired, resending message and restarting timer\n";
cMessage *newMsg = new cMessage("tictocMsg");
send(newMsg, "out");
scheduleAt(simTime()+timeout, timeoutEvent);
}

整体流程为 tic 先发出信息,并且等待1秒钟。如果1秒钟后收到了表示超时的自信息,则新建一个消息再发送一次。如果一秒内收到了非自信息,就说明成功发送,此时取消定时并且新建一个信息发送过去。toc 有0.1的概率丢失信息。

tictoc 9

对示例 8 的优化,即重传的时候传送消息的拷贝:

1
2
3
4
if (msg == timeoutEvent) {
sendCopyOf(message);
scheduleAt(simTime()+timeout, timeoutEvent);
}

tictoc 10

展示了每个模块具有多个输入输出时的操作方式,即在 NED 文件中:

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
simple Txc10
{
parameters:
@display("i=block/routing");
gates:
input in[]; // declare in[] and out[] to be vector gates
output out[];
}

network Tictoc10
{
submodules:
tic[6]: Txc10;
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++;
}

inout 后面的 ++ 是因为目前不知道要用多少个 inout,就让他们每次执行完自加 1。

txc10.cc 中随机选择输出口进行输出的代码:

1
2
3
4
5
6
7
8
void Txc10::forwardMessage(cMessage *msg)
{
// In this example, we just pick a random gate to send it on.
// We draw a random number between 0 and the size of gate `out[]'.
int n = gateSize("out");
int k = intuniform(0, n-1);
send(msg, "out", k);
}

tictoc 11

定义了信道 Channel,其他与示例 10 一致:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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++;
}

这里使用的是延迟信道 ned.DelayChannel ,还有理想信道 ned.IdealChannel、数据率信道 ned.DatarateChannel

tictoc 12

在 NED 文件中引入 gate 以代替 inout,减少代码量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
simple Txc12
{
parameters:
@display("i=block/routing");
gates:
inout gate[]; // declare two way connections
}

// using two way connections to further simplify the network definition
network Tictoc12
{
types:
channel Channel extends ned.DelayChannel {
delay = 100ms;
}
submodules:
tic[6]: Txc12;
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++;
}

但是在使用 gate 代替 inout 后,发送数据也要改变格式,即 send(msg, "gate$o", k); 其中,gate$o这个 o 代表 out,如果是 i 则代表 in

tictoc 13

使用 .msg 文件直接通过 build 生成 .h 文件和 .cc 文件,与命令行仿真关系不大。

tictoc 14

介绍了强制类型转换方式:

1
TicTocMsg13 *ttmsg = check_and_cast<TicTocMsg13 *>(msg);

此外,消息经过传输后不仅包含了消息名,还包含源节点、目的节点和跳数信息。例如,可以通过函数 msg->gerDestination() 获取目的地信息。

tictoc 15

介绍了自动记录一个有关消息交换历史的详细日志方法,在 omnetpp.ini 文件中设置:

1
record-eventlog = true

首先在类 txc15 的私有属性中写入变量:

1
2
cLongHistogram hopCountStats;
cOutVector hopCountVector;

然后在初始化中:

1
2
hopCountStats.setName("hopCountStats");
hopCountVector.setName("HopCount");

接着当消息到达目标节点时,更新统计信息:

1
2
hopCountVector.record(hopcount);
hopCountStats.collect(hopcount);

最后标量信息需要调用finish()函数手动记录:

1
2
3
4
recordScalar("#sent", numSent);
recordScalar("#received", numReceived);

hopCountStats.recordAs("hop count");

把跳数记录在一个输出向量里(横坐标是仿真时间,纵坐标是跳数)

tictoc 16

介绍了使用 @signal 进行数据统计的方式:

@signal [arrival](type = “long”):定义一个名为 arrivallong 类型信号。

@statistic[hopCount](title="hop count"; source="arrival"; record=vector,stats; interpolationmode=none);:定义一个统计量,名字叫 hopCount,统计量标题名为 hop count,数据源是信号 arrival,记录形式为向量,stats 代表记录了平均值,方差等,是固定的。

txc16.cc 文件中,首先定义一个 simsignal_t arrivalSignal; 变量,并且对信号进行注册 arrivalSignal = registerSignal("arrival");。需要注意的是,每个统计信号都要注册。然后使用 int hopcount = ttmsg->getHopCount(); 得出跳数,最后通过 emit(arrivalSignal, hopcount);函数来对信号进行统计量的输出。

其中,代码 msg->setHopCount(msg->getHopCount()+1); 实现了跳数的增加。

tictoc 17

在示例 16 的基础上增加了一些 GUI 的美化方式,与命令行关系不大。

tictoc 18

更改网络结构,在 NED 文件中引入了 for 循环。

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
simple Txc18 extends Txc16
{
}

network TicToc18
{
parameters:
int numCentralNodes = default(2);
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++;
}
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 lgc0208@foxmail.com
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信