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后也会‘回传’,造成混乱。