OMNet++学习笔记(二): tictoc例程学习笔记--Enhancing the 2-node TicToc

本博客继续记录我学习tictoc教程中的一些学习笔记,以便后续复习。

 Part 3 - Enhancing the 2-node TicToc

具体可以新增的功能包括:

1. 给simple module添加icon

2. 在module initialize()和handleMessage()时增加Log输出

3. 增加计数器变量int counter,通过WATCH()实时监控counter的变化,当counter值小到一定值时,可以选择delete msg;

4. 向module增加parameters,增加module行为的多样性

parameters的作用:将某个parameter的值设置为10,并且再新建一个boolean类型的parameter来决定module是否在它的初始化代码中发送首个消息(无论是tic还是toc module)

1)在NED文件中声明parameter

simple Txc4
{
    parameters:
        //用来标识Module是否在其初始化函数中发送消息
        bool sendMsgOnInit = default(false);
        int limit = default(2);
        @display("i=block/routing");//添加默认icon
    gates:
        input in;
        output out;
}

2)修改cc文件,在cc文件中接收这些parameter,具体方法是par(变量名)

void Txc4::initialize(){
    counter = par("limit");
    if (par("sendMsgOnInit").boolValue()==true){
        EV<<"Sending initial message\n";
        cMessage *msg = new cMessage("tictocMsg");
        send(msg,"out");
    }
}

现在,我们可以在NED文件或者ini文件中来设置这些para的值了。不过在NED中的赋值是更优先的,我们可以在定义para时用default(xxx)的方法设置默认值,正如我们在1)中的代码中所写的那样。

两种赋值方式如下:

①NED

network Tictoc4
{
    submodules:
        tic: Txc4 {
            parameters:
                sendMsgOnInit=true; // 初始化
                @display("i=,cyan");
        }
        toc: Txc4 {
            parameters:
                sendMsgOnInit=false; // 初始化
                @display("i=,gold");
        }
    connections:   
}

②ini

Tictoc4.toc.limit=5

需要注意的是,由于omnetpp.ini支持通配符,所以我们也可以写成以下形式:

Tictoc4.t*c.limit=5
Tictoc4.*.limit=5
**.limit=5

小结:可以直接在.cc中定义变量;也可以在NED文件中定义parameter,然后在.cc中利用par()来实现和.cc中定义的变量的绑定,或者直接par().**Value()来获取值。

5. NED继承

可以通过继承的方式创造一个新的simple module

simple Tic5 extends Txc5
{
    parameters:
        @display("i=,cyan");
        sendMsgOnInit=true;//Tic modules将会在初始时发送消息
}

simple Toc5 extends Txc5 //继承
{
    parameters:
        @display("i=,gold");
        sendMsgOnInit=false;//Toc modules在初始时不发送消息
}

继承允许我们使用公共部分,避免冗余定义和parameter设置

6. 模拟节点处理延迟

在之前的model中,tic和toc会立刻把接收到的消息发送回去。本例中我们添加一些时间操作:tic和toc将会保存消息1s再发送出去。在omnet++中这样的时间操作的实现是通过节点发送消息给自身实现的。这些消息叫self-messages(这仅仅是因为它们的使用方式,否则就是一个普通消息)

我们给cc文件中的class添加两个cMessage*变量——event和tictocMsg,用以记录我们的时间、事件操作,进而在我们仿真时处理延迟。

