通过前面几篇教程,大家应该能够成功跑出Demo并且可以通过修改应用层等代码实现自己的功能。但是大家肯定觉得很奇怪为什么通过OSMWebWizard
导出的明明是好看的彩色的地图,使用netedit
打开查看时建筑物也是彩色的,但是使用Veins仿真时所有建筑都变成了红色边框无填充的多边形。丑陋的展示,自己可以理解但是给别人演示的时候并不太方便。
这篇教程将从修改建筑颜色入手,讲讲SUMO的TraCI。
Color 首先我们实现建筑物涂色。
pol.xml 我们知道建筑物属性均在pol.xml
中定义,打开这个文件可以看到每一个poly
标签均为一个建筑,定义了
id type 类型,前面讲过在Veins中使用时需要修改成已经定义过的建筑类型。这里多讲一点,不同的建筑类型可以有不同的信号衰减效率,也可以进一步定义其他功能。目前仿真暂不涉及信号,因此把所有类型改成building,并将building的衰减均设为0 color 颜色,OSMWebWizard
导出的颜色大多为RGB,少数为颜色名称 fill 是否填色,1为填色 layer 叠层,由于之后讲解的TraCI中不提供这个参数的修改和查询,可以忽略 shape 顶点坐标 Structure 下一步就是要找到具体哪个模块涉及颜色载入的功能。那就需要弄清楚涉及的具体的控制流程。
一个简单的办法就是全局搜索。由于Veins中所有建筑都是红色,我们可以直接全局搜索red
即可。在OMNet++顶栏点击Search - Search即可打开全局搜索。结果中src
下有三个文件夹有搜索结果,简要分析一下。之前讲过obstacle与建筑相关,并且该文件夹中有一个ObstacleControl.cc
文件有搜索结果,可以从这里入手。
打开ObstacleControl.cc
首先查看initialize
初始化函数,可以发现最后有一个addFromXml
,显然这是一个从xml添加项目的函数。查看这个函数可以发现是从xml依次添加标签的函数,但是奇怪的是循环内通过if else
判断标签头,而pol.xml
中没有type
标签头,且他的注释中给了示例明显是config.xml
中的建筑类型的配置。可以合理猜测这个文件是从config.xml
中添加配置,再推一步config.xml
中也可以定义建筑物,并通过if (tag == "poly")
分支导入。
可以从pol.xml
复制一部分建筑物到config.xml
中,然后注释pol.xml
中的建筑物,发现是可以正常导入的。不过这种方式会使得config.xml
过于臃肿,既要定义建筑类型的衰减效率又要定义建筑本身,因此不推荐这种方式导入建筑。本路线不是本例的重点,但是大家可以在Branch-Color 中查看修改的代码。
addFromXml
函数下面有一个addFromTypeAndShape
函数,可以猜测这个是”手动”加载建筑。再次通过全局搜索查看哪个模块调用了addFromTypeAndShape
函数,可知是src/veins/modules/monility/traci/TraCIScenarioManager.cc
中的init_traci
函数中调用了。通过前面的学习可知TraCI
是SUMO提供的API可用于查询仿真结果,设置参数等等。
再看addFromTypeAndShape
函数,可知具体将建筑物添加到画布上是通过add
函数通过调用AnnotationManager
的drawPolygon
函数实现的。
打开 src/veins/modules/world/annotations/AnnotationManager.cc
可知drawPolygon
函数最终是通过show
函数调用traci命令来添加polygon,line以及point
Conclusion 以上就是整个控制流,总结一下就是。
Veins开始仿真的时候,初始化TraCI连接,查询SUMO中载入的polygon,然后通过obstacleControl调用AnnotationManager加载polygon。Annotation
就是注解,可以理解为画布上除了仿真以外的东西。
Enable Loading Color 其实刚才梳理流程时,大家应该能够发现ObstacleContol的add
函数在调用annotations->drawPolygon
函数时直接将颜色定义成red
,这是所有建筑为红色的原因。
综合理解以上流程,我们需要从三个方面实现颜色加载:
pol.xml
定义color以及fillTraCIScenarioManager.cc
中能够通过TraCI的命令获取pol.xml
中建筑的color和fill,并传递给obstacleControl
obstacleControl
将正确的color和fill传递给annotations->drawPolygon
annotations
的show
函数读取color和fill并通过TraCI的命令添加建筑pol.xml 正常导出的pol.xml
含有所有属性
TraCIScenarioManager.cc 参照其他代码可知,polygon的属性是通过commandInterface
获取polygon的属性。默认的src/veins/modules/mobility/traci/TraCICommandInterface.cc/h
中的Polygon没有定义color属性和getColor函数,我们可以如下定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // TraCICommandInterface.h class VEINS_API Polygon { public: Polygon(TraCICommandInterface* traci, std::string polyId) : traci(traci) , polyId(polyId) { connection = &traci->connection; } TraCIColor getColor(); bool getFilled(); std::string getTypeId(); std::list<Coord> getShape(); void setShape(const std::list<Coord>& points); void remove(int32_t layer); ......
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 // TraCICommandInterface.cc TraCIColor TraCICommandInterface::Polygon::getColor() { TraCIColor res(0, 0, 0, 0); TraCIBuffer p; p << static_cast<uint8_t>(VAR_COLOR); p << polyId; TraCIBuffer buf = connection->query(CMD_GET_POLYGON_VARIABLE, p); uint8_t cmdLength; buf >> cmdLength; if (cmdLength == 0) { uint32_t cmdLengthX; buf >> cmdLengthX; } uint8_t commandId_r; buf >> commandId_r; uint8_t responseId = RESPONSE_GET_POLYGON_VARIABLE; ASSERT(commandId_r == responseId); uint8_t varId; buf >> varId; uint8_t variableId = VAR_COLOR; ASSERT(varId == variableId); std::string objectId_r; buf >> objectId_r; std::string objectId = polyId; ASSERT(objectId_r == objectId); uint8_t resType_r; buf >> resType_r; uint8_t resultTypeId = TYPE_COLOR; ASSERT(resType_r == resultTypeId); buf >> res.red; buf >> res.green; buf >> res.blue; buf >> res.alpha; ASSERT(buf.eof()); return res; } bool TraCICommandInterface::Polygon::getFilled() { int filled; TraCIBuffer p; p << static_cast<uint8_t>(VAR_FILL); p << polyId; TraCIBuffer buf = connection->query(CMD_GET_POLYGON_VARIABLE, p); uint8_t cmdLength; buf >> cmdLength; if (cmdLength == 0) { uint32_t cmdLengthX; buf >> cmdLengthX; } uint8_t commandId_r; buf >> commandId_r; uint8_t responseId = RESPONSE_GET_POLYGON_VARIABLE; ASSERT(commandId_r == responseId); uint8_t varId; buf >> varId; uint8_t variableId = VAR_FILL; ASSERT(varId == variableId); std::string objectId_r; buf >> objectId_r; std::string objectId = polyId; ASSERT(objectId_r == objectId); uint8_t resType_r; buf >> resType_r; uint8_t resultTypeId = TYPE_INTEGER; ASSERT(resType_r == resultTypeId); buf >> filled; ASSERT(buf.eof()); if (filled == 1) { return true; } else { return false; } }
这个函数的实现主要通过修改TraCIColor TraCICommandInterface::Vehicle::getColor()
而来
其中大写常量均为命令,具体可以参考TraCI 。VAR_COLOR
中的VAR表示小车,但是polygon和vehicle操作获取颜色的命令相同,均为0x45
,所以可以直接使用。VAR_FILL
同理。getFilled
函数与getColor
函数大体相同,主要区别是回传的Buffer长度不同,用于接收的变量类型那个不同。具体可以参看TraCI-Polygon
现在可以修改TraCIScenarioManager.cc
中的代码来获取polygon的color和filled:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ObstacleControl* obstacles = ObstacleControlAccess().getIfExists(); if (obstacles) { { // get list of polygons std::list<std::string> ids = commandInterface->getPolygonIds(); for (std::list<std::string>::iterator i = ids.begin(); i != ids.end(); ++i) { std::string id = *i; std::string typeId = commandInterface->polygon(id).getTypeId(); if (!obstacles->isTypeSupported(typeId)) continue; std::list<Coord> coords = commandInterface->polygon(id).getShape(); std::vector<Coord> shape; std::copy(coords.begin(), coords.end(), std::back_inserter(shape)); TraCIColor color = commandInterface->polygon(id).getColor(); bool filled = commandInterface->polygon(id).getFilled(); obstacles->addFromTypeAndShape(id, typeId, color, filled, shape); } } }
obstacleControl 然后是修改obstacleControl.cc/h
中的addFromTypeAndShape
函数,使之接收新的参数
1 2 3 4 5 6 7 8 9 10 // obstacleControl.cc void ObstacleControl::addFromTypeAndShape(std::string id, std::string typeId, TraCIColor color, bool filled, std::vector<Coord> shape) { if (!isTypeSupported(typeId)) { throw cRuntimeError("Unsupported obstacle type: \"%s\"", typeId.c_str()); } Obstacle obs(id, typeId, color, filled, getAttenuationPerCut(typeId), getAttenuationPerMeter(typeId)); obs.setShape(shape); add(obs); }
还要修改add
函数,给annotations->drawPolygon
传递参数
1 2 3 4 5 6 7 8 9 10 11 void ObstacleControl::add(Obstacle obstacle) { Obstacle* o = new Obstacle(obstacle); obstacleOwner.emplace_back(o); // visualize using AnnotationManager if (annotations) o->visualRepresentation = annotations->drawPolygon(o->getShape(), o->getColor(), o->getFilled(), annotationGroup); cacheEntries.clear(); isBboxLookupDirty = true; }
同时还要修改src/modules/obstacle/Obstacle.cc/h
中的Obstacle构造函数,这里就不展示,可以自行摸索或者参考Branch-Color
annotations annotations
的show
函数中主要修改if (const Polygon* p = dynamic_cast<const Polygon*>(annotation))
分支:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ASSERT(p->coords.size() >= 2); if (hasGUI()) { cPolygonFigure* figure = new cPolygonFigure(); std::vector<cFigure::Point> points; for (std::list<Coord>::const_iterator i = p->coords.begin(); i != p->coords.end(); ++i) { points.push_back(cFigure::Point(i->x, i->y)); } TraCIColor color = p->color; figure->setLineColor(cFigure::Color(color.red, color.green, color.blue)); figure->setPoints(points); figure->setFillColor(cFigure::Color(color.red, color.green, color.blue)); figure->setFilled(p->filled); annotation->figure = figure; annotationLayer->addFigure(annotation->figure); } TraCIScenarioManager* traci = TraCIScenarioManagerAccess().get(); if (traci && traci->isConnected()) {\ std::stringstream nameBuilder; nameBuilder << "Annotation" << getEnvir()->getUniqueNumber(); traci->getCommandInterface()->addPolygon(nameBuilder.str(), "Annotation", p->color, false, 4, p->coords); annotation->traciPolygonsIds.push_back(nameBuilder.str()); }
其中p->color
和p->filled
不太符合OOP,读者可以自行修改。
以上只展示了部分重要部分的修改,还有很多定义需要修改,主要是需要接收新的color和filled参数,读者可以参照编译报错来逐个排查/
全部改完,编译成功之后,仿真时应该能够正确显示pol.xml
中定义的颜色。如下所示,彩色很美。
Background Color 画布底色默认为灰色,我们可以改成白色。
底色由Scenario
的displayString
控制,默认为灰色,可以改成
1 2 3 4 5 6 7 network Scenario { parameters: double playgroundSizeX @unit(m); // x size of the area the nodes are in (in meters) double playgroundSizeY @unit(m); // y size of the area the nodes are in (in meters) double playgroundSizeZ @unit(m); // z size of the area the nodes are in (in meters) @display("bgb=$playgroundSizeX,$playgroundSizeY,white");