Veins教程07 - Veins魔改应用层
Modification on Veins
这篇实现V2R通信,目标是RSU能知晓有多少小车可以通信,并借此深入了解Message,Application应用层和mac层的相关知识。讲解两个版本,第一个是,小车间歇发送报告消息,RSU接收并记录。第二个版本是RSU定时间歇发送询问信息,小车收到后应答,RSU接收并记录。
Version One
Message
首先自定义一个Message,在src/veins/modules/messages
下新建一个名为ReportMessage.msg
的文件,实现如下:
1 | cplusplus {{ |
ReportMessage继承了BaseFrame1609_4并包含两个属性分别是Coord
类型的发送者所标,以及L2Type
的发送者ID。编译之后同目录下生成同名.h
和.cc
文件。其函数实现暂时不做修改,默认的setter和getter足够使用。BaseFrame为最基础的消息帧,只包含几个基础属性,便于自定义。
接下来以TraCIDemoXX
-> DemoBaseApplLayer
的顺序修改项目
Car - TraCIDemo11p
接下来修改小车的实现文件使之满足要求。Veins中自带的src/veins/modules/application/traci/TraCIDemo11p.*
就是小车的简易实现,准确说是其应用层的实现,本篇只完成对其功能的修改来更好地理解Veins,之后如有需要可以自定义整个文件。
打开TraCIDemo11p.ned
可知其继承自DemoBaseApplLayer
,显然后者也是一个简易实现的应用层。
应用层的介绍可以参考Veins教程06 - Veins应用层详解
打开TraCIDemo11p.h
可知其只实现了onWSM
,onWSA
,handleSelfMsg
,handlePositionUpdate
四个函数。前两个是小车分别收到WSM
(WAVE Short Message)和WSA
(WAVE Service Advertisment)消息时作出的处理。
handlePositionUpdate
是每次小车更新位置时的函数处理。“小车更新位置”的时刻就是每次SUMO回传小车的仿真结果的时刻。
handleSelfMsg
其实是一个OMNet++的概念。通俗来说就是处理小车发送给自己的消息。小车给自己发送消息用到的是OMNet++提供的scheduleAt
函数,scheduleAt
函数相当于一个Timer函数,类似Node.js中的setTimeOut
,也就是在设定的时刻给自发送一个消息,并因此触发handleSelfMsg
。
在这个版本中,小车并不需要接收我们自定的ReportMessage
,因此我们只需要实现小车不断发送ReportMessage
的功能
Editing handlePositionUpdate
最简单的方式是在小车每次更新坐标时,也就是handlePositionUpdate
时发送。可以在该函数中的if
前加上:
1 | ReportMessage* rm = new ReportMessage(); |
记得include对应的文件
populateWSM
函数是一个统一处理消息的函数,简便起见,这里就直接使用了
curPosition
是DemoBaseApplLayer
中的成员变量,记录的是当前坐标
sendDown
函数是往下层也就是mac层传递消息,可以直接理解成发送。深入查看的话mac层会做一个消息类型的记录
if
内的功能是判断小车是否停止不动长达十秒,来一次判断是否有阻塞/车祸发生。如果有,则传递消息并reroute,如内置demo中演示的效果。
Set Timer
也可以在initialize
初始化函数中通过scheduleAt
函数设定一个Timer
需要注意的是,initialize
接受一个stage
参数,可以实现分阶段初始化,因此在修改initialize
时,需要避免在多个stage
的initialize
中重复设置。可以参考如下实现:
1 | if (stage == 1) { |
同时,由于scheduleAt
会在设定时刻给自己发送消息,还需要在handleSelfMsg
添加:
1 | else if (ReportMessage* rm = dynamic_cast<ReportMessage*>(msg)) { |
RSU - TraCIDemoRSU11p
这个版本中RSU只负责接收ReportMessage
,因此只需要添加onRM(ReportMessage* frame)
函数处理ReportMessage
消息。
不过由于需要统计可连接的小车数量,需要添加一个成员变量std::set<LAddress::L2Type> connectedNodes
L2Type
是veins/base/utils/SimpleAddress
中定义的地址(ID)类型。
TraCIDemoRSU11p.h
可以参考修改为:
1 | #pragma once |
对应的TraCIDemoRSU11p.cc
文件参考添加
1 | void TraCIDemoRSU11p::onRM(ReportMessage* frame) |
此时RSU已经可以接收并统计小车
不过如果想要输出,可以参考设置Timer
1 | void TraCIDemoRSU11p::initialize(int stage) |
DemoBaseApplLayer
主要需要修改:
- populateWSM: 可以新建一个
ReportMessage
分支,不过默认分支已经足够 - handleLowerMsg: 这个是接收消息的分发处理函数,需要新建一个
ReportMessage
分支,这样RSUc才能正确收到消息
参考如下修改
1 | void DemoBaseApplLayer::populateWSM(BaseFrame1609_4* wsm, LAddress::L2Type rcvId, int serial) |
其他有些不太重要的地方可以参考github代码
Version Two
主要的修改思路与Version One类似,RSU中设置Timer不断发送轮训消息,小车收到后回传给RSU。如果不想将发送和回传的消息分开,可以都使用ReportMessage
,但是需要设置一个类似于消息类型的变量。不然小车收到其他小车发送的回传ReportMessage
后也会‘回传’,造成混乱。