3.1添加圖標
在這里,我們使模型在GUI中看起來更漂亮。我們分配block/routing
圖標(文件images/block/routing.png
),然后將其繪制為青色,將其繪制為tic
黃色toc
。這是通過將顯示字符串添加到NED文件來實現的。i=
顯示字符串中的標簽指定圖標。
simple Txc2
{
parameters:
@display("i=block/routing"); // 添加默認圖標
gates:
input in;
output out;
}
//
// 讓兩個模塊有着不一樣的着色效果
// 使用青色表示“ tic”,使用金色表示“ toc”
//
network Tictoc2
{
submodules:
tic: Txc2 {
parameters:
@display("i=,cyan"); // 不改變圖標,只改變顏色
}
toc: Txc2 {
parameters:
@display("i=,gold"); // 這也是
}
connections:
tic.out --> { delay = 100ms; } --> toc.in;
tic.in <-- { delay = 100ms; } <-- toc.out;
}
改變顏色后的結果
3.2添加日志
我們還修改了C ++代碼。我們向其中添加日志語句,Txc1
以便打印出正在執行的操作。OMNeT ++提供了具有日志級別,日志通道,過濾等功能的復雜日志記錄工具,這對於大型和復雜的模型很有用,但是在此模型中,我們將使用其最簡單的形式EV
:
EV << "Sending initial message\n";
和
EV << "Received message `" << msg->getName() << "', sending it out again\n";
在OMNeT ++運行時環境中運行仿真時,日志窗口中將會輸出以下信息:
您還可以通過右鍵單擊_tic_和_toc_的圖標並從菜單中選擇“_Component log”_來打開單獨的_tic_和_toc_輸出窗口。當您擁有大型模型(“快速滾動日志綜合症”)並且僅對特定模塊的日志消息感興趣時,此功能將非常有用。
來源:tictoc2.ned,txc2.cc,omnetpp.ini
3.3添加狀態變量
在這一步中,我們向模塊添加一個計數器,並在十次交換后刪除該消息。
我們將計數器添加到類中
class Txc3 : public cSimpleModule
{
private:
int counter; // Note the counter here
protected:
我們在初始化方法initialize()
中初始化counter為10,然后在處理信息函數 handleMessage()
中減一,即每次消息到達時候減一。counter變成0的時候,仿真結束。
注意
WATCH(counter);
添加上面的語句可以在圖形運行時環境中查看計數器值。
如果單擊tic
的圖標,則主窗口左下角的檢查器窗口將顯示有關的詳細信息tic
。確保從頂部的工具欄中選擇了children mode。檢查器現在顯示計數器變量。
在繼續運行仿真時,您可以按照以下步驟進行操作:計數器不斷遞減直至達到零。
來源:tictoc3.ned,txc3.cc,omnetpp.ini
3.4添加參數
在這一步中,您將學習如何向模擬中添加輸入參數:我們將“magic number(神奇的數字)” 10變成參數,並添加布爾值參數,以決定模塊是否應在其初始化代碼中發送第一條消息(無論是一個tic
還是一個toc
模塊)。
模塊參數必須在NED文件中聲明。數據類型可以是數字,字符串,布爾值或xml(后者是為了輕松訪問XML配置文件)等。
simple Txc4
{
parameters:
bool sendMsgOnInit = default(false); //決定模塊初始化的時候是否要發送一條信息
int limit = default(2); // 具有默認值的另一個參數
@display("i=block/routing");
gates:
我們還必須修改C ++代碼以讀取中的參數 initialize()
,並將其分配給計數器。
counter = par("limit");// 猜測par應該是parameter的縮寫,這里是吧limit的值賦值給變量limits
我們可以使用第二個參數來決定是否發送初始消息:
if (par("sendMsgOnInit").boolValue() == true) {}//如果parameters里面的sendMsgInit是true就發送信息
現在,我們可以在NED文件中或從omnetpp.ini
中賦值參數。NED文件中的賦值具有高優先權。如果使用NED文件中的default(...)
語法,則可以定義參數的默認值。在這種情況下,您可以在omnetpp.ini中設置參數的值,也可以使用NED文件提供的默認值。
在這里,我們在NED文件中分配一個參數:
network Tictoc4
{
submodules:
tic: Txc4 {
parameters:
sendMsgOnInit = true;
@display("i=,cyan");
}
toc: Txc4 {
parameters:
sendMsgOnInit = false;
@display("i=,gold");
}
connections:
另一個在omnetpp.ini
:
Tictoc4.toc.limit = 5
請注意,由於omnetpp.ini支持通配符,並且從NED文件分配的參數優先於omnetpp.ini中的參數,
我們可以使用
Tictoc4.t * c.limit = 5
或者
Tictoc4.*.limit=5
甚至
**.limit=5
實現相同的效果。(*
和**
之間的區別是,*
不匹配點.,而**
匹配
在圖形運行環境中,您可以在主窗口左側的對象樹中,或在模塊檢查器的“參數”頁面中檢查模塊參數(信息顯示在主窗口的左下角點擊一個模塊)。
具有較小限制的模塊將刪除消息,從而結束仿真。
來源:tictoc4.ned,txc4.cc,omnetpp.ini
3.5使用NED繼承特性
如果我們仔細看一下NED文件,我們將意識到這一點,tic
並且toc
僅在它們的參數值和它們的顯示字符串上有所不同。我們可以通過從另一個繼承並指定或重寫其某些參數來創建新的簡單模塊類型。在我們的例子中,我們將得出兩個簡單的模塊類型(Tic
和Toc
)。之后,我們可以在定義網絡中的子模塊時使用這些類型。
從現有的簡單模塊派生很容易。這是基本模塊:
simple Txc5
{
parameters:
bool sendMsgOnInit = default(false);
int limit = default(2);
@display("i=block/routing");
gates:
input in;
output out;
}
這是子模塊。我們只需要簡單地指定參數值並添加一些顯示屬性即可。
simple Tic5 extends Txc5
{
parameters:
@display("i=,cyan");
sendMsgOnInit = true; // Tic modules should send a message on init
}
該Toc
模塊看起來相似,但是參數值不同。
simple Toc5 extends Txc5
{
parameters:
@display("i=,gold");
sendMsgOnInit = false; // Toc modules should NOT send a message on init
}
筆記
C ++實現是從基本簡單模塊(Txc5
)繼承的。
一旦創建了新的simple模塊,就可以在網絡中將它們用作子模塊類型:
network Tictoc5
{
submodules:
tic: Tic5; //參數limit 在這依然沒有賦值,我們會從ini文件中獲取她
toc: Toc5;
connections:
如您所見,網絡定義現在變得更短,更簡單。繼承使您可以在網絡中使用通用類型,減少多余的定義和參數設置。
3.6 建模處理延遲
在以前的機型,tic
和toc
立即回發收到的消息。在這里,我們將添加一些時間:tic
和toc
將在發送消息回去之前模擬1秒的延遲。在OMNeT ++中,這種計時是通過模塊向自身發送消息來實現的。這樣的消息稱為self-messages (這是由於它們的使用方式,否則它們也只是普通的消息對象)。
我們在類中添加了兩個cMessage派生而來兩個指針變量event
並tictocMsg
,用來計時模擬延遲使用的時間。
class Txc6 : public cSimpleModule
{
private:
cMessage *event; // 指向我們將用於計時的事件對象的指針
cMessage *tictocMsg; // 變量以記住消息,直到我們將其發送回去
public:
我們使用scheduleAt()函數“發送”self-messages,指定何時應將其傳遞回模塊。
scheduleAt(simTime()+1.0, event);
在handleMessage()函數
,現在我們要區分新的消息是否是通過input gate還是self message回來了(計時器過期)。在這里我們正在使用
if (msg == event) {// 判斷是否是當前對象
我們也還可以寫以下代碼也一樣
if (msg->isSelfMessage())//判斷是否是self messages
為了使源代碼保持較小,我們省略了計數器。
當運行f仿真時,您將看到以下日志輸出:
來源:tictoc6.ned,txc6.cc,omnetpp.ini
3.7隨機數和參數
在這一步中,我們將介紹隨機數。我們將延遲從1s更改為可以從NED文件或omnetpp.ini設置的隨機值。模塊參數能夠返回隨機變量;但是,要使用此功能,我們必須在handleMessage()
每次使用它時都讀取該參數。
// 延時的參數可以在(tictoc7.ned, omnetpp.ini)中設置成"exponential(5)"
// 這樣每次我們都會得到一個不同的延時
simtime_t delay = par("delayTime");
EV << "Message arrived, starting to wait " << delay << " secs...\n";
tictocMsg = msg;
另外,我們將以很小的(硬編碼)概率“丟失”(刪除)該數據包。
if (uniform(0, 1) < 0.1) {
EV << "\"Losing\" message\n";
delete msg;
}
我們將在omnetpp.ini中分配參數:
Tictoc7.tic.delayTime = exponential(3s)
Tictoc7.toc.delayTime = truncnormal(3s,1s)
您可以嘗試不管重新運行模擬多少次(或重新啟動_模擬_,_Simulate-> Rebuild network_菜單項),都將獲得完全相同的結果。這是因為OMNeT ++使用確定性算法(默認情況下為Mersenne Twister RNG)來生成隨機數,並將其初始化為相同的種子。這對於可重現的仿真很重要。如果將以下行添加到omnetpp.ini,則可以嘗試使用其他種子:
[General]
seed-0-mt=532569 # or any other 32-bit value
從語法上,您可能已經猜到OMNeT ++支持多個RNG。沒錯,但是,本教程中的所有模型都使用RNG 0。
聯系
也嘗試其他發行版。
來源:tictoc7.ned,txc7.cc,omnetpp.ini
3.8 超時,取消計時器
為了建模網絡協議更進一步,讓我們將模型轉換為停止等待仿真。這一次,我們將有單獨的類tic
和toc
。基本方案與之前的方案類似:tic
並且toc
將彼此之間傳遞一條消息。但是,toc
將以某種非零概率“丟失”消息,在這種情況下,tic
將不得不重新發送該消息。
這是toc
的代碼:
void Toc8::handleMessage(cMessage *msg)
{
if (uniform(0, 1) < 0.1) {
EV << "\"Losing\" message.\n";
bubble("message lost"); // 讓動畫更加豐富,彈出一個信息框...
delete msg;
}
else {
多虧bubble()
了代碼中的調用,toc
每當丟包的時候,都會顯示出來。
因此,tic
無論何時發送消息,都將啟動一個計時器。當計時器到期時,我們將假定該消息已丟失並發送另一條消息。如果toc
的答復到來,則必須取消計時器。計時器將是(還有什么?)一個自發消息。
scheduleAt(simTime()+timeout, timeoutEvent);
取消計時器將在cancelEvent()函數
調用中完成。請注意,這不會阻止我們一遍又一遍地重用同一超時消息。
cancelEvent(timeoutEvent);
您可以在txc8.cc中閱讀Tic的完整源代碼
來源:tictoc8.ned,txc8.cc,omnetpp.ini
3.9重發同一條消息
在這一步中,我們將優化先前的模型。如果需要重傳,因為我們在那里剛剛創建了另一個數據包,所以也可以,因為數據包中包含的內容很少。但是在現實生活中,保留原始數據包的副本通常更為實用,這樣我們就可以重新發送它而無需再次構建它。保留指向已發送消息的指針(以便我們可以再次發送)可能會更加容易,但是當消息在另一個節點處銷毀時,指針將變為無效。
我們在這里所做的是保留原始數據包,並僅發送其副本。收到toc
確認后,我們將刪除原始文件。為了更容易直觀地驗證模型,我們將在消息名稱中包含一個消息序列號。
為了避免handleMessage()
變得太大,我們將相應的代碼放入兩個新函數中,generateNewMessage()
然后sendCopyOf()
從中調用它們handleMessage()
。
功能:
cMessage *Tic9::generateNewMessage()
{
// Generate a message with a different name every time.
char msgname[20];
sprintf(msgname, "tic-%d", ++seq);
cMessage *msg = new cMessage(msgname);
return msg;
}
void Tic9::sendCopyOf(cMessage *msg)
{
// Duplicate message and send the copy.
cMessage *copy = (cMessage *)msg->dup();
send(copy, "out");
}
來源:tictoc9.ned,txc9.cc,omnetpp.ini
Omnet++學習資源太少,個人翻譯不易。您的每一分錢都會給后來的開發者提供幫助!感謝支持。