OMNet++ 例程学习 tictoc1~tictoc9

文档链接 https://docs.omnetpp.org/tutorials/tictoc/part3/

TicToc1

tictoc1.ned

// 简单模块,包含两个端口,一个输入,一个输出
simple Txc1{
	gates:
	    input in;
	    output out;
}
// 网络,包含两个子模块,这两个子模块都是Txc1的实例
// 通过connections定义子模块之间的连接关系
network Tictoc1{
	submodules:
	    tic: Txc1;
	    toc: Txc1;
	connections:
	    tic.out --> {delay = 100ms;} --> toc.in;
	    toc.out --> {delay = 100ms;} --> tic.in;		  				
}

txc1.cc

#include 
#include 

using namespace omnetpp;

class Txc1 : public cSimpleModule{
protected:
    virtual void initialize() override;
    virtual void handleMessage(cMessage *msg) override;
};
// 所有普通模块都要调用这个函数进行宏注册
Define_Module(Txc1);

// 初始化函数,在仿真开始运行一次,这里实现发送第一条消息
void Txc1::initialize(){
	// getName获取实例名称,也就是ned模块中实例名称
	// 字符串比较,tic发送第一条消息
    if(strcmp("tic", getName()) == 0){
    	// 创建消息,并通过该模块的out端口发送出去,字符串就是消息名称
        cMessage *msg = new cMessage("tictocMsg");
        send(msg, "out");
    }
}
// 当该类的对象接收到一个消息时开始执行handleMessage函数
void Txc1::handleMessage(cMessage *msg){
	// 任何一个模块收到消息之后再经过该模块的out端口发送出去
    send(msg, "out");
}

总结

所有simple模块都要继承cSimpleModule类,并重initialize()handleMessage()两个函数。
initialize(),初始化函数,在整个程序开始运行时执行一次。
handleMessage(),每次有消息到达该模块时,都会执行一次该函数。
messageevents都由cMessage对象表示

TicToc2

tictoc2.ned

这里主要是对ned模块中的设计样式进行了修改,基本连接关系不变

simple Txc2
{	
    parameters:
    	//给模块加上图片,直接对应安装目录image/block中的图片
        @display("i=block/routing");
    gates:
        input in;
        output out;
}

network Tictoc2
{
	// 绘制背景大小
    @display("bgb=300,300");
    submodules:
        tic: Txc2 {
            parameters:
            	// 修改icon的颜色,设置网络中结点的位置
                @display("i=,cyan;p=100,100");
        }
        toc: Txc2 {
            parameters:
                @display("i=,gold;p=200,200");
        }
    connections:
		tic.out --> {  delay = 100ms; } --> toc.in;
        tic.in <-- {  delay = 100ms; } <-- toc.out;
}

txc2.cc

增加了信息打印,逻辑功能没有变化

#include 
#include 

using namespace omnetpp;

class Txc2 : public cSimpleModule{
protected:
    virtual void initialize() override;
    virtual void handleMessage(cMessage *msg) override;
};

Define_Module(Txc2);

void Txc2::initialize(){
    if(strcmp("tic", getName()) == 0){
        // EV相当于C++中的cout
        // 可以使用std::cout<< <
        EV << "Sending initial message\n";
        cMessage *msg = new cMessage("tictocMsg");
        send(msg, "out");
    }
}
void Txc2::handleMessage(cMessage *msg){
    // msg->getName()消息的名字,在这里就是tictocMsg
    EV << "Received message `" << msg->getName() << "', sending it out again\n";
    send(msg, "out");
}

TicToc3

该案例TicToc2的基础上增加状态变量counter,每接收一次消息计数器减一,减到0之后消息删除,事件结束。
并增加观测事件WATCH(counter)

txc3.cc

#include 
#include 

using namespace omnetpp;

class Txc3 : public cSimpleModule{
private:
    int counter;	// 初始化私有变量
protected:
    virtual void initialize() override;
    virtual void handleMessage(cMessage *msg) override;
};

Define_Module(Txc3);

