OMNet++学习笔记(二): tictoc例程学习笔记--Enhancing the 2-node TicToc
本博客继续记录我学习tictoc教程中的一些学习笔记,以便后续复习。
Part 3 - Enhancing the 2-node TicToc
具体可以新增的功能包括:
1. 给simple module添加icon
2. 在module initialize()和handleMessage()时增加Log输出
3. 增加计数器变量int counter,通过WATCH()实时监控counter的变化,当counter值小到一定值时,可以选择delete msg;
4. 向module增加parameters,增加module行为的多样性
parameters的作用:将某个parameter的值设置为10,并且再新建一个boolean类型的parameter来决定module是否在它的初始化代码中发送首个消息(无论是tic还是toc module)
1)在NED文件中声明parameter
simple Txc4 { parameters: //用来标识Module是否在其初始化函数中发送消息 bool sendMsgOnInit = default(false); int limit = default(2); @display("i=block/routing");//添加默认icon gates: input in; output out; }
2)修改cc文件,在cc文件中接收这些parameter,具体方法是par(变量名)
void Txc4::initialize(){ counter = par("limit"); if (par("sendMsgOnInit").boolValue()==true){ EV<<"Sending initial message\n"; cMessage *msg = new cMessage("tictocMsg"); send(msg,"out"); } }
现在,我们可以在NED文件或者ini文件中来设置这些para的值了。不过在NED中的赋值是更优先的,我们可以在定义para时用default(xxx)的方法设置默认值,正如我们在1)中的代码中所写的那样。
两种赋值方式如下:
①NED
network Tictoc4 { submodules: tic: Txc4 { parameters: sendMsgOnInit=true; // 初始化 @display("i=,cyan"); } toc: Txc4 { parameters: sendMsgOnInit=false; // 初始化 @display("i=,gold"); } connections: }
②ini
Tictoc4.toc.limit=5
需要注意的是,由于omnetpp.ini支持通配符,所以我们也可以写成以下形式:
Tictoc4.t*c.limit=5 Tictoc4.*.limit=5 **.limit=5
小结:可以直接在.cc中定义变量;也可以在NED文件中定义parameter,然后在.cc中利用par()来实现和.cc中定义的变量的绑定,或者直接par().**Value()来获取值。
5. NED继承
可以通过继承的方式创造一个新的simple module
simple Tic5 extends Txc5 { parameters: @display("i=,cyan"); sendMsgOnInit=true;//Tic modules将会在初始时发送消息 } simple Toc5 extends Txc5 //继承 { parameters: @display("i=,gold"); sendMsgOnInit=false;//Toc modules在初始时不发送消息 }
继承允许我们使用公共部分,避免冗余定义和parameter设置
6. 模拟节点处理延迟
在之前的model中,tic和toc会立刻把接收到的消息发送回去。本例中我们添加一些时间操作:tic和toc将会保存消息1s再发送出去。在omnet++中这样的时间操作的实现是通过节点发送消息给自身实现的。这些消息叫self-messages(这仅仅是因为它们的使用方式,否则就是一个普通消息)
我们给cc文件中的class添加两个cMessage*变量——event和tictocMsg,用以记录我们的时间、事件操作,进而在我们仿真时处理延迟。
class Txc6 : public cSimpleModule { private: cMessage * event;//指向用于时间操作的事件对象 cMessage * tictocMsg;//用于记录消息的变量 public :
我们通过scheduleAt()函数发送self-message,在函数中指明发送时间:
scheduleAt(simTime()+1.0,event)
在handleMessage()中我们必须对新消息加以区分——①通过input gate到达的消息;②self-message。
这里,我们可以用以下语句加以判断:
if(msg==event)
或者我们也可以写成
if(msg->isSelfMessage())
这样做,我们就可以不用counter,进一步使源代码规模变小。
具体源码如下,我在特别需要注意的地方写了注释。(PS. 官方文档也提供了源码可参考:https://docs.omnetpp.org/tutorials/tictoc/part3/)
① NED:没有修改
simple Txc1 { parameters: bool sendMsgOnInit = default(false); // Module是否在其初始化函数中发送消息 @display("i=block/routing");//添加默认icon gates: input in; output out; } network Tictoc1 { @display("bgb=270,260"); submodules: tic:Txc1{ parameters: sendMsgOnInit=true; @display("p=64,161"); @display("i=,cyan");//不改变icon样式,只是给它加颜色 }; toc:Txc1{ parameters: sendMsgOnInit=false; @display("p=142,58"); @display("i=,gold"); }; connections: tic.out --> { delay = 100ms;} --> toc.in; tic.in <-- { delay = 100ms;} <-- toc.out; }
② .cc:修改较多,需要注意
#include<string.h> #include<omnetpp.h> using namespace omnetpp; class Txc1:public cSimpleModule { private: cMessage* event;//指向用于时间操作的事件对象 cMessage* tictocMsg;//用于记录消息的变量 protected: virtual void initialize() override; virtual void handleMessage(cMessage *msg) override; }; Define_Module(Txc1); Txc1::~Txc1() { cancelAndDelete(event);//专门用来销毁self-messages的 delete tictocMsg; } void Txc1::initialize(){ // 注意:需要在初始化时new一个新的cMessage对象,否则send()方法会报错,说发送的是空对象。这里new的是self-message,每个节点都要new event = new cMessage("event"); if(par("sendMsgOnInit").boolValue()==true){ EV << "Sending initial message\n"; // 注意:一个网络中传输的消息需要是“转发”的,否则会报错。因此只有发送者new一个新的cMessage对象,作为普通消息传输 tictocMsg = new cMessage("tictocMsg"); send(tictocMsg,"out"); } } void Txc1::handleMessage(cMessage *msg){ if(msg==event){ // 接收到了self-message,可以继续发送普通消息 send(tictocMsg,"out"); }else{ // 接收方第一次收到普通消息时,需要保存指针,否则后续无法继续发送普通消息 tictocMsg = msg; // 接收到了普通消息,需要延时,因此发送self-messge scheduleAt(simTime()+1.0,event); } }
③.ini文件:没有修改
[General]
network = Tictoc1
效果如下图所示:
小结:通过self-message及相应scheduleAt()可以实现消息延时的模拟效果-->计算任务调度可以用类似的方法
7. 随机数
目的:使延时时间从固定值1s变为一个随机时间,使其与实际情况更加贴合。这一点可以在NED文件和ini文件中实现,然后可以在handleMessage()中利用par()绑定这个随机parameter并使用它
具体源码如下:
①NED:声明parameter,注意这里定义了是volatile和@unit()
simple Txc1 { parameters: bool sendMsgOnInit = default(false); volatile double delayTime @unit(s); // 发送消息的延时是个随机数 @display("i=block/routing"); gates: input in; output out; } network Tictoc1 { @display("bgb=270,260"); submodules: tic:Txc1{ parameters: sendMsgOnInit=true; @display("p=64,161"); @display("i=,cyan"); }; toc:Txc1{ parameters: sendMsgOnInit=false; @display("p=142,58"); @display("i=,gold"); }; connections: tic.out --> { delay = 100ms;} --> toc.in; tic.in <-- { delay = 100ms;} <-- toc.out; }
②.cc:利用par()绑定这个parameter
#include<string.h> #include<omnetpp.h> using namespace omnetpp; class Txc1:public cSimpleModule { private: cMessage* event; cMessage* tictocMsg; public: virtual ~Txc1(); protected: virtual void initialize() override; virtual void handleMessage(cMessage *msg) override; }; Define_Module(Txc1); Txc1::~Txc1() { cancelAndDelete(event); delete tictocMsg; } void Txc1::initialize(){ event = new cMessage("event"); if(par("sendMsgOnInit").boolValue()==true){ EV << "Sending initial message\n"; tictocMsg = new cMessage("tictocMsg"); send(tictocMsg,"out"); } } void Txc1::handleMessage(cMessage *msg){ if(msg==event){ send(tictocMsg,"out"); }else{ //将在.ned和.ini中定义的delayTime绑定 simtime_t delay = par("delayTime"); EV << "Message arrived, starting to wait " << delay << " secs...\n"; tictocMsg = msg; scheduleAt(simTime()+delay,event); } }
此外,还可以在handleMessage中模拟丢包的行为
if(uniform(0,1)<0.1){ EV<<"\"Losing\" message\n"; delete msg; }
③.ini:定义parameter的随机生成方式
[General] network = Tictoc1 seed-0-mt=532569 #或者任一32b的值,设置随机数种子,这样重运行多少次仿真都会得到一样的结果 Tictoc1.tic.delayTime = exponential(3s) Tictoc1.toc.delayTime = truncnormal(3s,1s)
8. 超时、计时器
为了更进一步模拟网络协议,我们可以采用stop-and-wait仿真model,实现的场景为:tic和toc分别向对方发送一个消息,同时toc会有一定概率丢包,此时下tic需要重新发送。
两个特点:
1)介绍了bubble()方法,当toc丢包时,可以在前端用弹出框标识消息丢失
2)利用self-message实现timer的功能,其中定时器的取消=self-message的删除,即使用cancelEvent()方法。
9. 重传相同消息
在实际中,我们通常是保留一个原始包的备份,这样,当我们需要重传时就不用再创建新的包了。
具体方法:保留原始包并只发送它的备份。当toc的Ack到达时,我们再删除这个原始包。为了让这一过程在模型中可视化,我们为每个消息记录一个消息号。
为了避免handleMessage()函数过于庞大,我们将相关代码放在两个新的函数generateNewMessage()和sendCopyOf()中,并在handleMessage()中调用它们
由于我不需要这个功能,所以大家可以参考博客的 “Part3 - Enhancing the 2-node TicToc ⑨ 重传相同消息:tictoc9” 的章节,其中对NED, .cc, .ini都进行了详细的分析
浙公网安备 33010602011771号