学习翻译自: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. elasticsearch算法之推荐系统的相似度算法(一)

    一.推荐系统简介 推荐系统主要基于对用户历史的行为数据分析处理,寻找得到用户可能感兴趣的内容,从而实现主动向用户推荐其可能感兴趣的内容: 从物品的长尾理论来看,推荐系统通过发掘用户的行为,找到用户的个 ...

  2. Ansible架构

  3. RSS生成工具/服务推荐

    时至2022,关于碎片化阅读.信息焦虑的讨论仍在继续且似乎并没有形成广泛共识的解决办法.而研究生期间主要研究方向就是推荐系统且未来也大概率从事相关岗位的我,对以算法为中心的信息获取方式可以说是又爱又恨 ...

  4. 前缀函数与Z函数介绍

    字符串算法果然玄学=_= 参考资料: OI Wiki:前缀函数与KMP算法 OI Wiki:Z函数(扩展KMP) 0. 约定 字符串的下标从 \(0\) 开始.\(|s|\) 表示字符串 \(s\) ...

  5. 在树莓派上开发SpringBoot 之使用VSCode远程开发

    一些运行在ARM单板电脑上的IoT应用通常会提供RESTful风格的API接口.本次的文章记录如何在本地电脑上通过VS Code的远程开发功能,在树莓派端创建一个SpringBoot工程,并实现调试和 ...

  6. Linux 配置 dubbo 和 dubbo的简单介绍。

    一.是么是  dubbo? 一.dubbo? 1.因为项目之间需要相互调用,达到某种预期的结果 1.1 restful? 门户网站必须要知道用户的登录状态,但是用户的登录状态在登录项目中,所以门户网站 ...

  7. ApacheCN JavaScript 译文集(二) 20211123 更新

    使用 Meteor 构建单页 Web 应用 零.前言 一.制作 Meteor 应用 二.构建 HTML 模板 三.存储数据和处理集合 四.控制数据流 五.使我们的应用与路由通用 六.保持会话状态 七. ...

  8. AT2650 [ARC077C] guruguru

    可以发现,如果我们枚举每个理想亮度 \(X\) 然后再求在这个理想亮度情况下的答案是非常难维护的. 不妨反过来,考虑每个位置 \(i, i + 1\) 之间对每个理想亮度 \(X\) 减少次数的贡献. ...

  9. select 级联选择

    转载请注明来源:https://www.cnblogs.com/hookjc/ <script   language="javascript">   <!--   ...

  10. Byobu安装与使用

    机子为Ubuntu18 Byobu安装 sudo apt-get install byobu Byobu安装后默认禁用,需要启用Byobu,之后每次登陆自动启用Byobu byobu-enable 还 ...