void Txc3::initialize(){
	// 赋值并观测
    counter = 10;
    WATCH(counter);

    if(strcmp("tic", getName()) == 0){
        EV << "Sending initial message\n";
        cMessage *msg = new cMessage("tictocMsg");
        send(msg, "out");
    }
}

void Txc3::handleMessage(cMessage *msg){
    counter--;
    // getName()打印出tic/toc
    // 计数器为0时,消息删除,仿真结束
    if(counter == 0){
        EV<<getName()<<"'s counter reached zero, deleting message\n";
        delete msg;
    } else{
        EV << getName() <<"'s counter is " << counter << ", sending back message\n";
        send(msg, "out");
    }
}

TicToc4

本案例主要研究了在模块中添加参数、初始化参数以及cc文件与ned文件传参三个问题。

tictoc4.ned

simple Txc4{
	parameters:
	    // 定义第一个变量,决定模块是否发送第一个消息,默认false
	    bool sendMsgOnInit = default(false);
	    // 定义接收消息限制值,默认设置为2
	    // 同时也可以在ini文件中进行初始化
	    int limit = default(2);
	    @display("i=block/routing");
	gates:
	    input in;
	    output out;
}
network Tictoc4{  
    submodules:
        tic: Txc4{
            parameters:
                sendMsgOnInit = true;		// 初始化发送第一个消息
                @display("i=,blue;p=100,100");
        }
        toc: Txc4{
        	parameters:
        	    sendMsgOnInit = false;
        	    @display("i=,yellow;p=200,200");                
        }
	connections:
	    tic.out --> {delay = 100ms;} --> toc.in;
	    toc.out --> {delay = 100ms;} --> tic.in;	    
}

txc4.cc

#include 
#include 
#include 

using namespace omnetpp;

class Txc4 : public cSimpleModule{
private:
    int counter;
protected:
    virtual void initialize() override;
    virtual void handleMessage(cMessage *msg) override;
};

Define_Module(Txc4);

void Txc4::initialize(){
	// counter不再直接赋值,而是通过par()进行传参
	// par()括号中的内容是ned模块中定义的变量,以字符串形式表示
    counter = par("limit");
	// 
    if(par("sendMsgOnInit").boolValue() == true){
        EV << "Sending initial message\n";
        cMessage *msg = new cMessage("tictocMsg");
        send(msg, "out");
    }
}

void Txc4::handleMessage(cMessage *msg){
    counter--;
	if(counter == 0){
        EV << getName() <<"'s counter reached zero, deleting message\n";
        delete msg;
    } else{
        EV << getName() <<"'s counter is " << counter << ", sending back message\n";
        send(msg, "out");
    }
}

关于赋值的总结

在模块中定义的变量总共有三种赋值方式,第一种是默认值,即使用default(),第二种是在生成实例时进行赋值,例如tictoc4.ned中的sendMsgOnInit = true,第三种是omnetpp.ini文件中进行赋值。三种赋值方式的优先级为第二种>第三种>第一种。
omnetpp.ini

Tictoc4.tic.limit = 3
Tictoc4.toc.limit = 5
// ini文件支持通配符,因此如果两个值赋值相同,可以改写成
Tictoc4.*.limit = 4
// 或者
Tictoc4.t*c.limit = 4
// 甚至可以是
**.limit = 4

TicToc5

在案例tictoc4的基础上使用了ned继承方式

tictoc.ned

simple Txc5{
    parameters:
        bool sendMsgOnInit = default(false);
        int limit = default(2);
        @display("i=block/routing");
    gates:
        input in;
        output out;
}
// Tic5和Txc5作为Txc5的派生,分别进行定义并进行参数赋值
simple Tic5 extends Txc5{
    parameters:
        sendMsgOnInit = true;
        @display("i=,green;p=200,200");
}
simple Toc5 extends Txc5{
    parameters:
        sendMsgOnInit = false;
        @display("i=,orange;p=100,100");
}
network Tictoc5{
    submodules:
        tic: Tic5;
        toc: Toc5;
	connections:
	    tic.in <-- {delay=100ms;} <-- toc.out;
	    toc.in <-- {delay=100ms;} <-- tic.out;
}

