如今,咱们将迈出一大步:建立几个tic
模块并将它们链接到网络中。如今,咱们将使它们的工做变得简单:一个节点生成一条消息,其余节点继续沿随机方向扔消息,直到它到达预约的目标节点为止。NED文件将须要进行一些更改。首先,该Txc
模块将须要具备多个输入和输出门:数组
simple Txc10 { parameters: @display("i=block/routing"); gates: input in[]; //声明input,ouput两种类型的gate数组 output out[]; }
数组[ ]
多个门变成gate向量。向量的大小(门数)将在咱们使用Txc构建网络的地方肯定。安全
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++; }
在这里,咱们建立了6个模块做为模块向量,并将它们链接起来。
生成的拓扑以下所示:
在此版本中,tic[0]
将生成要发送的消息。这是在函数initialize()
的帮助下完成的,该getIndex()
函数返回向量中模块的索引。
代码的forwardMessage()
实质是handleMessage()
每当消息到达节点时咱们从中调用的函数。它绘制一个随机的gate number,并在该gate上发送message。网络
void Txc10::forwardMessage(cMessage *msg) { // 咱们选择随机的一个gate发送信息 // 咱们在0~out[]数组长度之间选择一个随机数 int n = gateSize("out"); int k = intuniform(0, n-1); EV << "Forwarding message " << msg << " on port out[" << k << "]\n"; send(msg, "out", k); }
当消息到达时tic[3]
,handleMessage()
它将删除该消息。
请参阅txc10.cc中的完整代码
练习
您会注意到,这种简单的“路由”效率不是很高:一般,数据包会在两个节点之间不断跳动一段时间,而后再发送到另外一个方向。若是节点不将数据包发送回发送方,则能够在某种程度上进行改进。实现这一点。提示:cMessage::getArrivalGate()
, cGate::getIndex()
。请注意,若是消息不是经过门到达的,而是self-messages,则getArrivalGate()
返回NULL
。
来源:tictoc10.ned,txc10.cc,omnetpp.inidom
咱们新的网络定义变得很是复杂冗余,尤为是链接部分。让咱们尝试简化它。咱们注意到的第一件事是,链接始终使用相同的delay
参数。与简单模块相似,能够为链接建立类型(它们称为信道)。咱们应该建立一个指定延迟参数的信道类型,并将该类型用于网络中的全部链接。函数
network Tictoc11 { types: channel Channel extends ned.DelayChannel { delay = 100ms; } submodules:
如您所见,咱们经过添加一个types
字段在网络定义中定义了新的信道类型。此类型定义仅在网络内部可见。它称为局部或内部类型。若是愿意,您也能够将简单模块用做内部类型。
笔记
咱们经过专门内置来建立通道DelayChannel
。(能够在ned
包内找到内置信道。这就是为何咱们在extends
关键字后使用完整类型名称的ned.DelayChannel的缘由
)
如今,让咱们检查一下该connections
部分是如何更改的。this
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++; }
如您所见,咱们仅在链接定义内指定通道名称。这样能够轻松更改整个网络的延迟参数。
来源:tictoc11.ned,txc11.cc,omnetpp.ini编码
若是再检查一下该connections
部分,咱们将意识到每一个节点对都经过两个链接链接。每一个方向一个。OMNeT ++ 4支持两种方式的链接,所以让咱们使用它们。
首先,咱们必须定义双向(或所谓的inout
)门,而不是以前使用的分离input
和output
门。url
simple Txc12 { parameters: @display("i=block/routing"); gates: inout gate[]; // declare two way connections }
新connections
部分以下所示:指针
connections: tic[0].gate++ <--> Channel <--> tic[1].gate++;//tic[0].gate[0]<-->tic[1].gate[0] tic[1].gate++ <--> Channel <--> tic[2].gate++;//tic[1].gate[1]<-->tic[2].gate[1] tic[1].gate++ <--> Channel <--> tic[4].gate++; tic[3].gate++ <--> Channel <--> tic[4].gate++; tic[4].gate++ <--> Channel <--> tic[5].gate++; }
咱们已经修改了门名称,所以咱们必须对C ++代码进行一些修改。code
void Txc12::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 `gate[]'. int n = gateSize("gate"); int k = intuniform(0, n-1); EV << "Forwarding message " << msg << " on gate[" << k << "]\n"; // $o and $i 后缀被用来识别门的双向输入输出 send(msg, "gate$o", k); }
笔记
gate名称后的特殊$i和$o后缀容许咱们分别使用链接的两个方向。
来源:tictoc12.ned,txc12.cc,omnetpp.ini
在此步骤中,再也不对目标地址进行硬编码tic[3]
-咱们绘制一个随机目标,而后将目标地址添加到消息中。
最好的方法是重写cMessage的子类并将目标添加为数据成员。手动编码消息类一般很乏味,由于它包含许多样板代码,所以咱们让OMNeT ++为咱们生成该类。消息类规范位于tictoc13.msg
:
message TicTocMsg13 { int source; int destination; int hopCount = 0; }
笔记
有关消息的更多详细信息,请参见OMNeT ++手册的第6节。
设置makefile以便调用消息编译器opp_msgc并生成消息声明tictoc13_m.h
并tictoc13_m.cc
从消息声明生成(文件名是根据tictoc13.msg
文件名而不是消息类型名生成的)。它们将包含一个TicTocMsg13
从[ cMessage
]子类生成的类;该类将为每一个字段提供getter和setter方法。
咱们将tictoc13_m.h
在咱们的C ++代码中包含该代码,而且能够将其TicTocMsg13
用做任何其余类。
#include "tictoc13_m.h"
例如,咱们使用如下几行generateMessage()
来建立消息并填写其字段。
TicTocMsg13 *msg = new TicTocMsg13(msgname); msg->setSource(src); msg->setDestination(dest); return msg;
而后,handleMessage()
像这样开始:
void Txc13::handleMessage(cMessage *msg) { TicTocMsg13 *ttmsg = check_and_cast<TicTocMsg13 *>(msg); if (ttmsg->getDestination() == getIndex()) {
在handleMessage()的参数中,咱们将消息做为cMessage*
指针。可是,只有TicTocMsg13
将msg转换为时,咱们才能访问在中定义的字段TicTocMsg13*
。纯C样式的强制转换((TicTocMsg13 *)msg
)是不安全的,由于若是消息_不是_a TicTocMsg13
,则程序最终将崩溃,从而致使错误,而这是很难发现的。
C ++提供了一种称为的解决方案dynamic_cast
。在这里,咱们使用check_and_cast<>()
OMNeT ++提供的功能:它尝试经过强制转换指针dynamic_cast
,若是失败,则会经过错误消息中止模拟,相似于如下内容:
在下一行中,咱们检查目标地址是否与节点的地址相同。该getIndex()
成员函数返回子模块向量模块的索引(记住,在咱们声明是NED文件tic[6]: Txc13
,因此节点地址0..5)。
为了使模型的执行时间更长,在消息到达其目标以后,目标节点将生成另外一条具备随机目标地址的消息,依此类推。阅读完整的代码:txc13.cc
运行模型时,它将以下所示:
您能够单击消息以在检查器窗口中查看其内容。双击将在新窗口中打开检查器。(您必须为此暂时中止模拟,或者要很是快地处理鼠标)。检查器窗口显示许多有用的信息;消息字段可/以在“_目录”_页面上看到。
来源:tictoc13.ned,tictoc13.msg,txc13.cc,omnetpp.ini 练习 在此模型中,在任何给定时刻只有一条消息正在运行:节点仅在另外一条消息到达它们时才生成一条消息。咱们这样作是为了使跟踪仿真变得更加容易。更改模块类,以便改成按期生成消息。消息之间的间隔应该是一个模块参数,返回指数分布的随机数。