class Txc6 : public cSimpleModule
{
private:
    cMessage * event;//指向用于时间操作的事件对象
    cMessage * tictocMsg;//用于记录消息的变量
public :

我们通过scheduleAt()函数发送self-message,在函数中指明发送时间:

scheduleAt(simTime()+1.0,event)

在handleMessage()中我们必须对新消息加以区分——①通过input gate到达的消息;②self-message。

这里,我们可以用以下语句加以判断:

if(msg==event)

或者我们也可以写成

if(msg->isSelfMessage())

这样做,我们就可以不用counter,进一步使源代码规模变小。

具体源码如下,我在特别需要注意的地方写了注释。(PS. 官方文档也提供了源码可参考:https://docs.omnetpp.org/tutorials/tictoc/part3/

① NED:没有修改

simple Txc1
{
    parameters:
        bool sendMsgOnInit = default(false); // Module是否在其初始化函数中发送消息
        @display("i=block/routing");//添加默认icon
     gates:
         input in;
         output out;   
}

network Tictoc1
{
    @display("bgb=270,260");
     submodules:
         tic:Txc1{
              parameters:
                 sendMsgOnInit=true;
                @display("p=64,161");
                @display("i=,cyan");//不改变icon样式,只是给它加颜色
         };
         toc:Txc1{
             parameters:
                 sendMsgOnInit=false;
                  @display("p=142,58");
                @display("i=,gold");
         };
    connections:
         tic.out --> { delay = 100ms;} --> toc.in;
         tic.in <-- { delay = 100ms;} <-- toc.out;          
}

② .cc:修改较多,需要注意

#include<string.h>
#include<omnetpp.h>
using namespace omnetpp;

class Txc1:public cSimpleModule
{
private:
    cMessage* event;//指向用于时间操作的事件对象
    cMessage* tictocMsg;//用于记录消息的变量

protected:
    virtual void initialize() override;
    virtual void handleMessage(cMessage *msg) override;
};

Define_Module(Txc1);

Txc1::~Txc1()
{
    cancelAndDelete(event);//专门用来销毁self-messages的
    delete tictocMsg;
}

void Txc1::initialize(){
    // 注意:需要在初始化时new一个新的cMessage对象,否则send()方法会报错,说发送的是空对象。这里new的是self-message,每个节点都要new
    event = new cMessage("event");
    if(par("sendMsgOnInit").boolValue()==true){
        EV << "Sending initial message\n";
        // 注意:一个网络中传输的消息需要是“转发”的,否则会报错。因此只有发送者new一个新的cMessage对象,作为普通消息传输
        tictocMsg = new cMessage("tictocMsg");
        send(tictocMsg,"out");
    }
}

void Txc1::handleMessage(cMessage *msg){
    if(msg==event){
        // 接收到了self-message,可以继续发送普通消息
        send(tictocMsg,"out");
    }else{
        // 接收方第一次收到普通消息时,需要保存指针,否则后续无法继续发送普通消息
        tictocMsg = msg;
        // 接收到了普通消息,需要延时,因此发送self-messge
        scheduleAt(simTime()+1.0,event);
    }
}

③.ini文件:没有修改

[General]
network = Tictoc1

 效果如下图所示:

小结:通过self-message及相应scheduleAt()可以实现消息延时的模拟效果-->计算任务调度可以用类似的方法

7. 随机数

目的:使延时时间从固定值1s变为一个随机时间,使其与实际情况更加贴合。这一点可以在NED文件和ini文件中实现,然后可以在handleMessage()中利用par()绑定这个随机parameter并使用它

具体源码如下:

①NED:声明parameter,注意这里定义了是volatile和@unit()

simple Txc1
{
    parameters:
        bool sendMsgOnInit = default(false); 
        volatile double delayTime @unit(s); // 发送消息的延时是个随机数
        @display("i=block/routing");
     gates:
         input in;
         output out;   
}

network Tictoc1
{
    @display("bgb=270,260");
     submodules:
         tic:Txc1{
              parameters:
                 sendMsgOnInit=true;
                @display("p=64,161");
                @display("i=,cyan");
         };
         toc:Txc1{
             parameters:
                 sendMsgOnInit=false;
                  @display("p=142,58");
                @display("i=,gold");
         };
    connections:
         tic.out --> { delay = 100ms;} --> toc.in;
         tic.in <-- { delay = 100ms;} <-- toc.out;          
}

②.cc:利用par()绑定这个parameter

#include<string.h>
#include<omnetpp.h>
using namespace omnetpp;

class Txc1:public cSimpleModule
{
private:
    cMessage* event;
    cMessage* tictocMsg;

public:
    virtual ~Txc1();

protected:
    virtual void initialize() override;
    virtual void handleMessage(cMessage *msg) override;
};

Define_Module(Txc1);

Txc1::~Txc1()
{
    cancelAndDelete(event);
    delete tictocMsg;
}

void Txc1::initialize(){
    event = new cMessage("event");
    if(par("sendMsgOnInit").boolValue()==true){
        EV << "Sending initial message\n";
        tictocMsg = new cMessage("tictocMsg");
        send(tictocMsg,"out");
    }
}

void Txc1::handleMessage(cMessage *msg){
    if(msg==event){
        send(tictocMsg,"out");
    }else{
        //将在.ned和.ini中定义的delayTime绑定
        simtime_t delay = par("delayTime");
        EV << "Message arrived, starting to wait " << delay << " secs...\n";
        tictocMsg = msg;
        scheduleAt(simTime()+delay,event);
    }
}

此外,还可以在handleMessage中模拟丢包的行为

if(uniform(0,1)<0.1){
    EV<<"\"Losing\" message\n";
    delete msg;
}

③.ini:定义parameter的随机生成方式

[General]
network = Tictoc1
seed-0-mt=532569 #或者任一32b的值,设置随机数种子,这样重运行多少次仿真都会得到一样的结果
Tictoc1.tic.delayTime = exponential(3s)
Tictoc1.toc.delayTime = truncnormal(3s,1s)

8. 超时、计时器

为了更进一步模拟网络协议,我们可以采用stop-and-wait仿真model,实现的场景为:tic和toc分别向对方发送一个消息,同时toc会有一定概率丢包,此时下tic需要重新发送。

两个特点:

1)介绍了bubble()方法,当toc丢包时,可以在前端用弹出框标识消息丢失

2)利用self-message实现timer的功能,其中定时器的取消=self-message的删除,即使用cancelEvent()方法。

9. 重传相同消息

在实际中,我们通常是保留一个原始包的备份,这样,当我们需要重传时就不用再创建新的包了。

具体方法:保留原始包并只发送它的备份。当toc的Ack到达时,我们再删除这个原始包。为了让这一过程在模型中可视化,我们为每个消息记录一个消息号。

为了避免handleMessage()函数过于庞大,我们将相关代码放在两个新的函数generateNewMessage()和sendCopyOf()中,并在handleMessage()中调用它们

由于我不需要这个功能,所以大家可以参考博客的 “Part3 - Enhancing the 2-node TicToc ⑨ 重传相同消息:tictoc9” 的章节,其中对NED, .cc, .ini都进行了详细的分析

 

posted @ 2025-11-04 22:16  碳酸钾K2CO3  阅读(2)  评论(0)    收藏  举报