txc5.cc

逻辑功能与txc4.cc相同

TicToc6

案例tictoc6在于实现消息的延迟发送,即一个模块在接收到消息之后,延迟一定时长再发送给另一个模块。
在OMNet++中这样的延时是通过模块将消息发送给自身实现的。

tictoc6.ned文件参照tictoc2.ned

txc6.cc

#include 
#include 
#include 

using namespace omnetpp;

class Txc6 : public cSimpleModule{
private:
    cMessage *event;        //指向用来计时的对象
    cMessage *tictocMsg;    //记录收到的消息,直到将其发回
public:
  Txc6();                   //构造函数,在txc6的基础上构造函数
  virtual ~Txc6();          //析构函数,把构造的函数删除
protected:
    virtual void initialize() override;
    virtual void handleMessage(cMessage *msg) override;
};

Define_Module(Txc6);

Txc6::Txc6(){
    // 初始化为空指针
    event = tictocMsg = nullptr;
}

Txc6::~Txc6(){
    //删除已分配的自消息
    cancelAndDelete(event);     //把自消息event先取消再删除
    delete tictocMsg;           //直接删除
}

void Txc6::initialize(){
    event = new cMessage("event");
    tictocMsg = nullptr;    //初始化实际传输的对象、
    // 这里没有直接将消息发送出去,而是创建消息之后启用定时器,定时5s
    // scheduleAt(simtime_t t, cMessage *msg),指定时一段时间之后发送自消息
    if(strcmp("tic", getName()) == 0){
        EV <<"Scheduling first send to t=5.0s\n";
        tictocMsg = new cMessage("tictocMsg");
        scheduleAt(5.0, event);
    }
}
// 5s   tic收到自消息,然后将tictocMsg发送出去
// 5.1s toc收到tictocMsg!=event,执行else,保存收到的消息,定时1s,发送自消息
// 6.1s toc收到自消息 = event,将之前保存的消息发送出去,并清除消息
// 6.2s tic收到tictocMsg!=event,执行else,保存收到的消息,定时1s,发送自消息
void Txc6::handleMessage(cMessage *msg){
    if(msg == event){
        EV << "Wait period is over, sending back message\n";
        send(tictocMsg, "out");
        tictocMsg = nullptr;
    }else{
        EV <<"Message arrived, starting to wait 1 sec...\n";
        tictocMsg = msg;
        // simTime()当前仿真时间
        scheduleAt(simTime() + 1.0, event);
    }
}

TicToc7

案例tictoc7在前一个案例的基础上将延时时间由固定值1s修改为一个可以由ini文件设置的任意可变值,同时增加了丢包率的设计,当丢包发生时,消息删除,仿真结束

tictoc7.ned

simple Txc7
{
    parameters:
        @display("i=block/routing");
        //volatile可变的,@unit(s)属性修饰指明当前变量的单位,此处s表示单位为秒
     	volatile double delayTime @unit(s);		//设置延迟发送的时间变量
    gates:
        input in;
        output out;		
}

network Tictoc7
{
    @display("bgb=300,300");
    submodules:
        tic: Txc7 {
            parameters:
                @display("i=,green;p=200,200");
        }
        toc: Txc7 {
            parameters:
                @display("i=,red;p=100,100");
        }
    connections:
        tic.out --> {  delay = 100ms; } --> toc.in;
        toc.out --> {  delay = 100ms; } --> tic.in;
}

txc7.cc

#include 
#include 
#include 

using namespace omnetpp;


class Txc7 : public cSimpleModule{
private:
    cMessage *event;
    cMessage *tictocMsg;
public:
    Txc7();
    virtual ~Txc7();
protected:
    virtual void initialize() override;
    virtual void handleMessage(cMessage *msg) override;
};

Define_Module(Txc7);

