omnet++:官方文檔翻譯總結(四)


學習翻譯自:Adding Statistics Collection - OMNeT++ Technical Articles

Part 5 - Adding Statistics Collection

①展示收發包的數量:tictoc14

為了大致了解運行時每個節點收發包的數量,我們給module對應的class添加兩個計數器:numSent、numReceived

class Txc14 : public cSimpleModule
{
    private:
        long numSent;
        long numReceived;
    protected:

它們需要在initialize()中設置為0並且用關鍵字WATCH加以監視,這樣我們就可以在運行時監視其變化了。現在我們可以使用Find/inspect對象對話框來了解有多少包被不同的節點收發了。

打開方式見下邊兩張圖,打開結果其實是一樣的,都是一個 Find Objects對話框。

 

 

 

 需要注意的是,在具體的仿真model中,幾乎不可能得到完全相同的數字,我們唯一能確定的就是intuniform()正常工作了。但是實際中仿真,我們可以通過這種方式很快地了解到model中各個節點的狀態。

這些信息在顯示時放在module的icon之上。在display關鍵字中使用t=這個tag指明文本,我們唯一需要修正的是運行時顯示出來的字符串。以下代碼做了這項工作:

void Txc14 :: refreashDisplay() const
{
    char buf[40];
    sprintf(buf,"rcvd: %ld sent: %ld",numReceived,numSent);
    getDisplayString().setTagArg("t",0,buf);
}

結果就像下邊這樣:

 總結:tictoc14

  1. 本節的目的是:將每個節點收發包的數量顯示到界面中;
  2. 存儲收發包數量的變量,在cc文件的simple module類中定義,就像數據都是private權限那樣,這里兩個變量也是private權限:
    class Txc14 : public cSimpleModule
    {
        private:
            long numSent;
            long numReceived;
  3. 這兩個變量需要在initialize()初始化,並且用WATCH加以監視:
    numSent=0;
    numReceived=0;
    WATCH(numSent);
    WATCH(numReceived);
  4. 界面更新的工作放在refreshDisplay中,在我們的程序中,我們只需要在這個函數中寫刷新界面的代碼,而不用在某處調用它,程序運行過程中,IDE會自動調用它刷新整個界面:
    void Txc14 :: refreashDisplay() const
    {
        char buf[40];
        sprintf(buf,"rcvd: %ld sent: %ld",numReceived,numSent);
        getDisplayString().setTagArg("t",0,buf);
    }

    與之對應的是,這個函數在類中,也應該聲明為const

    virtual void refreshDisplay() const override;

     

