学习翻译自: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。在弹出的检查框中我们可以看到hopCountStats和hopCountVector对象。我们也可以跟进一步查看这两个对象中记录的数据,在之前的检查框中右击这两个变量,点击Open Graphical View:

最开始的图像是空的,但是在用Fast和Express模式下运行,就能得到用于展示的足够的数据。一段时间后,我们可以得到以下的图像:

当我们认为已经收集到了足够多的数据,我们可以停止仿真然后在线下分析结果文件(Tictoc15-#0.vec和Tictoc15-#0.sca)。我们需要从菜单中选择Simulate->Conclude Simulation或者点图标,这将调用finish()函数并把数据写入sca文件。

③不修改model完成数据统计:tictoc16

在之前,我们在我们的model中添加了数据统计的代码,不过在编写代码时,我们通常不知道用户需要哪些数据。

OMNET++提供了额外的机制来记录数值和事件。所有Model都可以发送携带数值或对象signals。Model的创建人员只需要确定发送哪些signals,哪些数据附加到这些signals上,什么时候发送它们。使用者可以监听那些处理和记录它们的数据项目的signals。这种方式下的model代码就不必包含任何完全明了的统计数据,使用者不用看C++代码就可以自由统计某些信息了。

我们将重写在上一个例子最后中用以统计数据的代码,本例中将使用signals。我们可以安全地从我们的model中移除所有与数据统计相关的变量。比如,cOutVector和cLongHistogram就不再需要了。我们只需要一个携带了到达目的节点时,消息经历的hopCount的signal就可以了。

首先,我们需要定义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关键字指明了附加我们的统计数据的signal;record关键字告诉我们需要如何处理收到的数据。本例中我们需要将每个值都保存到vector file(vector关键字)中,此外还要统计上段中说到的那些统计信息(stats关键字)。NED文件写完之后,我们就完成了我们的model。

现在我们需要查看tic[1] module中关于hopCount的直方图,此外我们不需要记录tic 0,1,2的vector 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,它们的名字是description和lasthopcount,并且设置了它们的位置坐标。font参数说明了文本字体,有三个分量——typeface,size,style。这三个分量中的每一个都可以略去,这样实际中会代之以默认值。本例中我们只是设置了字体的style为bold。

默认情况下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,比如network。getCanvas()函数返回network的canvas,getFigure()可以通过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,名字分别是description、lasthopcount。这两个文本在此时是静态的。

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

omnet++:官方文档翻译总结(四)的更多相关文章

  1. Android官方文档翻译 十四 3.2Supporting Different Screens

    Supporting Different Screens 支持不同的屏幕 This lesson teaches you to 这节课教给你 Create Different Layouts 创建不同 ...

  2. omnet++:官方文档翻译总结(三)

    翻译总结自:Turning it Into a Real Network - OMNeT++ Technical Articles 接官方文档翻译总结(二),本节主要是真实网络的搭建 Part 4 - ...

  3. omnet++:官方文档翻译总结(二)

    这一部分是官方案例介绍 1.Introduction 学习自:Introduction - OMNeT++ Technical Articles 本教程是基于Tictoc的仿真案例,这些案例我们可以在 ...

  4. GreenDao官方文档翻译(上)

    笔记摘要: 上一篇博客简单介绍了SQLite和GreenDao的比较,后来说要详细介绍下GreenDao的使用,这里就贴出本人自己根据官网的文档进行翻译的文章,这里将所有的文档分成上下两部分翻译,只为 ...

  5. Aircrack-ng官方文档翻译[中英对照]---Airdecap-ng

    Aircrack-ng官方文档翻译---Airdecap-ng   Description[简介] With airdecap-ng you can decrypt WEP/WPA/WPA2 capt ...

  6. Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第一部分(Page 6)

    编写你的第一个 Django app,第一部分(Page 6)转载请注明链接地址 Django 2.0.1 官方文档翻译: Django 2.0.1.dev20171223092829 documen ...

  7. Spring官方文档翻译(1~6章)

    Spring官方文档翻译(1~6章) 转载至 http://blog.csdn.net/tangtong1/article/details/51326887 Spring官方文档.参考中文文档 一.S ...

  8. iOS网络基础---iOS-Apple苹果官方文档翻译

    CHENYILONG Blog iOS网络基础---iOS-Apple苹果官方文档翻译 iOS网络基础 技术博客http://www.cnblogs.com/ChenYilong/ 新浪微博http: ...

  9. 基本控件文档-UITextField属性---iOS-Apple苹果官方文档翻译

    本系列所有开发文档翻译链接地址:iOS7开发-Apple苹果iPhone开发Xcode官方文档翻译PDF下载地址 //转载请注明出处--本文永久链接:http://www.cnblogs.com/Ch ...

  10. UIControl事件---iOS-Apple苹果官方文档翻译

    本系列所有开发文档翻译链接地址: iOS7开发-Apple苹果iPhone开发Xcode官方文档翻译PDF下载地址 UIControl事件1.UIControlEventTouchDown单点触摸按下 ...

随机推荐

  1. 源码分析axios(1)~源码分析、模拟axios的创建

    ■ 查看源码发现,起初axios[instance=bind(Axios.prototype.request, context);]是一个函数, 但后续[ utils.extend(instance, ...

  2. Iptables的命令与用法

    目录 一:iptables的用法 1.iptables简介 二:Iptables链的概念 1.那四个表,有哪些作用? 2.那五条链,运行在那些地方? 3.Iptables流程图 三:iptables的 ...

  3. debian老版本下载地址

    https://cdimage.debian.org/cdimage/archive/

  4. QA(测试) 工作准则建议

    身为一个专业的 QA 当然需要有自己的测试原则,这些测试原则不仅可以帮助我们提高产品质量,对外还能体现出我们的专业性,从而让合作方后续还有意愿和我们合作. 1 测试前 1.1 需求评审 必须参与,有问 ...

  5. JDK安装步骤

    安装过程: 新建文件夹 新建文件夹 首先新建两个路径:D:\java\jdk和D:\java\jre,代表我把Java安装到D盘下的java路径下,在该路径下要新建两个路径,一会儿放jdk和jre. ...

  6. new操作符具体干了什么呢?

    考察对new关键的深刻认识,是否对前端知识有专研,如果没有专研的人,肯定说创建了一个对象,恭喜你面试官知道你是小菜鸟来的,这次面试基本上没有太大的希望了.一定要对new过程的4个步骤非常清楚,这样才能 ...

  7. 对JSP中的Session 简单理解

    我的理解: 简单来说,要使用服务器端的session对象,就是要有其对应的key,即sessionid,它只认识sessionid. 下面我说的cookie,url重写或者隐藏表单,都是为了将其对应的 ...

  8. Protocol基本概念

    1.protocol 基本概念 Protocol翻译过来, 叫做"协议" 在写java的时候都会有接口interface这个概念,接口就是一堆方法的声明没有实现,而在OC里面Int ...

  9. python——三方电子邮件库pyzmail

    pyzmail比默认smtplib和mime模块简单很多. 模块首页 http://pyzmail.readthedocs.io/en/latest/ python3.2以上,pip install ...

  10. 手把手教你实现pynq-z2条形码识别

    我是 雪天鱼,一名FPGA爱好者,研究方向是FPGA架构探索和SOC设计. 关注公众号,拉你进"IC设计交流群". 1.前言 单单实现一个二维码识别就花了将近一个星期,这篇文章我就 ...