Txc7::Txc7(){
    event = tictocMsg = nullptr;
}
Txc7::~Txc7(){
    cancelAndDelete(event);
    delete tictocMsg;
}
void Txc7::initialize(){

    event = new cMessage("event");
    tictocMsg = nullptr;

    if(strcmp("tic", getName()) == 0){
        EV <<"Scheduling first send to t=5.0s\n";
        scheduleAt(5.0, event);
        tictocMsg = new cMessage("tictocMsg");
    }
}
void Txc7::handleMessage(cMessage *msg){

    double p;

    if(msg == event){
        EV <<"Wait period is over, sending back message\n";
        send(tictocMsg, "out");
        tictocMsg = nullptr;
    }else{
        // 设置丢包率,0~1之间的一个随机数
        p = uniform(0,1);
        EV << "p = "<< p <<"\n";
        if(p < 0.1){
            EV <<"\"Losing\" message\n";
            delete msg;
        }
        else{
            // 获取ned模块中delayTime参数的值
            simtime_t delay = par("delayTime");
            EV <<"Message arrived, starting to wait " << delay <<" sec...\n";
            tictocMsg = msg;
            scheduleAt(simTime()+delay, event);
        }
    }
}

omnetpp.ini文件中增加:

[Config Tictoc7]
network = Tictoc7
// argument to exponential() is the mean; truncnormal() returns values from
// the normal distribution truncated to nonnegative values
Tictoc7.tic.delayTime = exponential(3s)
Tictoc7.toc.delayTime = truncnormal(3s,1s)

如果整个程序多运行几次会发现丢包时间总是在第14次发生,这是因为这个仿真软件的随机事件实际上是确定性算法,可以在ini文件中修改种子信息,测试得到不同结果:

[General]
seed-0-mt= 8  //or any other 32-bit value

TicToc8

案例tictoc8在前一个案例的基础上,设计了tic的重传功能,tic每次发送消息之后,会开启定时,定时完成意味着信息丢失,tic会启动重传机制。
在该案例中,tictoc的处理函数逻辑不同,因此应该分开定义。

tictou8.ned

simple Tic8{
    parameters:
        @display("i=block/routing");
    gates:
        input in;
        output out;
}
simple Toc8{
	parameters:
	    @display("i=block/process");
	gates:
		input in;
		output out;        
}
network Tictoc8{
	submodules:
	    tic: Tic8 {
            parameters:
                @display("i=,green;p=200,200");
        }
        toc: Toc8 {
            parameters:
                @display("i=,red;p=100,100");
        }
	connections:
        tic.out --> {  delay = 100ms; } --> toc.in;
        toc.out --> {  delay = 100ms; } --> tic.in;	    
}

txc8.cc

#include 
#include 
#include 

using namespace omnetpp;

class Tic8 : public cSimpleModule{

private:
    simtime_t timeout;          // 定义超时时间
    cMessage *timeoutEvent;     // 定义超时消息事件
public:
    Tic8();
    virtual ~Tic8();
protected:
    virtual void initialize() override;
    virtual void handleMessage(cMessage *msg) override;
};

Define_Module(Tic8);

Tic8::Tic8(){
    timeoutEvent = nullptr;
}

Tic8::~Tic8(){
    cancelAndDelete(timeoutEvent);
}

void Tic8::initialize(){
    // 初始化变量
    timeout = 1.0;
    timeoutEvent = new cMessage("timeoutEvent");
    EV << "Sending initial message\n";
    cMessage *msg = new cMessage("tictocMsg");
    //tic8->toc8
    send(msg, "out");
    //消息发送完成后,timeout时间后判断消息有没有接受到
    scheduleAt(simTime()+timeout, timeoutEvent);
}

void Tic8::handleMessage(cMessage *msg){

    if(msg == timeoutEvent){
        // 如果能收到超时事件消息,证明消息丢失,需要重新传输
        EV << "Timeout expired, resending message and restarting timer\n";
        cMessage *newMsg = new cMessage("tictocMsg");
        send(newMsg, "out");
        scheduleAt(simTime()+timeout, timeoutEvent);
    }else {
        // 没有收到超时事件消息,收到的是正常消息,取消超时事件消息
        EV << "Timer cancelled.\n";
        cancelEvent(timeoutEvent);
        delete msg;
        // 重新发送一个消息给toc8,并重新开始超时定时
        cMessage *newMsg = new cMessage("tictocMsg");
        send(newMsg, "out");
        scheduleAt(simTime()+timeout, timeoutEvent);
    }
}