 ②添加統計信息

之前的仿真Model中我們收集到了一些統計信息,我們可以對其進行一些操作。例如,你可能對消息到底目的地前經歷的平均跳數比較感興趣。

我們將在每個消息到達時,把它所經歷的跳數記錄到一個輸出向量中(例如一系列的(time,value)對,並根據time進行排序)。我們也可以記錄下每個節點的平均值、標准差、最小值、最大值並且在仿真結束時把它們寫入文件中。然后我們就可以用OMNET++ IDE中的工具來分析這個文件。

例如,我們添加一個output vector對象(用來記錄信息,最后把這個對象中的信息保存到Tictoc15-#0.vec中)和一個histogram對象(用於計算平均值等等)到class中:

class Txc15 : public cSimpleModule
{
    private:
        long numSent;
        long numReceived;
        cLongHistogram hopCountStats;
        cOutVector hopCountVector;
    protected:

每當一個消息到達目的節點時,我們更新統計信息。把下段代碼加入handleMessage()中:

hopCountVector.record(hopcount);
hopCountStats.collect(hopcount);

hopCountVector.record()函數將數據寫入Tictoc15-#0.vec中。對於大的仿真model和長時間運行之后,vec文件會變的很大。為了解決這種情況,我們可以在ini文件中特別指明開啟/關閉vector,我們也可以指定仿真時間間隔(在時間間隔以外的數據記錄將被丟棄)。

當開啟了新的仿真過程時,已存在的Tictoc15-#0.vec/sca文件就被刪除了。

數值數據(仿真過程中通過histogram對象收集到)只能在finish()函數中手動記錄。finish()方法在仿真成功完成(即它不因錯誤而終止的時候)之后被調用。recordScalar()方法負責把數據寫入到Tictoc15-#0.sca文件中。

void Txc15::finish()
{
    //該方法在OMNET++仿真結束時調用
    EV<<"Sent:     "<<numSent<<endl;
    EV<<"Received: "<<numReceived<<endl;
    EV<<"Hop count, min:    "<<hopCountStats.getMin()<<endl;
    EV<<"Hop count, max:    "<<hopCountStats.getMax()<<endl;
    EV<<"Hop count, mean:    "<<hopCountStats.getMean()<<endl;
    EV<<"Hop count, stddev:    "<<hopCountStats.getStddev()<<endl;

    recordScalar("#sent",numSent);
    recordScalar("#received",numReceived);
    hopCountStats.recordAs("hop count");
}

sca文件將被存儲在result/子目錄下。

我們也可以在仿真過程中顯示出這些數據。為了實現這一目的,右擊一個module,選擇Open Details。在彈出的檢查框中我們可以看到hopCountStatshopCountVector對象。我們也可以跟進一步查看這兩個對象中記錄的數據,在之前的檢查框中右擊這兩個變量,點擊Open Graphical View:

 

 

 

 最開始的圖像是空的,但是在用FastExpress模式下運行,就能得到用於展示的足夠的數據。一段時間后,我們可以得到以下的圖像:

 

 

 

 當我們認為已經收集到了足夠多的數據,我們可以停止仿真然后在線下分析結果文件(Tictoc15-#0.vecTictoc15-#0.sca)。我們需要從菜單中選擇Simulate->Conclude Simulation或者點圖標,這將調用finish()函數並把數據寫入sca文件。

 

 ③不修改model完成數據統計:tictoc16

在之前,我們在我們的model中添加了數據統計的代碼,不過在編寫代碼時,我們通常不知道用戶需要哪些數據。

OMNET++提供了額外的機制來記錄數值和事件。所有Model都可以發送攜帶數值或對象signals。Model的創建人員只需要確定發送哪些signals,哪些數據附加到這些signals上,什么時候發送它們。使用者可以監聽那些處理和記錄它們的數據項目的signals。這種方式下的model代碼就不必包含任何完全明了的統計數據,使用者不用看C++代碼就可以自由統計某些信息了。

我們將重寫在上一個例子最后中用以統計數據的代碼,本例中將使用signals。我們可以安全地從我們的model中移除所有與數據統計相關的變量。比如,cOutVectorcLongHistogram就不再需要了。我們只需要一個攜帶了到達目的節點時,消息經歷的hopCountsignal就可以了。

首先,我們需要定義signal,下段代碼中arrivalSignal就是之后要用到的signal變量:

class Txc16 : public cSimpleModule
{
    private:
        simsignal_t arrivalSignal;
    protected:

 

在使用signals前,我們必須注冊所有signals。注冊代碼通常放在initialize()方法中:

void Txc16 :: initialize()
{
    arrivalSignal = registerSignal("arrival")

之后我們就可以發送signal了,本例中的發送時機選擇消息到達目的節點時:

void Txc16 :: handleMessage(cMessage * msg)
{
    TicTocMsg16 *ttmsg = check_and_cast<TicTocMsg16 *>(msg);
    
    if(ttmsg->getDestination()==getIndex()){
        //消息到達目的地時
        int hopcount = ttmsg->getHopCount();
        //發送signal
        emit(arrivalSignal , hopcount);
        
        EV<<"Message "<<ttmsg<<" arrived after "<<hopcount<<" hops.\n";

由於我們不用保存任何數據,所以finish()方法可以刪去了,我們再也不用它了。

最后一步就是在NED文件中定義signal了。在NED文件中聲明signals,這樣我們就可以在一個地方了解到所有關於module的信息了。我們可以看到它的parameters、它的input和output gates、signals和它代表的統計數據。

simple Txc16
{
    parameters:
        @signal[arrival](type="long");
        @statistic[hopCount](title="hop count";source="arrival";record=vector,stats;interpolationmode=none);
        @display("i=block/routing");

現在我們也可以定義一個需要被默認采集的統計數據。在我們之前的例子中已經收集到了一些關於到達信息的hop count的統計信息(最大值、最小值、均值、總和等),所以讓我們在本例中收集相同的數據。

source關鍵字指明了附加我們的統計數據的signalrecord關鍵字告訴我們需要如何處理收到的數據。本例中我們需要將每個值都保存到vector filevector關鍵字)中,此外還要統計上段中說到的那些統計信息(stats關鍵字)。NED文件寫完之后,我們就完成了我們的model。

現在我們需要查看tic[1] module中關於hopCount的直方圖,此外我們不需要記錄tic 0,1,2vector data。我們可以不接觸C++和NED文件來實現添加直方圖並且移除不需要的vector的目的,只需要打開ini文件並且修改統計數據的記錄語句:

[Config Tictoc16]
network = Tictoc16
**.tic[1].hopCount.result-recording-modes = +histogram
**.tic[0..2].hopCount.result-recording-modes = -vector

④添加figures(形式):tictoc17

OMNET++可以在canvas上展示一系列的figures,例如文本、幾何圖形、圖像。這些figures可以是靜態的,也可以根據仿真過程中發生的事件動態變化。本例中,我們展示了靜態描述文本、動態顯示hop count的文本。

我們在ned文件中創建figures,需要在parameters@figure說明:

network Tictoc17
{
    parameters:
        @figure[description](type=text;pos=5,20;font=,,bold;
                    text="Random routing example - displaying last hop count");
        @figure[lasthopcount](type=text;pos=5,35;text="last hopCount: N/A");

這里創建了兩個文本figure,它們的名字descriptionlasthopcount,並且設置了它們的位置坐標。font參數說明了文本字體,有三個分量——typeface,size,style。這三個分量中的每一個都可以略去,這樣實際中會代之以默認值。本例中我們只是設置了字體的stylebold

默認情況下lasthopcount中的文本是靜態的,但是當消息到達時需要修改它。要做到這一點,需要修改handleMessage()函數:

if(hasGUI()){
    char label[50];
    //把last hop count寫為string形式
    sprintf(label,"last hopCount = %d",hopcount);

    //定義一個指向figure的指針
    cCanvas * canvas = getParentModule()->getCanvas();
    cTextFigure *textFigure = check_and_cast<cTextFigure*>(canvas->getFigure("lasthopcount"));
    //更新文本
    textFigure->setText(label);
}

cc文件中用cTextFigure這個class代表figure。figure types有很多種,所有都是繼承自cFigure的子類。我們在得到hopCount變量之后,即可寫入代碼並更新文本。

對上文代碼的解釋,我們要在network的canvas上畫figures,getParentModule()函數返回這個節點的父module,比如networkgetCanvas()函數返回networkcanvasgetFigure()可以通過Figure名得到figure。之后我們用setText()函數更新figure文本。

當我們運行仿真時,在第一個消息到達前,figure會顯示“last hopCount:N/A”。之后,每當一個消息到達它的目的地時,這個文本都會更新:

 

 如果對布局不滿意,比如figure文本和節點重疊在一塊了,可以點擊“re-layout”:

 

總結:tictoc17

  1. 本例中,我們學習了在界面上動態、靜態顯示文本的方法;
  2. NED文件中,要先對這兩個文本進行定義,在network下的parameters下,用@figure進行標注:
    network Tic17
    {
        parameters:
            @figure[description](type=text;pos=5,20;font=,,bold;
              text="Random routing example - displaying last hop count") ;
            @figure[lasthopcount](type=text;pos=5,35;text="last hopCount: N/A");

    這里創建了兩個文本類型的figure,名字分別是descriptionlasthopcount。這兩個文本在此時是靜態的。

  3. 對文本進行動態修改的代碼,在handleMessage()中:
    if (hasGUI()) {
        char label[50];//要動態顯示的內容
        sprintf(label,"last hopCount = %d",hopcount);
    
        //獲取指向canvas的指針
        cCanvas * canvas = getParentModuel->getCanvas();
    
        //獲取指向canvas中文本lasthopcount的指針,這個lasthopcount就是之前我們在ned中定義的那個靜態文本
        cTextFigure * textFigure = check_and_cast<cTextFigure *>(canvas->getFigure("lasthopcount"));
        
        //更新這個文本內容(動態更新)
        textFigure->setText(label);
    }
  4. 在第一個消息到達前,文本是之前的靜態文本;在一個消息到達后,handleMessage處理消息,刷新頁面,更新文本使之變成動態文本。

 在最后幾步中,我們收集並且展示了統計數據。下一節中我們將會展示如何在IDE中查看或者分析它們。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM