OMnet++初学者教学 第3节 丰富 双-节点 TicToc
3.1添加图标
在这里,我们使模型在GUI中看起来更漂亮。我们分配block/routing
图标(文件images/block/routing.png
),然后将其绘制为青色,将其绘制为tic
黄色toc
。这是通过将显示字符串添加到NED文件来实现的。i=
显示字符串中的标签指定图标。
simple Txc2
{
parameters:
@display("i=block/routing"); // 添加默认图标
gates:
input in;
output out;
}
//
// 让两个模块有着不一样的着色效果
// 使用青色表示“ tic”,使用金色表示“ toc”
//
network Tictoc2
{
submodules:
tic: Txc2 {
parameters:
@display("i=,cyan"); // 不改变图标,只改变颜色
}
toc: Txc2 {
parameters:
@display("i=,gold"); // 这也是
}
connections:
tic.out --> { delay = 100ms; } --> toc.in;
tic.in <-- { delay = 100ms; } <-- toc.out;
}
改变颜色后的结果
3.2添加日志
我们还修改了C ++代码。我们向其中添加日志语句,Txc1
以便打印出正在执行的操作。OMNeT ++提供了具有日志级别,日志通道,过滤等功能的复杂日志记录工具,这对于大型和复杂的模型很有用,但是在此模型中,我们将使用其最简单的形式EV
:
EV << "Sending initial message\n";
和
EV << "Received message `" << msg->getName() << "', sending it out again\n";
在OMNeT ++运行时环境中运行仿真时,日志窗口中将会输出以下信息:
您还可以通过右键单击_tic_和_toc_的图标并从菜单中选择“_Component log”_来打开单独的_tic_和_toc_输出窗口。当您拥有大型模型(“快速滚动日志综合症”)并且仅对特定模块的日志消息感兴趣时,此功能将非常有用。
来源:tictoc2.ned,txc2.cc,omnetpp.ini
3.3添加状态变量
在这一步中,我们向模块添加一个计数器,并在十次交换后删除该消息。
我们将计数器添加到类中
class Txc3 : public cSimpleModule
{
private:
int counter; // Note the counter here
protected:
我们在初始化方法initialize()
中初始化counter为10,然后在处理信息函数 handleMessage()
中减一,即每次消息到达时候减一。counter变成0的时候,仿真结束。
注意
WATCH(counter);
添加上面的语句可以在图形运行时环境中查看计数器值。
如果单击tic
的图标,则主窗口左下角的检查器窗口将显示有关的详细信息tic
。确保从顶部的工具栏中选择了children mode。检查器现在显示计数器变量。
在继续运行仿真时,您可以按照以下步骤进行操作:计数器不断递减直至达到零。
来源:tictoc3.ned,txc3.cc,omnetpp.ini
3.4添加参数
在这一步中,您将学习如何向模拟中添加输入参数:我们将“magic number(神奇的数字)” 10变成参数,并添加布尔值参数,以决定模块是否应在其初始化代码中发送第一条消息(无论是一个tic
还是一个toc
模块)。
模块参数必须在NED文件中声明。数据类型可以是数字,字符串,布尔值或xml(后者是为了轻松访问XML配置文件)等。
simple Txc4
{
parameters:
bool sendMsgOnInit = default(false); //决定模块初始化的时候是否要发送一条信息
int limit = default(2); // 具有默认值的另一个参数
@display("i=block/routing");
gates:
我们还必须修改C ++代码以读取中的参数 initialize()
,并将其分配给计数器。
counter = par("limit");// 猜测par应该是parameter的缩写,这里是吧limit的值赋值给变量limits
我们可以使用第二个参数来决定是否发送初始消息:
if (par("sendMsgOnInit").boolValue() == true) {}//如果parameters里面的sendMsgInit是true就发送信息
现在,我们可以在NED文件中或从omnetpp.ini
中赋值参数。NED文件中的赋值具有高优先权。如果使用NED文件中的default(...)
语法,则可以定义参数的默认值。在这种情况下,您可以在omnetpp.ini中设置参数的值,也可以使用NED文件提供的默认值。
在这里,我们在NED文件中分配一个参数:
network Tictoc4
{
submodules:
tic: Txc4 {
parameters:
sendMsgOnInit = true;
@display("i=,cyan");
}
toc: Txc4 {
parameters:
sendMsgOnInit = false;
@display("i=,gold");
}
connections:
另一个在omnetpp.ini
:
Tictoc4.toc.limit = 5
请注意,由于omnetpp.ini支持通配符,并且从NED文件分配的参数优先于omnetpp.ini中的参数,
我们可以使用
Tictoc4.t * c.limit = 5
或者
Tictoc4.*.limit=5
甚至
**.limit=5
实现相同的效果。(*
和**
之间的区别是,*
不匹配点.,而**
匹配
在图形运行环境中,您可以在主窗口左侧的对象树中,或在模块检查器的“参数”页面中检查模块参数(信息显示在主窗口的左下角点击一个模块)。
具有较小限制的模块将删除消息,从而结束仿真。
来源:tictoc4.ned,txc4.cc,omnetpp.ini
3.5使用NED继承特性
如果我们仔细看一下NED文件,我们将意识到这一点,tic
并且toc
仅在它们的参数值和它们的显示字符串上有所不同。我们可以通过从另一个继承并指定或重写其某些参数来创建新的简单模块类型。在我们的例子中,我们将得出两个简单的模块类型(Tic
和Toc
)。之后,我们可以在定义网络中的子模块时使用这些类型。
从现有的简单模块派生很容易。这是基本模块:
simple Txc5
{
parameters:
bool sendMsgOnInit = default(false);
int limit = default(2);
@display("i=block/routing");
gates:
input in;
output out;
}
这是子模块。我们只需要简单地指定参数值并添加一些显示属性即可。
simple Tic5 extends Txc5
{
parameters:
@display("i=,cyan");
sendMsgOnInit = true; // Tic modules should send a message on init
}
该Toc
模块看起来相似,但是参数值不同。
simple Toc5 extends Txc5
{
parameters:
@display("i=,gold");
sendMsgOnInit = false; // Toc modules should NOT send a message on init
}
笔记
C ++实现是从基本简单模块(Txc5
)继承的。
一旦创建了新的simple模块,就可以在网络中将它们用作子模块类型:
network Tictoc5
{
submodules:
tic: Tic5; //参数limit 在这依然没有赋值,我们会从ini文件中获取她
toc: Toc5;
connections:
如您所见,网络定义现在变得更短,更简单。继承使您可以在网络中使用通用类型,减少多余的定义和参数设置。
3.6 建模处理延迟
在以前的机型,tic
和toc
立即回发收到的消息。在这里,我们将添加一些时间:tic
和toc
将在发送消息回去之前模拟1秒的延迟。在OMNeT ++中,这种计时是通过模块向自身发送消息来实现的。这样的消息称为self-messages (这是由于它们的使用方式,否则它们也只是普通的消息对象)。
我们在类中添加了两个cMessage派生而来两个指针变量event
并tictocMsg
,用来计时模拟延迟使用的时间。
class Txc6 : public cSimpleModule
{
private:
cMessage *event; // 指向我们将用于计时的事件对象的指针
cMessage *tictocMsg; // 变量以记住消息,直到我们将其发送回去
public:
我们使用scheduleAt()函数“发送”self-messages,指定何时应将其传递回模块。
scheduleAt(simTime()+1.0, event);
在handleMessage()函数
,现在我们要区分新的消息是否是通过input gate还是self message回来了(计时器过期)。在这里我们正在使用
if (msg == event) {// 判断是否是当前对象
我们也还可以写以下代码也一样
if (msg->isSelfMessage())//判断是否是self messages
为了使源代码保持较小,我们省略了计数器。
当运行f仿真时,您将看到以下日志输出:
来源:tictoc6.ned,txc6.cc,omnetpp.ini
3.7随机数和参数
在这一步中,我们将介绍随机数。我们将延迟从1s更改为可以从NED文件或omnetpp.ini设置的随机值。模块参数能够返回随机变量;但是,要使用此功能,我们必须在handleMessage()
每次使用它时都读取该参数。
// 延时的参数可以在(tictoc7.ned, omnetpp.ini)中设置成"exponential(5)"
// 这样每次我们都会得到一个不同的延时
simtime_t delay = par("delayTime");
EV << "Message arrived, starting to wait " << delay << " secs...\n";
tictocMsg = msg;
另外,我们将以很小的(硬编码)概率“丢失”(删除)该数据包。
if (uniform(0, 1) < 0.1) {
EV << "\"Losing\" message\n";
delete msg;
}
我们将在omnetpp.ini中分配参数:
Tictoc7.tic.delayTime = exponential(3s)
Tictoc7.toc.delayTime = truncnormal(3s,1s)
您可以尝试不管重新运行模拟多少次(或重新启动_模拟_,_Simulate-> Rebuild network_菜单项),都将获得完全相同的结果。这是因为OMNeT ++使用确定性算法(默认情况下为Mersenne Twister RNG)来生成随机数,并将其初始化为相同的种子。这对于可重现的仿真很重要。如果将以下行添加到omnetpp.ini,则可以尝试使用其他种子:
[General]
seed-0-mt=532569 # or any other 32-bit value
从语法上,您可能已经猜到OMNeT ++支持多个RNG。没错,但是,本教程中的所有模型都使用RNG 0。
联系
也尝试其他发行版。
来源:tictoc7.ned,txc7.cc,omnetpp.ini
3.8 超时,取消计时器
为了建模网络协议更进一步,让我们将模型转换为停止等待仿真。这一次,我们将有单独的类tic
和toc
。基本方案与之前的方案类似:tic
并且toc
将彼此之间传递一条消息。但是,toc
将以某种非零概率“丢失”消息,在这种情况下,tic
将不得不重新发送该消息。
这是toc
的代码:
void Toc8::handleMessage(cMessage *msg)
{
if (uniform(0, 1) < 0.1) {
EV << "\"Losing\" message.\n";
bubble("message lost"); // 让动画更加丰富,弹出一个信息框...
delete msg;
}
else {
多亏bubble()
了代码中的调用,toc
每当丢包的时候,都会显示出来。
因此,tic
无论何时发送消息,都将启动一个计时器。当计时器到期时,我们将假定该消息已丢失并发送另一条消息。如果toc
的答复到来,则必须取消计时器。计时器将是(还有什么?)一个自发消息。
scheduleAt(simTime()+timeout, timeoutEvent);
取消计时器将在cancelEvent()函数
调用中完成。请注意,这不会阻止我们一遍又一遍地重用同一超时消息。
cancelEvent(timeoutEvent);
您可以在txc8.cc中阅读Tic的完整源代码
来源:tictoc8.ned,txc8.cc,omnetpp.ini
3.9重发同一条消息
在这一步中,我们将优化先前的模型。如果需要重传,因为我们在那里刚刚创建了另一个数据包,所以也可以,因为数据包中包含的内容很少。但是在现实生活中,保留原始数据包的副本通常更为实用,这样我们就可以重新发送它而无需再次构建它。保留指向已发送消息的指针(以便我们可以再次发送)可能会更加容易,但是当消息在另一个节点处销毁时,指针将变为无效。
我们在这里所做的是保留原始数据包,并仅发送其副本。收到toc
确认后,我们将删除原始文件。为了更容易直观地验证模型,我们将在消息名称中包含一个消息序列号。
为了避免handleMessage()
变得太大,我们将相应的代码放入两个新函数中,generateNewMessage()
然后sendCopyOf()
从中调用它们handleMessage()
。
功能:
cMessage *Tic9::generateNewMessage()
{
// Generate a message with a different name every time.
char msgname[20];
sprintf(msgname, "tic-%d", ++seq);
cMessage *msg = new cMessage(msgname);
return msg;
}
void Tic9::sendCopyOf(cMessage *msg)
{
// Duplicate message and send the copy.
cMessage *copy = (cMessage *)msg->dup();
send(copy, "out");
}
来源:tictoc9.ned,txc9.cc,omnetpp.ini
Omnet++学习资源太少,个人翻译不易。您的每一分钱都会给后来的开发者提供帮助!感谢支持。