http://blog.csdn.net/codingkid/article/details/7085214
首先解決一些概念上的問題:
1. 在omnetpp.org中提到的仿真模型和框架與OMNet++是什么關系?
OMNet++提供了基本的工具和機制來編寫仿真代碼,但它本身並不提供任何特定用於計算機網絡仿真,系統架構仿真和任意其它領域的組件;具體的仿真是由一些仿真模型和框架如Mobility Framework或INET Framework來支持,這些模型獨立於OMNet++開發,並有自己的發布周期。
2. OMNet++提供了什么?
一個C++庫,它由仿真內核及一些用來創建仿真組件(簡單模塊和信息)的工具類(如隨機數生成,統計收集,拓撲發現等);組裝和配置這些組件的基礎設施(NED語言,ini文件);運行時用戶接口或仿真環境(TKenv,Cmdenv);一個用來設計,運行和評估仿真的IDE環境;實時仿真的擴展接口;MRIP,並行的分布式仿真,數據庫連接等等這些組成。
3. OMNet++的仿真模型是什么樣的?
OMNet++提供了一個基於組件的架構,模型是由可重用的組件或模塊組成的。模塊之間可以通過gates(在其它系統中稱為ports,即端口)進行連接,以構成復合模塊。每個仿真模型是一個復合模塊類型的實例。這一層次(組件和拓撲)由NED文件來處理。
NED文件只定義了模型的結構(拓撲),其行為和模塊參數的某個子集則是開放的,如前面所提到的,行為是通過在簡單模塊相關聯的C++代碼來定義的,而在NED文件中沒有賦值的模塊參數則從ini文件中獲取它們的值。
5. 仿真的輸出是什么?
仿真過程中會有向量輸出和標題輸出,默認文件名為omnetpp.vec和omnetpp.sca,盡管omnetpp.ini可以指定輸出為不同的文件名,仍需要在簡單模塊中進行編碼以讓其擁有記錄仿真結果的能力,所以在他人編寫的仿真中可能並不創建這些文件。
一個輸出向量文件包含若干輸出向量,每一個向量都是一個 (timestamp,value) 對。輸出向量可以存儲例如關於時間的隊列長度,接收數據包時端到端的延遲,丟棄的數據包或者信道吞吐量—任意在簡單模塊中的編程所定義的統計量都可以。並且,可以在omnetpp.ini中配置輸出向量:即可以啟用或者關閉記錄某個輸出向量,或者用一定的仿真時間間隔來進行限制。可以通過查看cOutVector對象的C++的源代碼來查看一個簡單模塊可以獲取的輸出向量。
輸出向量根據時間來捕獲行為,而輸出標量文件則包含總的統計量:發送包的數目,丟棄包的數目,平均的端到端延遲,吞吐量的峰值等。可以在recodScala() 方法的調用 ,特別是在一個簡單模塊類中的finish()方法中查看輸出標量。
輸出向量可以用Plove程序來繪圖,而輸出標量則可以使用Scalars程序來繪圖。
6. 關於隨機數
OMNeT++默認的隨機數生成器是 Mersenne Twister,種子可以自動獲取,或者在omnetpp.ini中定義。OMNeT++ 支持很多種概率分布,並且在NED和C++中都是可用的(在 3.2 版本中有 14 個連續的分布和 6 個離散的分布, 具體可以查看 API doc)。非常量的模塊參數可以用隨機變量來賦值,如 exponential(0.2), 它表示C++代碼會在每次讀取參數時獲得一個不同的數字;這是指定隨機流量源的一種很方便的方法。( 常量參數也能用表達式如exponential(0.2)來賦值,但它只會被計算一次並且不再改變)。
7. 是否可以在OMNeT++中進行MRIP,分布式並行仿真,網絡模擬,或feature X?
可以。OMNet++ 具有很強的可擴展性並且開放源代碼,一些特性是可以開箱即用的:
MRIP是指 multiple replications in parallel,而Akaroa是一個很出色的工具。你可以單獨地下載並安裝它,然后啟用OMNet++的Akaroa支持進行重新編譯,但AFAIK Akaroa僅可以在 Linux(*nix)中使用,在OMNeT++的手冊中可以找到更多信息。
如果仿真需要大量的內存,則可以在集群中運行仿真。分塊和其它配置可以在omnetpp.ini中定義,但仿真模型本身卻不用進行改變(除非模型包含全局變量可能導致不能分布地運行)。通信層使用的是MPI,但事實上這是可配置的,所以如果你沒有MPI的話也仍然可以通過命名管道,或者基於文件的信息交換來進行一些基本的測試。當然如果需要,你也可以通過實現abstract cParsimCommunications接口來添加一個新的方案。
網絡模擬,實時仿真和類似於hardware-in-the-loop的功能是可用的,這是因為在仿真內核中的事件調度器是可插拔的。OMNet++的一個Demo展示了如何進行實時仿真和一個簡單的網絡模擬的樣例,足以讓你進入這個領域。Real network emulation with the INET Framework is in the queue.
可以用一個數據庫來代替omnetpp.ini文件作為配置數據的源,也能將仿真結果重定向到數據庫中,OMNeT++ 中包含這樣一個Demo,可以作為參考。
由於你擁有全部的源代碼和良好的文檔,所以你能根據自己的想法來實現很多事情。
8. 如何在C++代碼中編寫模型?
簡單模塊其實是C++類,你從cSimpleModule繼承子類,重定義一個虛成員函數,並通過Define_Module()宏來將新的類注冊到OMNeT++中。
模塊間主要使用消息傳遞進行通訊,而timers(timeouts)也處理模塊發送給自身的消息。消息應當是cMessage類或者是其子類,消息將被傳遞到模塊的 handleMessage(cMessage *msg) 方法,在這里應當添加你的代碼。幾乎你想定義的模塊行為都在handleMessage() 之中定義,因此這段代碼塊可能會很長,所以將其重構為其它成員函數例如命名為processTimer(),processPacket()是一個好主意(可以將activity() 方法視為handleMessage()的一個替換,但是在實踐中最好不要這么做。)。
你可以使用send(cMessage *msg, const char *outGateName) 方法來向其它模塊傳遞消息,對於無線仿真和一些情況下,則可以更方便地直接傳遞消息到其它模塊而不用在NED文件中建立連接。這可以由sendDirect(cMessage *msg, double delay, cModule *targetModule, const char *inGateName)方法來完成。Self-messages (that is, timers can be scheduled) 可以通過scheduleAt(simtime_t time, cMessage *msg) 進行傳送,並且在它們失效前可以通過cancelEvent(cMessage *msg) 來取消。這些函數都是cSimpleModule類的成員,在文檔中可以找到更多的相關信息。
基本的cMessage類包含幾個數據成員,最重要也最實用的是name,length,message”kind”(一個int成員)。其它數據成員則存儲此消息最近發送/調度的信息:arrival time,arrival gate等。為了進行協議仿真,cMessage可以封裝另外一個cMessage對象,可以查看它的encapsulate(cMessage *msg) 方法進行了解。這些方法也能逐步地修改域的長度。如果你需要在消息中運送更多的數據,其它數據成員可以通過繼承來添加。然而,你並不需要手工編寫新的C++類,可以更方便地在一個.msg文件中定義,從而讓OMNet++( opp_msgc 工具) 來為你生成C++類。生成的C++文件會擁有_m.h,m.cc的后綴。.msg文件支持進一步的繼承,組合,數組成員等等,並且它擁有可以讓在C++類中自定義的語法。一個消息文件的例子如下:
1 |
message NetworkPacket { |
2 |
fields: |
3 |
int srcAddr; |
4 |
int destAddr; |
5 |
} |
OMNet++經常用來進行網絡協議的仿真,cMessage有一個叫control info的域,它包含額外的信息來促進協議層間的通訊。例如,當應用層發送數據(一個消息對象)到TCP來傳輸時,它能在包含socket標志符的信息中附加一小段控制信息(一個對象)。或者,當TCP發送一個TCP segment到IP層傳輸時,它附加包含目標IP地址的控制信息,也可能是其它的一些選項,如TTL。控制信息也能發送到其它方向(向上),以便向上一層標記源IP地址或者TCP連接。控制信息是通過cMessage的 setControlInfo(cPolymorpic *ctrl) 和 removeControlInfo() 方法來處理的。
其它cSimpleModule模塊中需要重定義的虛成員函數是initialize()( 主要的初始化工作在這里進行,因為在構造函數的調用過程中模型也正在構建),initialize(int stage) 和多階段初始化的int numinitStages() const(這在一個模塊的初始化代碼依賴於其它已經初始化完畢的模塊時很有用),finish()來記錄總的結果(析構函數不適宜用於這種用途)。如果你想要模塊感知參數(參數可以交互式地在GUI中或由其它模塊改變,因此你需要重新讀取這個參數)在運行時的改變,則需要重定義handleParameterChange(const char *paramName)方法。
簡單模塊的NED參數可以使用par(const char *paramName) 方法來讀取,在典型的情況下,這個過程會在initialize()中進行,並將這些值存儲在模塊類的數據成員中。par()返回一個cPar對象的引用,這個引用可以用C++的語法或調用對象的doubleValue()方法等這些途徑轉為合適的類型(long,double,const char*,etc)。除了基本的類型,也可以是XML文件:參數可以賦值到XML文件中,它能用以DOM的對象樹的形式向C++代碼展示。
消息傳遞並不總是模塊間通訊的最好方法。例如,如果你設計了一個用於收集統計的模塊(常使用全局變量),通過消息來傳遞統計量的更新,不如將統計模塊作為一個C++對象,並調用它的用於此目的的公共成員函數(如updateStatistics(…))來得簡便。
對方法的直接調用有一些技術細節。首先你需要查找其它模塊:cModule的parentModule() 和submodule(const char*name)方法可以查找到與當前相關的模塊,而simulation.moduleByPath(const char *path) 可以通過一個絕對的路徑名在全局查找一個模塊,一旦你擁有其它模塊的指針,你需要將它轉化為實際的類型(i.e. to StatisticsCollector* from cModule*),這是由擁有與C++的dynamic_cast相同語法的check_and_cast來完成,但如果轉換不成功或指針為NULL時它會拋出一個錯誤。public的方法應當將Enther_Method()或者Enter_Method_Silent(…) 放置在頂端,它們啟用了GUI的動畫調用,同時進行了一些與臨時上下文切換相近的行為(在這里並不深究,但如果想要在方法中進行消息處理這是必須的)。INET框架廣泛地使用了方法調用來訪問模塊,如RoutingTable, InterfaceTable, NotificationBoard等。
INET的NotificationBoard在一些仿真中很有用,它通過對信息的產生者和消費者進行解耦,在它們之間轉發變化的notifications或者事件的notifications,從而支持幾個模塊間的信息共享(NotificationBoard寬泛地基於blackboard 區域,但在實踐中,僅轉發notifications比存儲blackboard中發布的信息更簡單有效。進一步地,notifications可能包含實際的數據或者指向數據的指針的拷貝)。
對於調試,模塊中打印一些必要的信息是很關鍵的。在OMNet++中使用ev<< (類似於printf和cout)將輸出寫到可以進行過濾的GUI窗口中,同時模塊用圖標的顏色或文件標簽及tooltips來展示狀態也有助於減少調試的時間;這個可以通過在執行時改變display string來完成(查看cModule's displayString())。通過調用bubble(const char*text)方法,可以在模塊的上方看到一個短暫出現的“氣泡”或“氣球”,這也是很有幫助的。另外一種調試的輔助是WATCH(variableName) 宏及其變種 (WATCH_VECTOR, 等),它能讓你在Tkenv GUI中查看變量的值(you'll find watched variables in the "Contents" tab of the module's inspector)。
為了記錄輸出向量,你應當添加一個cOutVector對象到類中作為一個數據成員(或者用new來創建),設置name string作為輸出向量的名字,然后持續調用它的record(double value)方法來記錄數字。輸出標量最好編寫在模塊(請查看cModule中的 recordScalar(const char *name, double value) 方法)的finish() 函數中,可以基於計數器作為模塊的數據成員。也可以計算基本的統計和圖表:請參考cStdDev, cDoubleHistogram and cLongHistogram classes 這些類。
NED文件描述靜態的拓撲,但也能動態地創建模塊和連接。這在網絡拓撲不是以NED文件(如普通文本文件,Excel sheet,database等)存儲,或者你想在運行時動態地創建和刪除模塊的情況下很有用。在前一種情況下,一般可以通過Awk,Perl,Python,Ruby,TCL或在文本編輯中一系列的查找/匹配操作來將數據轉化為NED。然而,當你決定以C++來創建動態模塊時,對一個樣本NED文件調用nedtool並查看生成的_n.cc文件會很有幫助。
接着動手編寫一個工程來運行:
可以參照這篇文章http://leeing.org/2010/01/24/the-ned-language-of-omnetpp-4-0/