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中查看或者分析它们。

posted @   ShineLe  阅读(2403)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示