学习翻译自: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. alpakka-kafka(9)-kafka在分布式运算中的应用

    kafka具备的分布式.高吞吐.高可用特性,以及所提供的各种消息消费模式可以保证在一个多节点集群环境里消息被消费的安全性:即防止每条消息遗漏处理或重复消费.特别是exactly-once消费策略:可以 ...

  2. SpringBoot+Minio搭建不再爆肝秃头的分布式文件服务器

    前言 1).有人一定会问,为什么不用FastDFS?众所周知,FastDFS的原生安装非常复杂,有过安装经验的人大体都明白,虽然可以利用别人做好的docker直接安装,但真正使用过程中也可能出现许多莫 ...

  3. python 小兵(5)参数

    我们目前为止,已经可以完成一些软件的基本功能了,那么我们来完成这样一个功能:约x 1 2 3 4 5 pint("拿出手机") print("打开陌陌") pr ...

  4. Java虚拟机的意义

    什么是Java虚拟机? 作为程序员,大家都知道写的代码都是在Java虚拟机上运行的,但大家是否知道,Java虚拟机又是什么呢? 先看看网上搜到有关JAVA虚拟机的介绍 : 虚拟机是一种抽象化的计算机, ...

  5. Ubuntu更换镜像源

    不同的源 当修改sources.list文件时,我们需要将下面任意一个镜像源的代码复制粘贴到该文件中. 阿里源 # 阿里镜像源 deb http://mirrors.aliyun.com/ubuntu ...

  6. 营销MM让我讲MySQL日志顺序读写及数据文件随机读写原理

    摘要:你知道吗,MySQL在实际工作时候的两种数据读写机制? 本文分享自华为云社区<MySQL日志顺序读写及数据文件随机读写原理>,作者:JavaEdge . MySQL在实际工作时候的两 ...

  7. linux 多个C源文件编译

    转载请注明来源:https://www.cnblogs.com/hookjc/ 如果有多个源文件,基本上有两种编译方法: [假设有两个源文件为test.c和testfun.c] 1. 多个文件一起编译 ...

  8. rsync 远程同步部署——上下行同步

    rsync 远程同步部署--上下行同步 1.rsync (Remote Sync,远程同步) : 是一个开源的快速备份工具,可以在不同主机之间镜像同步整个目录树,支持增量备份,并保持链接和权限,且采用 ...

  9. Shell编程之循环语句与echo的用法

    Shell编程之循环语句与echo的用法 目录 Shell编程之循环语句与echo的用法 一.echo用法 1. echo常用选项 2. 常用的转义字符 3. 特殊符号%.#的用法 二.循环语句 1. ...

  10. ElasticSearch 基本介绍和读写搜索过程

    cluster 代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的.es的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部 ...