class Toc8 : public cSimpleModule{

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

};

Define_Module(Toc8);

void Toc8::handleMessage(cMessage *msg)
{
    if (uniform(0, 1) < 0.1) {
        EV << "\"Losing\" message.\n";
        // 丢包之后,使用bubble()函数在仿真界面给用户提示
        bubble("message lost");
        delete msg;
    }
    else {
        EV << "Sending back same message as acknowledgement.\n";
        // toc8->tic8
        send(msg, "out");
    }
}

TicToc9

不同于案例tictoc8中每次消息重传都要重新创建消息,tictoc9 中重传的消息是拷贝的一份副本,当tic收到toc发回的应答信息时,才删除原始数据信息。

tictoc9.ned文件参照tictoc8.ned

txc9.cc

#include 
#include 
#include 

using namespace omnetpp;

class Tic9 : public cSimpleModule{
private:
    simtime_t timeout;
    cMessage *timeoutEvent;
    int seq; // 采用序列号进行验证
    cMessage *message;
public:
    Tic9();
    virtual ~Tic9();
protected:
    //为了避免handleMessage()过长,创建了两个新的函数
    virtual cMessage *generateNewMessage();         // 生成新的消息
    virtual void sendCopyOf(cMessage *msg);         // 发送拷贝
    virtual void initialize() override;
    virtual void handleMessage(cMessage *msg) override;
};

Define_Module(Tic9);

Tic9::Tic9()
{
    timeoutEvent = message = nullptr;
}

Tic9::~Tic9()
{
    cancelAndDelete(timeoutEvent);
    delete message;
}

void Tic9::initialize(){
    seq = 0;
    timeout = 1.0;
    timeoutEvent = new cMessage("timeoutEvent");
    EV << "Sending initial message.\n";
    // 调用新的函数生成事件消息
    message = generateNewMessage();
    // 拷贝副本并发送副本
    sendCopyOf(message);
    // 启动定时
    scheduleAt(simTime()+timeout, timeoutEvent);
}

void Tic9::handleMessage(cMessage *msg){
    // 超时,拷贝原始消息并发送副本
    if(msg == timeoutEvent){
        EV << "Timeout expired, resending message and restarting timer\n";
        sendCopyOf(message);
        scheduleAt(simTime()+timeout, timeoutEvent);
    } else{
        //收到应答消息"ack"
        EV << "Received: " << msg->getName() << "\n";
        delete msg;
        //删除定时,并删除原始消息
        EV << "Timer cancelled.\n";
        cancelEvent(timeoutEvent);
        delete message;

        // 重新发送新的消息
        message = generateNewMessage();
        sendCopyOf(message);
        scheduleAt(simTime()+timeout, timeoutEvent);
    }
}

cMessage *Tic9::generateNewMessage(){
    char msgname[20];
    // 利用seq记录当前发送的消息是第几条消息
    sprintf(msgname, "tic-%d", ++seq);
    cMessage *msg = new cMessage(msgname);
    return msg;
}

void Tic9::sendCopyOf(cMessage *msg){
    // 发送拷贝副本
    // dup函数还可用于多播或广播
    cMessage *copyMsg = (cMessage *)msg->dup();
    send(copyMsg,"out");
}


class Toc9 : public cSimpleModule{

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

Define_Module(Toc9);

void Toc9::handleMessage(cMessage *msg){

    if(uniform(0,1) < 0.1){
        EV << "\"Losing\" message.\n";
        bubble("message lost");
        delete msg;
    }else{
        EV << msg << " received, sending back an acknowledgement.\n";
        delete msg;
        // 成功收到消息,发送一个ack进行响应
        send(new cMessage("ack"), "out");
    }
}

你可能感兴趣的:(OMNet++,学习,网络,c++)