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號