1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2012-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    Vehicle.cpp
11 /// @author  Jakob Erdmann
12 /// @date    15.03.2017
13 /// @version $Id$
14 ///
15 // C++ Vehicle API
16 /****************************************************************************/
17 
18 // ===========================================================================
19 // included modules
20 // ===========================================================================
21 #include <config.h>
22 
23 #include <utils/geom/GeomHelper.h>
24 #include <utils/common/StringTokenizer.h>
25 #include <utils/common/StringUtils.h>
26 #include <utils/gui/globjects/GUIGlObjectTypes.h>
27 #include <utils/emissions/PollutantsInterface.h>
28 #include <utils/vehicle/SUMOVehicleParserHelper.h>
29 #include <microsim/traffic_lights/MSTrafficLightLogic.h>
30 #include <microsim/lcmodels/MSAbstractLaneChangeModel.h>
31 #include <microsim/devices/MSDevice.h>
32 #include <microsim/MSEdgeWeightsStorage.h>
33 #include <microsim/MSVehicle.h>
34 #include <microsim/MSVehicleControl.h>
35 #include <microsim/MSVehicleType.h>
36 #include <microsim/MSInsertionControl.h>
37 #include <microsim/MSNet.h>
38 #include <microsim/MSEdge.h>
39 #include <microsim/MSLane.h>
40 #include <microsim/MSParkingArea.h>
41 #include <libsumo/TraCIDefs.h>
42 #include <libsumo/TraCIConstants.h>
43 #include "Helper.h"
44 #include "Route.h"
45 #include "Polygon.h"
46 #include "Vehicle.h"
47 
48 
49 // ===========================================================================
50 // debug defines
51 // ===========================================================================
52 //#define DEBUG_NEIGHBORS
53 //#define DEBUG_DYNAMIC_SHAPES
54 #define DEBUG_COND (veh->isSelected())
55 
56 
57 
58 namespace libsumo {
59 // ===========================================================================
60 // static member initializations
61 // ===========================================================================
62 SubscriptionResults Vehicle::mySubscriptionResults;
63 ContextSubscriptionResults Vehicle::myContextSubscriptionResults;
64 
65 
66 // ===========================================================================
67 // static member definitions
68 // ===========================================================================
69 MSVehicle*
getVehicle(const std::string & id)70 Vehicle::getVehicle(const std::string& id) {
71     SUMOVehicle* sumoVehicle = MSNet::getInstance()->getVehicleControl().getVehicle(id);
72     if (sumoVehicle == nullptr) {
73         throw TraCIException("Vehicle '" + id + "' is not known");
74     }
75     MSVehicle* v = dynamic_cast<MSVehicle*>(sumoVehicle);
76     if (v == nullptr) {
77         throw TraCIException("Vehicle '" + id + "' is not a micro-simulation vehicle");
78     }
79     return v;
80 }
81 
82 
83 bool
isVisible(const SUMOVehicle * veh)84 Vehicle::isVisible(const SUMOVehicle* veh) {
85     return veh->isOnRoad() || veh->isParking() || veh->wasRemoteControlled();
86 }
87 
88 
89 bool
isOnInit(const std::string & vehicleID)90 Vehicle::isOnInit(const std::string& vehicleID) {
91     SUMOVehicle* sumoVehicle = MSNet::getInstance()->getVehicleControl().getVehicle(vehicleID);
92     return sumoVehicle == nullptr || sumoVehicle->getLane() == nullptr;
93 }
94 
95 std::vector<std::string>
getIDList()96 Vehicle::getIDList() {
97     std::vector<std::string> ids;
98     MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
99     for (MSVehicleControl::constVehIt i = c.loadedVehBegin(); i != c.loadedVehEnd(); ++i) {
100         if (isVisible((*i).second)) {
101             ids.push_back((*i).first);
102         }
103     }
104     return ids;
105 }
106 
107 int
getIDCount()108 Vehicle::getIDCount() {
109     return (int)getIDList().size();
110 }
111 
112 
113 double
getSpeed(const std::string & vehicleID)114 Vehicle::getSpeed(const std::string& vehicleID) {
115     MSVehicle* veh = getVehicle(vehicleID);
116     return isVisible(veh) ? veh->getSpeed() : INVALID_DOUBLE_VALUE;
117 }
118 
119 
120 double
getAcceleration(const std::string & vehicleID)121 Vehicle::getAcceleration(const std::string& vehicleID) {
122     MSVehicle* veh = getVehicle(vehicleID);
123     return isVisible(veh) ? veh->getAcceleration() : INVALID_DOUBLE_VALUE;
124 }
125 
126 
127 double
getSpeedWithoutTraCI(const std::string & vehicleID)128 Vehicle::getSpeedWithoutTraCI(const std::string& vehicleID) {
129     MSVehicle* veh = getVehicle(vehicleID);
130     return isVisible(veh) ? veh->getSpeedWithoutTraciInfluence() : INVALID_DOUBLE_VALUE;
131 }
132 
133 
134 TraCIPosition
getPosition(const std::string & vehicleID,const bool includeZ)135 Vehicle::getPosition(const std::string& vehicleID, const bool includeZ) {
136     MSVehicle* veh = getVehicle(vehicleID);
137     if (isVisible(veh)) {
138         return Helper::makeTraCIPosition(veh->getPosition(), includeZ);
139     }
140     return TraCIPosition();
141 }
142 
143 
144 TraCIPosition
getPosition3D(const std::string & vehicleID)145 Vehicle::getPosition3D(const std::string& vehicleID) {
146     return getPosition(vehicleID, true);
147 }
148 
149 
150 double
getAngle(const std::string & vehicleID)151 Vehicle::getAngle(const std::string& vehicleID) {
152     MSVehicle* veh = getVehicle(vehicleID);
153     return isVisible(veh) ? GeomHelper::naviDegree(veh->getAngle()) : INVALID_DOUBLE_VALUE;
154 }
155 
156 
157 double
getSlope(const std::string & vehicleID)158 Vehicle::getSlope(const std::string& vehicleID) {
159     MSVehicle* veh = getVehicle(vehicleID);
160     return veh->isOnRoad() ? veh->getSlope() : INVALID_DOUBLE_VALUE;
161 }
162 
163 
164 std::string
getRoadID(const std::string & vehicleID)165 Vehicle::getRoadID(const std::string& vehicleID) {
166     MSVehicle* veh = getVehicle(vehicleID);
167     return isVisible(veh) ? veh->getLane()->getEdge().getID() : "";
168 }
169 
170 
171 std::string
getLaneID(const std::string & vehicleID)172 Vehicle::getLaneID(const std::string& vehicleID) {
173     MSVehicle* veh = getVehicle(vehicleID);
174     return veh->isOnRoad() ? veh->getLane()->getID() : "";
175 }
176 
177 
178 int
getLaneIndex(const std::string & vehicleID)179 Vehicle::getLaneIndex(const std::string& vehicleID) {
180     MSVehicle* veh = getVehicle(vehicleID);
181     return veh->isOnRoad() ? veh->getLane()->getIndex() : INVALID_INT_VALUE;
182 }
183 
184 
185 std::string
getTypeID(const std::string & vehicleID)186 Vehicle::getTypeID(const std::string& vehicleID) {
187     return getVehicle(vehicleID)->getVehicleType().getID();
188 }
189 
190 
191 std::string
getRouteID(const std::string & vehicleID)192 Vehicle::getRouteID(const std::string& vehicleID) {
193     return getVehicle(vehicleID)->getRoute().getID();
194 }
195 
196 
197 int
getRouteIndex(const std::string & vehicleID)198 Vehicle::getRouteIndex(const std::string& vehicleID) {
199     MSVehicle* veh = getVehicle(vehicleID);
200     return veh->hasDeparted() ? veh->getRoutePosition() : INVALID_INT_VALUE;
201 }
202 
203 
204 TraCIColor
getColor(const std::string & vehicleID)205 Vehicle::getColor(const std::string& vehicleID) {
206     return Helper::makeTraCIColor(getVehicle(vehicleID)->getParameter().color);
207 }
208 
209 double
getLanePosition(const std::string & vehicleID)210 Vehicle::getLanePosition(const std::string& vehicleID) {
211     MSVehicle* veh = getVehicle(vehicleID);
212     return veh->isOnRoad() ? veh->getPositionOnLane() : INVALID_DOUBLE_VALUE;
213 }
214 
215 double
getLateralLanePosition(const std::string & vehicleID)216 Vehicle::getLateralLanePosition(const std::string& vehicleID) {
217     MSVehicle* veh = getVehicle(vehicleID);
218     return veh->isOnRoad() ? veh->getLateralPositionOnLane() : INVALID_DOUBLE_VALUE;
219 }
220 
221 double
getCO2Emission(const std::string & vehicleID)222 Vehicle::getCO2Emission(const std::string& vehicleID) {
223     MSVehicle* veh = getVehicle(vehicleID);
224     return isVisible(veh) ? veh->getCO2Emissions() : INVALID_DOUBLE_VALUE;
225 }
226 
227 double
getCOEmission(const std::string & vehicleID)228 Vehicle::getCOEmission(const std::string& vehicleID) {
229     MSVehicle* veh = getVehicle(vehicleID);
230     return isVisible(veh) ? veh->getCOEmissions() : INVALID_DOUBLE_VALUE;
231 }
232 
233 double
getHCEmission(const std::string & vehicleID)234 Vehicle::getHCEmission(const std::string& vehicleID) {
235     MSVehicle* veh = getVehicle(vehicleID);
236     return isVisible(veh) ? veh->getHCEmissions() : INVALID_DOUBLE_VALUE;
237 }
238 
239 double
getPMxEmission(const std::string & vehicleID)240 Vehicle::getPMxEmission(const std::string& vehicleID) {
241     MSVehicle* veh = getVehicle(vehicleID);
242     return isVisible(veh) ? veh->getPMxEmissions() : INVALID_DOUBLE_VALUE;
243 }
244 
245 double
getNOxEmission(const std::string & vehicleID)246 Vehicle::getNOxEmission(const std::string& vehicleID) {
247     MSVehicle* veh = getVehicle(vehicleID);
248     return isVisible(veh) ? veh->getNOxEmissions() : INVALID_DOUBLE_VALUE;
249 }
250 
251 double
getFuelConsumption(const std::string & vehicleID)252 Vehicle::getFuelConsumption(const std::string& vehicleID) {
253     MSVehicle* veh = getVehicle(vehicleID);
254     return isVisible(veh) ? veh->getFuelConsumption() : INVALID_DOUBLE_VALUE;
255 }
256 
257 double
getNoiseEmission(const std::string & vehicleID)258 Vehicle::getNoiseEmission(const std::string& vehicleID) {
259     MSVehicle* veh = getVehicle(vehicleID);
260     return isVisible(veh) ? veh->getHarmonoise_NoiseEmissions() : INVALID_DOUBLE_VALUE;
261 }
262 
263 double
getElectricityConsumption(const std::string & vehicleID)264 Vehicle::getElectricityConsumption(const std::string& vehicleID) {
265     MSVehicle* veh = getVehicle(vehicleID);
266     return isVisible(veh) ? veh->getElectricityConsumption() : INVALID_DOUBLE_VALUE;
267 }
268 
269 int
getPersonNumber(const std::string & vehicleID)270 Vehicle::getPersonNumber(const std::string& vehicleID) {
271     return getVehicle(vehicleID)->getPersonNumber();
272 }
273 
274 std::vector<std::string>
getPersonIDList(const std::string & vehicleID)275 Vehicle::getPersonIDList(const std::string& vehicleID) {
276     return getVehicle(vehicleID)->getPersonIDList();
277 }
278 
279 std::pair<std::string, double>
getLeader(const std::string & vehicleID,double dist)280 Vehicle::getLeader(const std::string& vehicleID, double dist) {
281     MSVehicle* veh = getVehicle(vehicleID);
282     if (veh->isOnRoad()) {
283         std::pair<const MSVehicle* const, double> leaderInfo = veh->getLeader(dist);
284         return std::make_pair(
285                    leaderInfo.first != nullptr ? leaderInfo.first->getID() : "",
286                    leaderInfo.second);
287     } else {
288         return std::make_pair("", -1);
289     }
290 }
291 
292 
293 double
getWaitingTime(const std::string & vehicleID)294 Vehicle::getWaitingTime(const std::string& vehicleID) {
295     return getVehicle(vehicleID)->getWaitingSeconds();
296 }
297 
298 
299 double
getAccumulatedWaitingTime(const std::string & vehicleID)300 Vehicle::getAccumulatedWaitingTime(const std::string& vehicleID) {
301     return getVehicle(vehicleID)->getAccumulatedWaitingSeconds();
302 }
303 
304 
305 double
getAdaptedTraveltime(const std::string & vehicleID,double time,const std::string & edgeID)306 Vehicle::getAdaptedTraveltime(const std::string& vehicleID, double time, const std::string& edgeID) {
307     MSEdge* edge = Helper::getEdge(edgeID);
308     double value = INVALID_DOUBLE_VALUE;
309     getVehicle(vehicleID)->getWeightsStorage().retrieveExistingTravelTime(edge, time, value);
310     return value;
311 }
312 
313 
314 double
getEffort(const std::string & vehicleID,double time,const std::string & edgeID)315 Vehicle::getEffort(const std::string& vehicleID, double time, const std::string& edgeID) {
316     MSEdge* edge = Helper::getEdge(edgeID);
317     double value = INVALID_DOUBLE_VALUE;
318     getVehicle(vehicleID)->getWeightsStorage().retrieveExistingEffort(edge, time, value);
319     return value;
320 }
321 
322 
323 bool
isRouteValid(const std::string & vehicleID)324 Vehicle::isRouteValid(const std::string& vehicleID) {
325     std::string msg;
326     return getVehicle(vehicleID)->hasValidRoute(msg);
327 }
328 
329 
330 std::vector<std::string>
getRoute(const std::string & vehicleID)331 Vehicle::getRoute(const std::string& vehicleID) {
332     std::vector<std::string> result;
333     MSVehicle* veh = getVehicle(vehicleID);
334     const MSRoute& r = veh->getRoute();
335     for (MSRouteIterator i = r.begin(); i != r.end(); ++i) {
336         result.push_back((*i)->getID());
337     }
338     return result;
339 }
340 
341 
342 int
getSignals(const std::string & vehicleID)343 Vehicle::getSignals(const std::string& vehicleID) {
344     return getVehicle(vehicleID)->getSignals();
345 }
346 
347 
348 std::vector<TraCIBestLanesData>
getBestLanes(const std::string & vehicleID)349 Vehicle::getBestLanes(const std::string& vehicleID) {
350     std::vector<TraCIBestLanesData> result;
351     MSVehicle* veh = getVehicle(vehicleID);
352     if (veh->isOnRoad()) {
353         const std::vector<MSVehicle::LaneQ>& bestLanes = veh->getBestLanes();
354         for (std::vector<MSVehicle::LaneQ>::const_iterator i = bestLanes.begin(); i != bestLanes.end(); ++i) {
355             TraCIBestLanesData bld;
356             const MSVehicle::LaneQ& lq = *i;
357             bld.laneID = lq.lane->getID();
358             bld.length = lq.length;
359             bld.occupation = lq.nextOccupation;
360             bld.bestLaneOffset = lq.bestLaneOffset;
361             bld.allowsContinuation = lq.allowsContinuation;
362             for (std::vector<MSLane*>::const_iterator j = lq.bestContinuations.begin(); j != lq.bestContinuations.end(); ++j) {
363                 if ((*j) != nullptr) {
364                     bld.continuationLanes.push_back((*j)->getID());
365                 }
366             }
367             result.push_back(bld);
368         }
369     }
370     return result;
371 }
372 
373 
374 std::vector<TraCINextTLSData>
getNextTLS(const std::string & vehicleID)375 Vehicle::getNextTLS(const std::string& vehicleID) {
376     std::vector<TraCINextTLSData> result;
377     MSVehicle* veh = getVehicle(vehicleID);
378     if (veh->isOnRoad()) {
379         const MSLane* lane = veh->getLane();
380         const std::vector<MSLane*>& bestLaneConts = veh->getBestLanesContinuation(lane);
381         double seen = veh->getLane()->getLength() - veh->getPositionOnLane();
382         int view = 1;
383         MSLinkCont::const_iterator link = MSLane::succLinkSec(*veh, view, *lane, bestLaneConts);
384         while (!lane->isLinkEnd(link)) {
385             if (!lane->getEdge().isInternal()) {
386                 if ((*link)->isTLSControlled()) {
387                     TraCINextTLSData ntd;
388                     ntd.id = (*link)->getTLLogic()->getID();
389                     ntd.tlIndex = (*link)->getTLIndex();
390                     ntd.dist = seen;
391                     ntd.state = (char)(*link)->getState();
392                     result.push_back(ntd);
393                 }
394             }
395             lane = (*link)->getViaLaneOrLane();
396             if (!lane->getEdge().isInternal()) {
397                 view++;
398             }
399             seen += lane->getLength();
400             link = MSLane::succLinkSec(*veh, view, *lane, bestLaneConts);
401         }
402         // consider edges beyond bestLanes
403         const int remainingEdges = (int)(veh->getRoute().end() - veh->getCurrentRouteEdge()) - view;
404         //std::cout << "remainingEdges=" << remainingEdges << " view=" << view << " best=" << toString(bestLaneConts) << "\n";
405         for (int i = 0; i < remainingEdges; i++) {
406             const MSEdge* prev = *(veh->getCurrentRouteEdge() + view + i - 1);
407             const MSEdge* next = *(veh->getCurrentRouteEdge() + view + i);
408             const std::vector<MSLane*>* allowed = prev->allowedLanes(*next, veh->getVClass());
409             if (allowed != nullptr && allowed->size() != 0) {
410                 for (MSLink* link : allowed->front()->getLinkCont()) {
411                     if (&link->getLane()->getEdge() == next && link->isTLSControlled()) {
412                         TraCINextTLSData ntd;
413                         ntd.id = link->getTLLogic()->getID();
414                         ntd.tlIndex = link->getTLIndex();
415                         ntd.dist = seen;
416                         ntd.state = (char)link->getState();
417                         result.push_back(ntd);
418                     }
419                 }
420             } else {
421                 // invalid route, cannot determine nextTLS
422                 break;
423             }
424         }
425     }
426     return result;
427 }
428 
429 
430 std::vector<TraCINextStopData>
getNextStops(const std::string & vehicleID)431 Vehicle::getNextStops(const std::string& vehicleID) {
432     std::vector<TraCINextStopData> result;
433     MSVehicle* veh = getVehicle(vehicleID);
434     std::list<MSVehicle::Stop> stops = veh->getMyStops();
435     for (std::list<MSVehicle::Stop>::iterator it = stops.begin(); it != stops.end(); ++it) {
436         if (!it->reached && !it->collision) {
437             TraCINextStopData nsd;
438             nsd.lane = it->lane->getID();
439             nsd.endPos = it->getEndPos(*veh);
440             // all optionals, only one can be set
441             if (it->busstop != nullptr) {
442                 nsd.stoppingPlaceID = it->busstop->getID();
443             }
444             if (it->containerstop != nullptr) {
445                 nsd.stoppingPlaceID = it->containerstop->getID();
446             }
447             if (it->parkingarea != nullptr) {
448                 nsd.stoppingPlaceID = it->parkingarea->getID();
449             }
450             if (it->chargingStation != nullptr) {
451                 nsd.stoppingPlaceID = it->chargingStation->getID();
452             }
453             nsd.stopFlags = ((it->reached ? 1 : 0) +
454                              (it->pars.parking ? 2 : 0) +
455                              (it->pars.triggered ? 4 : 0) +
456                              (it->pars.containerTriggered ? 8 : 0) +
457                              (it->busstop != nullptr ? 16 : 0) +
458                              (it->containerstop != nullptr ? 32 : 0) +
459                              (it->chargingStation != nullptr ? 64 : 0) +
460                              (it->parkingarea != nullptr ? 128 : 0));
461             nsd.duration = STEPS2TIME(it->pars.duration);
462             nsd.until = STEPS2TIME(it->pars.until);
463             result.push_back(nsd);
464         }
465     }
466     return result;
467 }
468 
469 
470 int
getStopState(const std::string & vehicleID)471 Vehicle::getStopState(const std::string& vehicleID) {
472     MSVehicle* veh = getVehicle(vehicleID);
473     int result = 0;
474     if (veh->isStopped()) {
475         const MSVehicle::Stop& stop = veh->getNextStop();
476         result = ((stop.reached ? 1 : 0) +
477                   (stop.pars.parking ? 2 : 0) +
478                   (stop.pars.triggered ? 4 : 0) +
479                   (stop.pars.containerTriggered ? 8 : 0) +
480                   (stop.busstop != nullptr ? 16 : 0) +
481                   (stop.containerstop != nullptr ? 32 : 0) +
482                   (stop.chargingStation != nullptr ? 64 : 0) +
483                   (stop.parkingarea != nullptr ? 128 : 0));
484     }
485     return result;
486 }
487 
488 double
getDistance(const std::string & vehicleID)489 Vehicle::getDistance(const std::string& vehicleID) {
490     MSVehicle* veh = getVehicle(vehicleID);
491     if (veh->isOnRoad()) {
492         double distance;
493         if (veh->getLane()->isInternal()) {
494             // route edge still points to the edge before the intersection
495             const double normalEnd = (*veh->getCurrentRouteEdge())->getLength();
496             distance = (veh->getRoute().getDistanceBetween(veh->getDepartPos(), normalEnd,
497                         veh->getRoute().begin(), veh->getCurrentRouteEdge())
498                         + veh->getRoute().getDistanceBetween(normalEnd, veh->getPositionOnLane(),
499                                 *veh->getCurrentRouteEdge(), &veh->getLane()->getEdge()));
500         } else {
501             distance = veh->getRoute().getDistanceBetween(veh->getDepartPos(), veh->getPositionOnLane(),
502                        veh->getRoute().begin(), veh->getCurrentRouteEdge());
503         }
504         if (distance == std::numeric_limits<double>::max()) {
505             return INVALID_DOUBLE_VALUE;
506         } else {
507             return distance;
508         }
509     } else {
510         return INVALID_DOUBLE_VALUE;
511     }
512 }
513 
514 
515 double
getDrivingDistance(const std::string & vehicleID,const std::string & edgeID,double position,int)516 Vehicle::getDrivingDistance(const std::string& vehicleID, const std::string& edgeID, double position, int /* laneIndex */) {
517     MSVehicle* veh = getVehicle(vehicleID);
518     if (veh->isOnRoad()) {
519         double distance = veh->getRoute().getDistanceBetween(veh->getPositionOnLane(), position,
520                           &veh->getLane()->getEdge(), Helper::getEdge(edgeID), true, veh->getRoutePosition());
521         if (distance == std::numeric_limits<double>::max()) {
522             return INVALID_DOUBLE_VALUE;
523         }
524         return distance;
525     } else {
526         return INVALID_DOUBLE_VALUE;
527     }
528 }
529 
530 
531 double
getDrivingDistance2D(const std::string & vehicleID,double x,double y)532 Vehicle::getDrivingDistance2D(const std::string& vehicleID, double x, double y) {
533     MSVehicle* veh = getVehicle(vehicleID);
534     if (veh->isOnRoad()) {
535         std::pair<MSLane*, double> roadPos = Helper::convertCartesianToRoadMap(Position(x, y), veh->getVehicleType().getVehicleClass());
536         double distance = veh->getRoute().getDistanceBetween(veh->getPositionOnLane(), roadPos.second,
537                           veh->getEdge(), &roadPos.first->getEdge(), true, veh->getRoutePosition());
538         if (distance == std::numeric_limits<double>::max()) {
539             return INVALID_DOUBLE_VALUE;
540         }
541         return distance;
542     } else {
543         return INVALID_DOUBLE_VALUE;
544     }
545 }
546 
547 
548 
549 double
getAllowedSpeed(const std::string & vehicleID)550 Vehicle::getAllowedSpeed(const std::string& vehicleID) {
551     MSVehicle* veh = getVehicle(vehicleID);
552     if (veh->isOnRoad()) {
553         return veh->getLane()->getVehicleMaxSpeed(veh);
554     } else {
555         return INVALID_DOUBLE_VALUE;
556     }
557 }
558 
559 
560 double
getSpeedFactor(const std::string & vehicleID)561 Vehicle::getSpeedFactor(const std::string& vehicleID) {
562     return getVehicle(vehicleID)->getChosenSpeedFactor();
563 }
564 
565 
566 int
getSpeedMode(const std::string & vehicleID)567 Vehicle::getSpeedMode(const std::string& vehicleID) {
568     return getVehicle(vehicleID)->getInfluencer().getSpeedMode();
569 }
570 
571 int
getLaneChangeMode(const std::string & vehicleID)572 Vehicle::getLaneChangeMode(const std::string& vehicleID) {
573     return getVehicle(vehicleID)->getInfluencer().getLaneChangeMode();
574 }
575 
576 int
getRoutingMode(const std::string & vehicleID)577 Vehicle::getRoutingMode(const std::string& vehicleID) {
578     return getVehicle(vehicleID)->getInfluencer().getRoutingMode();
579 }
580 
581 std::string
getLine(const std::string & vehicleID)582 Vehicle::getLine(const std::string& vehicleID) {
583     return getVehicle(vehicleID)->getParameter().line;
584 }
585 
586 std::vector<std::string>
getVia(const std::string & vehicleID)587 Vehicle::getVia(const std::string& vehicleID) {
588     return getVehicle(vehicleID)->getParameter().via;
589 }
590 
591 
592 std::pair<int, int>
getLaneChangeState(const std::string & vehicleID,int direction)593 Vehicle::getLaneChangeState(const std::string& vehicleID, int direction) {
594     MSVehicle* veh = getVehicle(vehicleID);
595     if (veh->isOnRoad()) {
596         return veh->getLaneChangeModel().getSavedState(direction);
597     } else {
598         return std::make_pair((int)LCA_UNKNOWN, (int)LCA_UNKNOWN);
599     }
600 }
601 
602 
603 std::string
getParameter(const std::string & vehicleID,const std::string & key)604 Vehicle::getParameter(const std::string& vehicleID, const std::string& key) {
605     MSVehicle* veh = getVehicle(vehicleID);
606     if (StringUtils::startsWith(key, "device.")) {
607         StringTokenizer tok(key, ".");
608         if (tok.size() < 3) {
609             throw TraCIException("Invalid device parameter '" + key + "' for vehicle '" + vehicleID + "'");
610         }
611         try {
612             return veh->getDeviceParameter(tok.get(1), key.substr(tok.get(0).size() + tok.get(1).size() + 2));
613         } catch (InvalidArgument& e) {
614             throw TraCIException("Vehicle '" + vehicleID + "' does not support device parameter '" + key + "' (" + e.what() + ").");
615         }
616     } else if (StringUtils::startsWith(key, "laneChangeModel.")) {
617         const std::string attrName = key.substr(16);
618         try {
619             return veh->getLaneChangeModel().getParameter(attrName);
620         } catch (InvalidArgument& e) {
621             throw TraCIException("Vehicle '" + vehicleID + "' does not support laneChangeModel parameter '" + key + "' (" + e.what() + ").");
622         }
623     } else if (StringUtils::startsWith(key, "carFollowModel.")) {
624         const std::string attrName = key.substr(15);
625         try {
626             return veh->getCarFollowModel().getParameter(veh, attrName);
627         } catch (InvalidArgument& e) {
628             throw TraCIException("Vehicle '" + vehicleID + "' does not support carFollowModel parameter '" + key + "' (" + e.what() + ").");
629         }
630     } else if (StringUtils::startsWith(key, "has.") && StringUtils::endsWith(key, ".device")) {
631         StringTokenizer tok(key, ".");
632         if (tok.size() != 3) {
633             throw TraCIException("Invalid check for device. Expected format is 'has.DEVICENAME.device'");
634         }
635         return veh->hasDevice(tok.get(1)) ? "true" : "false";
636     } else {
637         return veh->getParameter().getParameter(key, "");
638     }
639 }
640 
641 
642 std::map<const MSVehicle*, double>
getNeighbors(const std::string & vehicleID,const int mode)643 Vehicle::getNeighbors(const std::string& vehicleID, const int mode) {
644     int dir = (1 & mode) != 0 ? -1 : 1;
645     bool queryLeaders = (2 & mode) != 0;
646     bool blockersOnly = (4 & mode) != 0;
647 
648     MSVehicle* veh = getVehicle(vehicleID);
649     std::map<const MSVehicle*, double> neighs;
650     auto& lcm = veh->getLaneChangeModel();
651 
652 #ifdef DEBUG_NEIGHBORS
653     if DEBUG_COND {
654     std::cout << "getNeighbors() for veh '" << vehicleID << "': dir=" << dir
655               << ", queryLeaders=" << queryLeaders
656               << ", blockersOnly=" << blockersOnly << std::endl;
657 }
658 #endif
659 
660 
661 
662 if (blockersOnly) {
663         // Check if a blocking neigh exists in the given direction
664         bool blocked = false;
665         if (dir == -1) {
666             if (queryLeaders) {
667                 blocked = (lcm.getOwnState() & LCA_BLOCKED_BY_RIGHT_LEADER) != 0;
668             } else {
669                 blocked = (lcm.getOwnState() & LCA_BLOCKED_BY_RIGHT_FOLLOWER) != 0;
670             }
671         } else {
672             if (queryLeaders) {
673                 blocked = (lcm.getOwnState() & LCA_BLOCKED_BY_LEFT_LEADER) != 0;
674             } else {
675                 blocked = (lcm.getOwnState() & LCA_BLOCKED_BY_LEFT_FOLLOWER) != 0;
676             }
677         }
678 
679 #ifdef DEBUG_NEIGHBORS
680         if DEBUG_COND {
681         std::cout << " blocked=" << blocked << std::endl;
682     }
683 #endif
684 
685     if (!blocked) {
686             // Not blocked => return empty vector
687             return neighs;
688         }
689     }
690 
691     const std::shared_ptr<MSLeaderDistanceInfo> res = queryLeaders ? lcm.getLeaders(dir) : lcm.getFollowers(dir);
692     if (res != nullptr && res->hasVehicles()) {
693         auto distIt = begin(res->getDistances());
694         auto vehIt = begin(res->getVehicles());
695         while (distIt != end(res->getDistances())) {
696             if (*vehIt != nullptr) {
697                 neighs[*vehIt] = *distIt;
698             }
699             ++vehIt;
700             ++distIt;
701         }
702     }
703     return neighs;
704 }
705 
706 
707 std::map<const MSVehicle*, double>
708 Vehicle::getRightFollowers(const std::string& vehicleID, bool blockingOnly) {
709     if (blockingOnly) {
710         return getNeighbors(vehicleID, 5);
711     } else {
712         return getNeighbors(vehicleID, 1);
713     }
714 }
715 
716 
717 std::map<const MSVehicle*, double>
718 Vehicle::getRightLeaders(const std::string& vehicleID, bool blockingOnly) {
719     if (blockingOnly) {
720         return getNeighbors(vehicleID, 7);
721     } else {
722         return getNeighbors(vehicleID, 3);
723     }
724 }
725 
726 
727 std::map<const MSVehicle*, double>
728 Vehicle::getLeftFollowers(const std::string& vehicleID, bool blockingOnly) {
729     if (blockingOnly) {
730         return getNeighbors(vehicleID, 4);
731     } else {
732         return getNeighbors(vehicleID, 0);
733     }
734 }
735 
736 
737 std::map<const MSVehicle*, double>
738 Vehicle::getLeftLeaders(const std::string& vehicleID, bool blockingOnly) {
739     if (blockingOnly) {
740         return getNeighbors(vehicleID, 6);
741     } else {
742         return getNeighbors(vehicleID, 2);
743     }
744 }
745 
746 
747 const MSVehicleType&
748 Vehicle::getVehicleType(const std::string& vehicleID) {
749     return getVehicle(vehicleID)->getVehicleType();
750 }
751 
752 
753 std::string
754 Vehicle::getEmissionClass(const std::string& vehicleID) {
755     return PollutantsInterface::getName(getVehicleType(vehicleID).getEmissionClass());
756 }
757 
758 std::string
759 Vehicle::getShapeClass(const std::string& vehicleID) {
760     return getVehicleShapeName(getVehicleType(vehicleID).getGuiShape());
761 }
762 
763 
764 double
765 Vehicle::getLength(const std::string& vehicleID) {
766     return getVehicleType(vehicleID).getLength();
767 }
768 
769 
770 double
771 Vehicle::getAccel(const std::string& vehicleID) {
772     return getVehicleType(vehicleID).getCarFollowModel().getMaxAccel();
773 }
774 
775 
776 double
777 Vehicle::getDecel(const std::string& vehicleID) {
778     return getVehicleType(vehicleID).getCarFollowModel().getMaxDecel();
779 }
780 
781 
782 double Vehicle::getEmergencyDecel(const std::string& vehicleID) {
783     return getVehicleType(vehicleID).getCarFollowModel().getEmergencyDecel();
784 }
785 
786 
787 double Vehicle::getApparentDecel(const std::string& vehicleID) {
788     return getVehicleType(vehicleID).getCarFollowModel().getApparentDecel();
789 }
790 
791 
792 double Vehicle::getActionStepLength(const std::string& vehicleID) {
793     return getVehicleType(vehicleID).getActionStepLengthSecs();
794 }
795 
796 
797 double Vehicle::getLastActionTime(const std::string& vehicleID) {
798     return STEPS2TIME(getVehicle(vehicleID)->getLastActionTime());
799 }
800 
801 
802 double
803 Vehicle::getTau(const std::string& vehicleID) {
804     return getVehicleType(vehicleID).getCarFollowModel().getHeadwayTime();
805 }
806 
807 
808 double
809 Vehicle::getImperfection(const std::string& vehicleID) {
810     return getVehicleType(vehicleID).getCarFollowModel().getImperfection();
811 }
812 
813 
814 double
815 Vehicle::getSpeedDeviation(const std::string& vehicleID) {
816     return getVehicleType(vehicleID).getSpeedFactor().getParameter()[1];
817 }
818 
819 
820 std::string
821 Vehicle::getVehicleClass(const std::string& vehicleID) {
822     return toString(getVehicleType(vehicleID).getVehicleClass());
823 }
824 
825 
826 double
827 Vehicle::getMinGap(const std::string& vehicleID) {
828     return getVehicleType(vehicleID).getMinGap();
829 }
830 
831 
832 double
833 Vehicle::getMinGapLat(const std::string& vehicleID) {
834     return getVehicleType(vehicleID).getMinGapLat();
835 }
836 
837 
838 double
839 Vehicle::getMaxSpeed(const std::string& vehicleID) {
840     return getVehicleType(vehicleID).getMaxSpeed();
841 }
842 
843 
844 double
845 Vehicle::getMaxSpeedLat(const std::string& vehicleID) {
846     return getVehicleType(vehicleID).getMaxSpeedLat();
847 }
848 
849 
850 std::string
851 Vehicle::getLateralAlignment(const std::string& vehicleID) {
852     return toString(getVehicleType(vehicleID).getPreferredLateralAlignment());
853 }
854 
855 
856 double
857 Vehicle::getWidth(const std::string& vehicleID) {
858     return getVehicleType(vehicleID).getWidth();
859 }
860 
861 
862 double
863 Vehicle::getHeight(const std::string& vehicleID) {
864     return getVehicleType(vehicleID).getHeight();
865 }
866 
867 
868 void
869 Vehicle::setStop(const std::string& vehicleID,
870                  const std::string& edgeID,
871                  double pos,
872                  int laneIndex,
873                  double duration,
874                  int flags,
875                  double startPos,
876                  double until) {
877     MSVehicle* veh = getVehicle(vehicleID);
878     // optional stop flags
879     bool parking = false;
880     bool triggered = false;
881     bool containerTriggered = false;
882     SumoXMLTag stoppingPlaceType = SUMO_TAG_NOTHING;
883 
884     parking = ((flags & 1) != 0);
885     triggered = ((flags & 2) != 0);
886     containerTriggered = ((flags & 4) != 0);
887     if ((flags & 8) != 0) {
888         stoppingPlaceType = SUMO_TAG_BUS_STOP;
889     }
890     if ((flags & 16) != 0) {
891         stoppingPlaceType = SUMO_TAG_CONTAINER_STOP;
892     }
893     if ((flags & 32) != 0) {
894         stoppingPlaceType = SUMO_TAG_CHARGING_STATION;
895     }
896     if ((flags & 64) != 0) {
897         stoppingPlaceType = SUMO_TAG_PARKING_AREA;
898     }
899     const SUMOTime durationSteps = duration == INVALID_DOUBLE_VALUE ? SUMOTime_MAX : TIME2STEPS(duration);
900     const SUMOTime untilStep = until == INVALID_DOUBLE_VALUE ? -1 : TIME2STEPS(until);
901 
902     std::string error;
903     if (stoppingPlaceType != SUMO_TAG_NOTHING) {
904         // Forward command to vehicle
905         if (!veh->addTraciStopAtStoppingPlace(edgeID, durationSteps, untilStep, parking, triggered, containerTriggered, stoppingPlaceType, error)) {
906             throw TraCIException(error);
907         }
908     } else {
909         // check
910         if (startPos == INVALID_DOUBLE_VALUE) {
911             startPos = pos - POSITION_EPS;
912         }
913         if (startPos < 0.) {
914             throw TraCIException("Position on lane must not be negative.");
915         }
916         if (pos < startPos) {
917             throw TraCIException("End position on lane must be after start position.");
918         }
919         // get the actual lane that is referenced by laneIndex
920         MSEdge* road = MSEdge::dictionary(edgeID);
921         if (road == nullptr) {
922             throw TraCIException("Unable to retrieve road with given id.");
923         }
924         const std::vector<MSLane*>& allLanes = road->getLanes();
925         if ((laneIndex < 0) || laneIndex >= (int)(allLanes.size())) {
926             throw TraCIException("No lane with index '" + toString(laneIndex) + "' on road '" + edgeID + "'.");
927         }
928         // Forward command to vehicle
929         if (!veh->addTraciStop(allLanes[laneIndex], startPos, pos, durationSteps, untilStep, parking, triggered, containerTriggered, error)) {
930             throw TraCIException(error);
931         }
932     }
933 }
934 
935 void
936 Vehicle::rerouteParkingArea(const std::string& vehicleID, const std::string& parkingAreaID) {
937     MSVehicle* veh = getVehicle(vehicleID);
938     std::string error;
939     // Forward command to vehicle
940     if (!veh->rerouteParkingArea(parkingAreaID, error)) {
941         throw TraCIException(error);
942     }
943 }
944 
945 void
946 Vehicle::resume(const std::string& vehicleID) {
947     MSVehicle* veh = getVehicle(vehicleID);
948     if (!veh->hasStops()) {
949         throw TraCIException("Failed to resume vehicle '" + veh->getID() + "', it has no stops.");
950     }
951     if (!veh->resumeFromStopping()) {
952         MSVehicle::Stop& sto = veh->getNextStop();
953         std::ostringstream strs;
954         strs << "reached: " << sto.reached;
955         strs << ", duration:" << sto.duration;
956         strs << ", edge:" << (*sto.edge)->getID();
957         strs << ", startPos: " << sto.pars.startPos;
958         std::string posStr = strs.str();
959         throw TraCIException("Failed to resume from stoppingfor vehicle '" + veh->getID() + "', " + posStr);
960     }
961 }
962 
963 
964 void
965 Vehicle::changeTarget(const std::string& vehicleID, const std::string& edgeID) {
966     MSVehicle* veh = getVehicle(vehicleID);
967     const MSEdge* destEdge = MSEdge::dictionary(edgeID);
968     const bool onInit = isOnInit(vehicleID);
969     if (destEdge == nullptr) {
970         throw TraCIException("Can not retrieve road with ID " + edgeID);
971     }
972     // build a new route between the vehicle's current edge and destination edge
973     ConstMSEdgeVector newRoute;
974     const MSEdge* currentEdge = veh->getRerouteOrigin();
975     veh->getInfluencer().getRouterTT().compute(
976         currentEdge, destEdge, (const MSVehicle * const)veh, MSNet::getInstance()->getCurrentTimeStep(), newRoute);
977     // replace the vehicle's route by the new one (cost is updated by call to reroute())
978     if (!veh->replaceRouteEdges(newRoute, -1, 0, "traci:changeTarget", onInit)) {
979         throw TraCIException("Route replacement failed for " + veh->getID());
980     }
981     // route again to ensure usage of via/stops
982     try {
983         veh->reroute(MSNet::getInstance()->getCurrentTimeStep(), "traci:changeTarget", veh->getInfluencer().getRouterTT(), onInit);
984     } catch (ProcessError& e) {
985         throw TraCIException(e.what());
986     }
987 }
988 
989 
990 void
991 Vehicle::changeLane(const std::string& vehicleID, int laneIndex, double duration) {
992     std::vector<std::pair<SUMOTime, int> > laneTimeLine;
993     laneTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep(), laneIndex));
994     laneTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep() + TIME2STEPS(duration), laneIndex));
995     getVehicle(vehicleID)->getInfluencer().setLaneTimeLine(laneTimeLine);
996 }
997 
998 void
999 Vehicle::changeLaneRelative(const std::string& vehicleID, int indexOffset, double duration) {
1000     std::vector<std::pair<SUMOTime, int> > laneTimeLine;
1001     int laneIndex = getVehicle(vehicleID)->getLaneIndex() + indexOffset;
1002     laneTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep(), laneIndex));
1003     laneTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep() + TIME2STEPS(duration), laneIndex));
1004     getVehicle(vehicleID)->getInfluencer().setLaneTimeLine(laneTimeLine);
1005 }
1006 
1007 
1008 void
1009 Vehicle::changeSublane(const std::string& vehicleID, double latDist) {
1010     getVehicle(vehicleID)->getInfluencer().setSublaneChange(latDist);
1011 }
1012 
1013 
1014 void
1015 Vehicle::add(const std::string& vehicleID,
1016              const std::string& routeID,
1017              const std::string& typeID,
1018              const std::string& depart,
1019              const std::string& departLane,
1020              const std::string& departPos,
1021              const std::string& departSpeed,
1022              const std::string& arrivalLane,
1023              const std::string& arrivalPos,
1024              const std::string& arrivalSpeed,
1025              const std::string& fromTaz,
1026              const std::string& toTaz,
1027              const std::string& line,
1028              int /*personCapacity*/,
1029              int personNumber) {
1030     SUMOVehicle* veh = MSNet::getInstance()->getVehicleControl().getVehicle(vehicleID);
1031     if (veh != nullptr) {
1032         throw TraCIException("The vehicle " + vehicleID + " to add already exists.");
1033     }
1034 
1035     SUMOVehicleParameter vehicleParams;
1036     vehicleParams.id = vehicleID;
1037     MSVehicleType* vehicleType = MSNet::getInstance()->getVehicleControl().getVType(typeID);
1038     if (!vehicleType) {
1039         throw TraCIException("Invalid type '" + typeID + "' for vehicle '" + vehicleID + "'");
1040     }
1041     const MSRoute* route = MSRoute::dictionary(routeID);
1042     if (!route) {
1043         if (routeID == "") {
1044             // assume, route was intentionally left blank because the caller
1045             // intends to control the vehicle remotely
1046             SUMOVehicleClass vclass = vehicleType->getVehicleClass();
1047             const std::string dummyRouteID = "DUMMY_ROUTE_" + SumoVehicleClassStrings.getString(vclass);
1048             route = MSRoute::dictionary(dummyRouteID);
1049             if (route == nullptr) {
1050                 for (MSEdge* e : MSEdge::getAllEdges()) {
1051                     if (e->getFunction() == EDGEFUNC_NORMAL && (e->getPermissions() & vclass) == vclass) {
1052                         std::vector<std::string>  edges;
1053                         edges.push_back(e->getID());
1054                         libsumo::Route::add(dummyRouteID, edges);
1055                         break;
1056                     }
1057                 }
1058             }
1059             route = MSRoute::dictionary(dummyRouteID);
1060             if (!route) {
1061                 throw TraCIException("Could not build dummy route for vehicle class: '" + SumoVehicleClassStrings.getString(vehicleType->getVehicleClass()) + "'");
1062             }
1063         } else {
1064             throw TraCIException("Invalid route '" + routeID + "' for vehicle: '" + vehicleID + "'");
1065         }
1066     }
1067     // check if the route implies a trip
1068     if (route->getEdges().size() == 2) {
1069         const MSEdgeVector& succ = route->getEdges().front()->getSuccessors();
1070         if (std::find(succ.begin(), succ.end(), route->getEdges().back()) == succ.end()) {
1071             vehicleParams.parametersSet |= VEHPARS_FORCE_REROUTE;
1072         }
1073     }
1074     if (fromTaz != "" || toTaz != "") {
1075         vehicleParams.parametersSet |= VEHPARS_FORCE_REROUTE;
1076     }
1077     std::string error;
1078     if (!SUMOVehicleParameter::parseDepart(depart, "vehicle", vehicleID, vehicleParams.depart, vehicleParams.departProcedure, error)) {
1079         throw TraCIException(error);
1080     }
1081     if (vehicleParams.departProcedure == DEPART_GIVEN && vehicleParams.depart < MSNet::getInstance()->getCurrentTimeStep()) {
1082         vehicleParams.depart = MSNet::getInstance()->getCurrentTimeStep();
1083         WRITE_WARNING("Departure time for vehicle '" + vehicleID + "' is in the past; using current time instead.");
1084     } else if (vehicleParams.departProcedure == DEPART_NOW) {
1085         vehicleParams.depart = MSNet::getInstance()->getCurrentTimeStep();
1086     }
1087     if (!SUMOVehicleParameter::parseDepartLane(departLane, "vehicle", vehicleID, vehicleParams.departLane, vehicleParams.departLaneProcedure, error)) {
1088         throw TraCIException(error);
1089     }
1090     if (!SUMOVehicleParameter::parseDepartPos(departPos, "vehicle", vehicleID, vehicleParams.departPos, vehicleParams.departPosProcedure, error)) {
1091         throw TraCIException(error);
1092     }
1093     if (!SUMOVehicleParameter::parseDepartSpeed(departSpeed, "vehicle", vehicleID, vehicleParams.departSpeed, vehicleParams.departSpeedProcedure, error)) {
1094         throw TraCIException(error);
1095     }
1096     if (!SUMOVehicleParameter::parseArrivalLane(arrivalLane, "vehicle", vehicleID, vehicleParams.arrivalLane, vehicleParams.arrivalLaneProcedure, error)) {
1097         throw TraCIException(error);
1098     }
1099     if (!SUMOVehicleParameter::parseArrivalPos(arrivalPos, "vehicle", vehicleID, vehicleParams.arrivalPos, vehicleParams.arrivalPosProcedure, error)) {
1100         throw TraCIException(error);
1101     }
1102     if (!SUMOVehicleParameter::parseArrivalSpeed(arrivalSpeed, "vehicle", vehicleID, vehicleParams.arrivalSpeed, vehicleParams.arrivalSpeedProcedure, error)) {
1103         throw TraCIException(error);
1104     }
1105     vehicleParams.fromTaz = fromTaz;
1106     vehicleParams.toTaz = toTaz;
1107     vehicleParams.line = line;
1108     //vehicleParams.personCapacity = personCapacity;
1109     vehicleParams.personNumber = personNumber;
1110 
1111     SUMOVehicleParameter* params = new SUMOVehicleParameter(vehicleParams);
1112     try {
1113         SUMOVehicle* vehicle = MSNet::getInstance()->getVehicleControl().buildVehicle(params, route, vehicleType, true, false);
1114         MSNet::getInstance()->getVehicleControl().addVehicle(vehicleParams.id, vehicle);
1115         MSNet::getInstance()->getInsertionControl().add(vehicle);
1116     } catch (ProcessError& e) {
1117         throw TraCIException(e.what());
1118     }
1119 }
1120 
1121 
1122 void
1123 Vehicle::moveToXY(const std::string& vehicleID, const std::string& edgeID, const int laneIndex, const double x, const double y, double angle, const int keepRoute) {
1124     MSVehicle* veh = getVehicle(vehicleID);
1125     const bool doKeepRoute = (keepRoute == 1) && veh->getID() != "VTD_EGO";
1126     const bool mayLeaveNetwork = (keepRoute == 2);
1127     // process
1128     const std::string origID = edgeID + "_" + toString(laneIndex);
1129     // @todo add an interpretation layer for OSM derived origID values (without lane index)
1130     Position pos(x, y);
1131 #ifdef DEBUG_MOVEXY
1132     const double origAngle = angle;
1133 #endif
1134     // angle must be in [0,360] because it will be compared against those returned by naviDegree()
1135     // angle set to INVALID_DOUBLE_VALUE is ignored in the evaluated and later set to the angle of the matched lane
1136     if (angle != INVALID_DOUBLE_VALUE) {
1137         while (angle >= 360.) {
1138             angle -= 360.;
1139         }
1140         while (angle < 0.) {
1141             angle += 360.;
1142         }
1143     }
1144 
1145     Position vehPos = veh->getPosition();
1146 #ifdef DEBUG_MOVEXY
1147     std::cout << std::endl << "begin vehicle " << veh->getID() << " vehPos:" << vehPos << " lane:" << Named::getIDSecure(veh->getLane()) << std::endl;
1148     std::cout << " want pos:" << pos << " origID:" << origID << " laneIndex:" << laneIndex << " origAngle:" << origAngle << " angle:" << angle << " keepRoute:" << keepRoute << std::endl;
1149 #endif
1150 
1151     ConstMSEdgeVector edges;
1152     MSLane* lane = nullptr;
1153     double lanePos;
1154     double lanePosLat = 0;
1155     double bestDistance = std::numeric_limits<double>::max();
1156     int routeOffset = 0;
1157     bool found;
1158     double maxRouteDistance = 100;
1159     /* EGO vehicle is known to have a fixed route. @todo make this into a parameter of the TraCI call */
1160     if (doKeepRoute) {
1161         // case a): vehicle is on its earlier route
1162         //  we additionally assume it is moving forward (SUMO-limit);
1163         //  note that the route ("edges") is not changed in this case
1164 
1165         found = Helper::moveToXYMap_matchingRoutePosition(pos, origID,
1166                 veh->getRoute().getEdges(), (int)(veh->getCurrentRouteEdge() - veh->getRoute().begin()),
1167                 bestDistance, &lane, lanePos, routeOffset);
1168         // @note silenty ignoring mapping failure
1169     } else {
1170         double speed = pos.distanceTo2D(veh->getPosition()); // !!!veh->getSpeed();
1171         found = Helper::moveToXYMap(pos, maxRouteDistance, mayLeaveNetwork, origID, angle,
1172                                     speed, veh->getRoute().getEdges(), veh->getRoutePosition(), veh->getLane(), veh->getPositionOnLane(), veh->isOnRoad(),
1173                                     bestDistance, &lane, lanePos, routeOffset, edges);
1174     }
1175     if ((found && bestDistance <= maxRouteDistance) || mayLeaveNetwork) {
1176         // optionally compute lateral offset
1177         pos.setz(veh->getPosition().z());
1178         if (found && (MSGlobals::gLateralResolution > 0 || mayLeaveNetwork)) {
1179             const double perpDist = lane->getShape().distance2D(pos, false);
1180             if (perpDist != GeomHelper::INVALID_OFFSET) {
1181                 lanePosLat = perpDist;
1182                 if (!mayLeaveNetwork) {
1183                     lanePosLat = MIN2(lanePosLat, 0.5 * (lane->getWidth() + veh->getVehicleType().getWidth() - MSGlobals::gLateralResolution));
1184                 }
1185                 // figure out whether the offset is to the left or to the right
1186                 PositionVector tmp = lane->getShape();
1187                 try {
1188                     tmp.move2side(-lanePosLat); // moved to left
1189                 } catch (ProcessError&) {
1190                     WRITE_WARNING("Could not determine position on lane '" + lane->getID() + " at lateral position " + toString(-lanePosLat) + ".");
1191                 }
1192                 //std::cout << " lane=" << lane->getID() << " posLat=" << lanePosLat << " shape=" << lane->getShape() << " tmp=" << tmp << " tmpDist=" << tmp.distance2D(pos) << "\n";
1193                 if (tmp.distance2D(pos) > perpDist) {
1194                     lanePosLat = -lanePosLat;
1195                 }
1196             }
1197             pos.setz(lane->geometryPositionAtOffset(lanePos).z());
1198         }
1199         if (found && !mayLeaveNetwork && MSGlobals::gLateralResolution < 0) {
1200             // mapped position may differ from pos
1201             pos = lane->geometryPositionAtOffset(lanePos, -lanePosLat);
1202         }
1203         assert((found && lane != 0) || (!found && lane == 0));
1204         if (angle == INVALID_DOUBLE_VALUE) {
1205             if (lane != nullptr) {
1206                 angle = GeomHelper::naviDegree(lane->getShape().rotationAtOffset(lanePos));
1207             } else {
1208                 // compute angle outside road network from old and new position
1209                 angle = GeomHelper::naviDegree(veh->getPosition().angleTo2D(pos));
1210             }
1211         }
1212         // use the best we have
1213         Helper::setRemoteControlled(veh, pos, lane, lanePos, lanePosLat, angle, routeOffset, edges, MSNet::getInstance()->getCurrentTimeStep());
1214         if (!veh->isOnRoad()) {
1215             MSNet::getInstance()->getInsertionControl().alreadyDeparted(veh);
1216         }
1217     } else {
1218         if (lane == nullptr) {
1219             throw TraCIException("Could not map vehicle '" + vehicleID + "' no road found within " + toString(maxRouteDistance) + "m.");
1220         } else {
1221             throw TraCIException("Could not map vehicle '" + vehicleID + "' distance to road is " + toString(bestDistance) + ".");
1222         }
1223     }
1224 }
1225 
1226 void
1227 Vehicle::slowDown(const std::string& vehicleID, double speed, double duration) {
1228     MSVehicle* veh = getVehicle(vehicleID);
1229     std::vector<std::pair<SUMOTime, double> > speedTimeLine;
1230     speedTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep(), veh->getSpeed()));
1231     speedTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep() + TIME2STEPS(duration), speed));
1232     veh->getInfluencer().setSpeedTimeLine(speedTimeLine);
1233 }
1234 
1235 void
1236 Vehicle::openGap(const std::string& vehicleID, double newTimeHeadway, double newSpaceHeadway, double duration, double changeRate, double maxDecel, const std::string& referenceVehID) {
1237     MSVehicle* veh = getVehicle(vehicleID);
1238     MSVehicle* refVeh = nullptr;
1239     if (referenceVehID != "") {
1240         refVeh = getVehicle(referenceVehID);
1241     }
1242     const double originalTau = veh->getVehicleType().getCarFollowModel().getHeadwayTime();
1243     if (newTimeHeadway == -1) {
1244         newTimeHeadway = originalTau;
1245     }
1246     if (originalTau > newTimeHeadway) {
1247         WRITE_WARNING("Ignoring openGap(). New time headway must not be smaller than the original.");
1248         return;
1249     }
1250     veh->getInfluencer().activateGapController(originalTau, newTimeHeadway, newSpaceHeadway, duration, changeRate, maxDecel, refVeh);
1251 }
1252 
1253 void
1254 Vehicle::deactivateGapControl(const std::string& vehicleID) {
1255     MSVehicle* veh = getVehicle(vehicleID);
1256     if (veh->hasInfluencer()) {
1257         veh->getInfluencer().deactivateGapController();
1258     }
1259 }
1260 
1261 void
1262 Vehicle::requestToC(const std::string& vehID, double leadTime) {
1263     setParameter(vehID, "device.toc.requestToC", toString(leadTime));
1264 }
1265 
1266 void
1267 Vehicle::setSpeed(const std::string& vehicleID, double speed) {
1268     MSVehicle* veh = getVehicle(vehicleID);
1269     std::vector<std::pair<SUMOTime, double> > speedTimeLine;
1270     if (speed >= 0) {
1271         speedTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep(), speed));
1272         speedTimeLine.push_back(std::make_pair(SUMOTime_MAX - DELTA_T, speed));
1273     }
1274     veh->getInfluencer().setSpeedTimeLine(speedTimeLine);
1275 }
1276 
1277 void
1278 Vehicle::setSpeedMode(const std::string& vehicleID, int speedMode) {
1279     getVehicle(vehicleID)->getInfluencer().setSpeedMode(speedMode);
1280 }
1281 
1282 void
1283 Vehicle::setLaneChangeMode(const std::string& vehicleID, int laneChangeMode) {
1284     getVehicle(vehicleID)->getInfluencer().setLaneChangeMode(laneChangeMode);
1285 }
1286 
1287 void
1288 Vehicle::setRoutingMode(const std::string& vehicleID, int routingMode) {
1289     getVehicle(vehicleID)->getInfluencer().setRoutingMode(routingMode);
1290 }
1291 
1292 void
1293 Vehicle::setType(const std::string& vehicleID, const std::string& typeID) {
1294     MSVehicleType* vehicleType = MSNet::getInstance()->getVehicleControl().getVType(typeID);
1295     if (vehicleType == nullptr) {
1296         throw TraCIException("Vehicle type '" + typeID + "' is not known");
1297     }
1298     getVehicle(vehicleID)->replaceVehicleType(vehicleType);
1299 }
1300 
1301 void
1302 Vehicle::setRouteID(const std::string& vehicleID, const std::string& routeID) {
1303     MSVehicle* veh = getVehicle(vehicleID);
1304     const MSRoute* r = MSRoute::dictionary(routeID);
1305     if (r == nullptr) {
1306         throw TraCIException("The route '" + routeID + "' is not known.");
1307     }
1308     std::string msg;
1309     if (!veh->hasValidRoute(msg, r)) {
1310         WRITE_WARNING("Invalid route replacement for vehicle '" + veh->getID() + "'. " + msg);
1311         if (MSGlobals::gCheckRoutes) {
1312             throw TraCIException("Route replacement failed for " + veh->getID());
1313         }
1314     }
1315 
1316     if (!veh->replaceRoute(r, "traci:setRouteID", veh->getLane() == nullptr)) {
1317         throw TraCIException("Route replacement failed for " + veh->getID());
1318     }
1319 }
1320 
1321 void
1322 Vehicle::setRoute(const std::string& vehicleID, const std::vector<std::string>& edgeIDs) {
1323     MSVehicle* veh = getVehicle(vehicleID);
1324     ConstMSEdgeVector edges;
1325     try {
1326         MSEdge::parseEdgesList(edgeIDs, edges, "<unknown>");
1327         if (edges.size() > 0 && edges.back()->isInternal()) {
1328             edges.push_back(edges.back()->getLanes()[0]->getNextNormal());
1329         }
1330     } catch (ProcessError& e) {
1331         throw TraCIException("Invalid edge list for vehicle '" + veh->getID() + "' (" + e.what() + ")");
1332     }
1333     if (!veh->replaceRouteEdges(edges, -1, 0, "traci:setRoute", veh->getLane() == nullptr, true)) {
1334         throw TraCIException("Route replacement failed for " + veh->getID());
1335     }
1336 }
1337 
1338 void
1339 Vehicle::updateBestLanes(const std::string& vehicleID) {
1340     MSVehicle* veh = getVehicle(vehicleID);
1341     veh->updateBestLanes(true);
1342 }
1343 
1344 
1345 void
1346 Vehicle::setAdaptedTraveltime(const std::string& vehicleID, const std::string& edgeID,
1347                               double time, double begSeconds, double endSeconds) {
1348     MSVehicle* veh = getVehicle(vehicleID);
1349     MSEdge* edge = MSEdge::dictionary(edgeID);
1350     if (edge == nullptr) {
1351         throw TraCIException("Referended edge '" + edgeID + "' is not known.");
1352     }
1353     if (time != INVALID_DOUBLE_VALUE) {
1354         // add time
1355         if (begSeconds == 0 && endSeconds == std::numeric_limits<double>::max()) {
1356             // clean up old values before setting whole range
1357             while (veh->getWeightsStorage().knowsTravelTime(edge)) {
1358                 veh->getWeightsStorage().removeTravelTime(edge);
1359             }
1360         }
1361         veh->getWeightsStorage().addTravelTime(edge, begSeconds, endSeconds, time);
1362     } else {
1363         // remove time
1364         while (veh->getWeightsStorage().knowsTravelTime(edge)) {
1365             veh->getWeightsStorage().removeTravelTime(edge);
1366         }
1367     }
1368 }
1369 
1370 
1371 void
1372 Vehicle::setEffort(const std::string& vehicleID, const std::string& edgeID,
1373                    double effort, double begSeconds, double endSeconds) {
1374     MSVehicle* veh = getVehicle(vehicleID);
1375     MSEdge* edge = MSEdge::dictionary(edgeID);
1376     if (edge == nullptr) {
1377         throw TraCIException("Referended edge '" + edgeID + "' is not known.");
1378     }
1379     if (effort != INVALID_DOUBLE_VALUE) {
1380         // add effort
1381         if (begSeconds == 0 && endSeconds == std::numeric_limits<double>::max()) {
1382             // clean up old values before setting whole range
1383             while (veh->getWeightsStorage().knowsEffort(edge)) {
1384                 veh->getWeightsStorage().removeEffort(edge);
1385             }
1386         }
1387         veh->getWeightsStorage().addEffort(edge, begSeconds, endSeconds, effort);
1388     } else {
1389         // remove effort
1390         while (veh->getWeightsStorage().knowsEffort(edge)) {
1391             veh->getWeightsStorage().removeEffort(edge);
1392         }
1393     }
1394 }
1395 
1396 
1397 void
1398 Vehicle::rerouteTraveltime(const std::string& vehicleID) {
1399     MSVehicle* veh = getVehicle(vehicleID);
1400     veh->reroute(MSNet::getInstance()->getCurrentTimeStep(), "traci:rerouteTraveltime",
1401                  veh->getInfluencer().getRouterTT(), isOnInit(vehicleID));
1402 }
1403 
1404 
1405 void
1406 Vehicle::rerouteEffort(const std::string& vehicleID) {
1407     MSVehicle* veh = getVehicle(vehicleID);
1408     veh->reroute(MSNet::getInstance()->getCurrentTimeStep(), "traci:rerouteEffort", MSNet::getInstance()->getRouterEffort(), isOnInit(vehicleID));
1409 }
1410 
1411 
1412 void
1413 Vehicle::setSignals(const std::string& vehicleID, int signals) {
1414     MSVehicle* veh = getVehicle(vehicleID);
1415     // set influencer to make the change persistent
1416     veh->getInfluencer().setSignals(signals);
1417     // set them now so that getSignals returns the correct value
1418     veh->switchOffSignal(0x0fffffff);
1419     if (signals >= 0) {
1420         veh->switchOnSignal(signals);
1421     }
1422 }
1423 
1424 
1425 void
1426 Vehicle::moveTo(const std::string& vehicleID, const std::string& laneID, double position) {
1427     MSVehicle* veh = getVehicle(vehicleID);
1428     MSLane* l = MSLane::dictionary(laneID);
1429     if (l == nullptr) {
1430         throw TraCIException("Unknown lane '" + laneID + "'.");
1431     }
1432     MSEdge& destinationEdge = l->getEdge();
1433     if (!veh->willPass(&destinationEdge)) {
1434         throw TraCIException("Vehicle '" + laneID + "' may be set onto an edge to pass only.");
1435     }
1436     veh->onRemovalFromNet(MSMoveReminder::NOTIFICATION_TELEPORT);
1437     if (veh->getLane() != nullptr) {
1438         veh->getLane()->removeVehicle(veh, MSMoveReminder::NOTIFICATION_TELEPORT);
1439     } else {
1440         veh->setTentativeLaneAndPosition(l, position);
1441     }
1442     while (veh->getEdge() != &destinationEdge) {
1443         const MSEdge* nextEdge = veh->succEdge(1);
1444         // let the vehicle move to the next edge
1445         if (veh->enterLaneAtMove(nextEdge->getLanes()[0], true)) {
1446             MSNet::getInstance()->getVehicleControl().scheduleVehicleRemoval(veh);
1447             continue;
1448         }
1449     }
1450     if (!veh->isOnRoad()) {
1451         MSNet::getInstance()->getInsertionControl().alreadyDeparted(veh);
1452 
1453     }
1454     l->forceVehicleInsertion(veh, position,
1455                              veh->hasDeparted() ? MSMoveReminder::NOTIFICATION_TELEPORT : MSMoveReminder::NOTIFICATION_DEPARTED);
1456 }
1457 
1458 
1459 void
1460 Vehicle::setActionStepLength(const std::string& vehicleID, double actionStepLength, bool resetActionOffset) {
1461     if (actionStepLength < 0.0) {
1462         WRITE_ERROR("Invalid action step length (<0). Ignoring command setActionStepLength().");
1463         return;
1464     }
1465     MSVehicle* veh = getVehicle(vehicleID);
1466     if (actionStepLength == 0.) {
1467         veh->resetActionOffset();
1468     } else {
1469         veh->setActionStepLength(actionStepLength, resetActionOffset);
1470     }
1471 }
1472 
1473 
1474 void
1475 Vehicle::remove(const std::string& vehicleID, char reason) {
1476     MSVehicle* veh = getVehicle(vehicleID);
1477     MSMoveReminder::Notification n = MSMoveReminder::NOTIFICATION_ARRIVED;
1478     switch (reason) {
1479         case REMOVE_TELEPORT:
1480             // XXX semantics unclear
1481             // n = MSMoveReminder::NOTIFICATION_TELEPORT;
1482             n = MSMoveReminder::NOTIFICATION_TELEPORT_ARRIVED;
1483             break;
1484         case REMOVE_PARKING:
1485             // XXX semantics unclear
1486             // n = MSMoveReminder::NOTIFICATION_PARKING;
1487             n = MSMoveReminder::NOTIFICATION_ARRIVED;
1488             break;
1489         case REMOVE_ARRIVED:
1490             n = MSMoveReminder::NOTIFICATION_ARRIVED;
1491             break;
1492         case REMOVE_VAPORIZED:
1493             n = MSMoveReminder::NOTIFICATION_VAPORIZED;
1494             break;
1495         case REMOVE_TELEPORT_ARRIVED:
1496             n = MSMoveReminder::NOTIFICATION_TELEPORT_ARRIVED;
1497             break;
1498         default:
1499             throw TraCIException("Unknown removal status.");
1500     }
1501     if (veh->hasDeparted()) {
1502         veh->onRemovalFromNet(n);
1503         if (veh->getLane() != nullptr) {
1504             veh->getLane()->removeVehicle(veh, n);
1505         }
1506         MSNet::getInstance()->getVehicleControl().scheduleVehicleRemoval(veh);
1507     } else {
1508         MSNet::getInstance()->getInsertionControl().alreadyDeparted(veh);
1509         MSNet::getInstance()->getVehicleControl().deleteVehicle(veh, true);
1510     }
1511 }
1512 
1513 
1514 void
1515 Vehicle::setColor(const std::string& vehicleID, const TraCIColor& col) {
1516     const SUMOVehicleParameter& p = getVehicle(vehicleID)->getParameter();
1517     p.color.set((unsigned char)col.r, (unsigned char)col.g, (unsigned char)col.b, (unsigned char)col.a);
1518     p.parametersSet |= VEHPARS_COLOR_SET;
1519 }
1520 
1521 
1522 void
1523 Vehicle::setSpeedFactor(const std::string& vehicleID, double factor) {
1524     getVehicle(vehicleID)->setChosenSpeedFactor(factor);
1525 }
1526 
1527 
1528 void
1529 Vehicle::setLine(const std::string& vehicleID, const std::string& line) {
1530     getVehicle(vehicleID)->getParameter().line = line;
1531 }
1532 
1533 
1534 void
1535 Vehicle::setVia(const std::string& vehicleID, const std::vector<std::string>& via) {
1536     MSVehicle* veh = getVehicle(vehicleID);
1537     try {
1538         // ensure edges exist
1539         ConstMSEdgeVector edges;
1540         MSEdge::parseEdgesList(via, edges, "<via-edges>");
1541     } catch (ProcessError& e) {
1542         throw TraCIException(e.what());
1543     }
1544     veh->getParameter().via = via;
1545 }
1546 
1547 
1548 void
1549 Vehicle::setLength(const std::string& vehicleID, double length) {
1550     getVehicle(vehicleID)->getSingularType().setLength(length);
1551 }
1552 
1553 
1554 void
1555 Vehicle::setMaxSpeed(const std::string& vehicleID, double speed) {
1556     getVehicle(vehicleID)->getSingularType().setMaxSpeed(speed);
1557 }
1558 
1559 
1560 void
1561 Vehicle::setVehicleClass(const std::string& vehicleID, const std::string& clazz) {
1562     getVehicle(vehicleID)->getSingularType().setVClass(getVehicleClassID(clazz));
1563 }
1564 
1565 
1566 void
1567 Vehicle::setShapeClass(const std::string& vehicleID, const std::string& clazz) {
1568     getVehicle(vehicleID)->getSingularType().setShape(getVehicleShapeID(clazz));
1569 }
1570 
1571 
1572 void
1573 Vehicle::setEmissionClass(const std::string& vehicleID, const std::string& clazz) {
1574     getVehicle(vehicleID)->getSingularType().setEmissionClass(PollutantsInterface::getClassByName(clazz));
1575 }
1576 
1577 
1578 void
1579 Vehicle::setWidth(const std::string& vehicleID, double width) {
1580     getVehicle(vehicleID)->getSingularType().setWidth(width);
1581 }
1582 
1583 
1584 void
1585 Vehicle::setHeight(const std::string& vehicleID, double height) {
1586     getVehicle(vehicleID)->getSingularType().setHeight(height);
1587 }
1588 
1589 
1590 void
1591 Vehicle::setMinGap(const std::string& vehicleID, double minGap) {
1592     getVehicle(vehicleID)->getSingularType().setMinGap(minGap);
1593 }
1594 
1595 
1596 void
1597 Vehicle::setAccel(const std::string& vehicleID, double accel) {
1598     getVehicle(vehicleID)->getSingularType().setAccel(accel);
1599 }
1600 
1601 
1602 void
1603 Vehicle::setDecel(const std::string& vehicleID, double decel) {
1604     VehicleType::setDecel(getVehicle(vehicleID)->getSingularType().getID(), decel);
1605 }
1606 
1607 
1608 void
1609 Vehicle::setEmergencyDecel(const std::string& vehicleID, double decel) {
1610     VehicleType::setEmergencyDecel(getVehicle(vehicleID)->getSingularType().getID(), decel);
1611 }
1612 
1613 
1614 void
1615 Vehicle::setApparentDecel(const std::string& vehicleID, double decel) {
1616     getVehicle(vehicleID)->getSingularType().setApparentDecel(decel);
1617 }
1618 
1619 
1620 void
1621 Vehicle::setImperfection(const std::string& vehicleID, double imperfection) {
1622     getVehicle(vehicleID)->getSingularType().setImperfection(imperfection);
1623 }
1624 
1625 
1626 void
1627 Vehicle::setTau(const std::string& vehicleID, double tau) {
1628     getVehicle(vehicleID)->getSingularType().setTau(tau);
1629 }
1630 
1631 
1632 void
1633 Vehicle::setMinGapLat(const std::string& vehicleID, double minGapLat) {
1634     getVehicle(vehicleID)->getSingularType().setMinGapLat(minGapLat);
1635 }
1636 
1637 
1638 void
1639 Vehicle::setMaxSpeedLat(const std::string& vehicleID, double speed) {
1640     getVehicle(vehicleID)->getSingularType().setMaxSpeedLat(speed);
1641 }
1642 
1643 
1644 void
1645 Vehicle::setLateralAlignment(const std::string& vehicleID, const std::string& latAlignment) {
1646     getVehicle(vehicleID)->getSingularType().setPreferredLateralAlignment(SUMOXMLDefinitions::LateralAlignments.get(latAlignment));
1647 }
1648 
1649 
1650 void
1651 Vehicle::setParameter(const std::string& vehicleID, const std::string& key, const std::string& value) {
1652     MSVehicle* veh = getVehicle(vehicleID);
1653     if (StringUtils::startsWith(key, "device.")) {
1654         StringTokenizer tok(key, ".");
1655         if (tok.size() < 3) {
1656             throw TraCIException("Invalid device parameter '" + key + "' for vehicle '" + vehicleID + "'");
1657         }
1658         try {
1659             veh->setDeviceParameter(tok.get(1), key.substr(tok.get(0).size() + tok.get(1).size() + 2), value);
1660         } catch (InvalidArgument& e) {
1661             throw TraCIException("Vehicle '" + vehicleID + "' does not support device parameter '" + key + "' (" + e.what() + ").");
1662         }
1663     } else if (StringUtils::startsWith(key, "laneChangeModel.")) {
1664         const std::string attrName = key.substr(16);
1665         try {
1666             veh->getLaneChangeModel().setParameter(attrName, value);
1667         } catch (InvalidArgument& e) {
1668             throw TraCIException("Vehicle '" + vehicleID + "' does not support laneChangeModel parameter '" + key + "' (" + e.what() + ").");
1669         }
1670     } else if (StringUtils::startsWith(key, "carFollowModel.")) {
1671         const std::string attrName = key.substr(15);
1672         try {
1673             veh->getCarFollowModel().setParameter(veh, attrName, value);
1674         } catch (InvalidArgument& e) {
1675             throw TraCIException("Vehicle '" + vehicleID + "' does not support carFollowModel parameter '" + key + "' (" + e.what() + ").");
1676         }
1677     } else if (StringUtils::startsWith(key, "has.") && StringUtils::endsWith(key, ".device")) {
1678         StringTokenizer tok(key, ".");
1679         if (tok.size() != 3) {
1680             throw TraCIException("Invalid request for device status change. Expected format is 'has.DEVICENAME.device'");
1681         }
1682         const std::string deviceName = tok.get(1);
1683         bool create;
1684         try {
1685             create = StringUtils::toBool(value);
1686         } catch (BoolFormatException&) {
1687             throw TraCIException("Changing device status requires a 'true' or 'false'");
1688         }
1689         if (!create) {
1690             throw TraCIException("Device removal is not supported for device of type '" + deviceName + "'");
1691         }
1692         try {
1693             veh->createDevice(deviceName);
1694         } catch (InvalidArgument& e) {
1695             throw TraCIException("Cannot create vehicle device (" + std::string(e.what()) + ").");
1696         }
1697     } else {
1698         ((SUMOVehicleParameter&)veh->getParameter()).setParameter(key, value);
1699     }
1700 }
1701 
1702 
1703 void
1704 Vehicle::highlight(const std::string& vehicleID, const TraCIColor& col, double size, const int alphaMax, const double duration, const int type) {
1705     // NOTE: Code is duplicated in large parts in POI.cpp
1706     MSVehicle* veh = getVehicle(vehicleID);
1707 
1708     // Center of the highlight circle
1709     Position center = veh->getPosition();
1710     const double l2 = veh->getLength() * 0.5;
1711     center.sub(cos(veh->getAngle())*l2, sin(veh->getAngle())*l2);
1712     // Size of the highlight circle
1713     if (size <= 0) {
1714         size = veh->getLength() * 0.7;
1715     }
1716     // Make polygon shape
1717     const unsigned int nPoints = 34;
1718     const PositionVector circlePV = GeomHelper::makeRing(size, size + 1., center, nPoints);
1719     TraCIPositionVector circle = Helper::makeTraCIPositionVector(circlePV);
1720 
1721 #ifdef DEBUG_DYNAMIC_SHAPES
1722     std::cout << SIMTIME << " Vehicle::highlight() for vehicle '" << vehicleID << "'\n"
1723               << " circle: " << circlePV << std::endl;
1724 #endif
1725 
1726     // Find a free polygon id
1727     int i = 0;
1728     std::string polyID = veh->getID() + "_hl" + toString(i);
1729     while (Polygon::exists(polyID)) {
1730         polyID = veh->getID() + "_hl" + toString(++i);
1731     }
1732     // Line width
1733     double lw = 0.;
1734     // Layer
1735     double lyr = 0.;
1736     if (MSNet::getInstance()->isGUINet()) {
1737         lyr = GLO_VEHICLE + 0.01;
1738         lyr += (type + 1) / 257.;
1739     }
1740     // Make Polygon
1741     Polygon::addHighlightPolygon(vehicleID, type, polyID, circle, col, true, lw, "highlight", (int)lyr);
1742 
1743     // Animation time line
1744     double maxAttack = 1.0; // maximal fade-in time
1745     std::vector<double> timeSpan;
1746     if (duration > 0.) {
1747         timeSpan = {0, MIN2(maxAttack, duration / 3.), 2.*duration / 3., duration};
1748     }
1749     // Alpha time line
1750     std::vector<double> alphaSpan;
1751     if (alphaMax > 0.) {
1752         alphaSpan = {0., (double) alphaMax, (double)(alphaMax) / 3., 0.};
1753     }
1754     // Attach dynamics
1755     Polygon::addDynamics(polyID, vehicleID, timeSpan, alphaSpan, false, true);
1756 }
1757 
1758 
1759 LIBSUMO_SUBSCRIPTION_IMPLEMENTATION(Vehicle, VEHICLE)
1760 
1761 
1762 void
1763 Vehicle::subscribeLeader(const std::string& vehicleID, double /* dist */, double beginTime, double endTime) {
1764     // TODO handle dist correctly
1765     Vehicle::subscribe(vehicleID, std::vector<int>({libsumo::VAR_LEADER}), beginTime, endTime);
1766 }
1767 
1768 
1769 void
1770 Vehicle::storeShape(const std::string& id, PositionVector& shape) {
1771     shape.push_back(getVehicle(id)->getPosition());
1772 }
1773 
1774 
1775 std::shared_ptr<VariableWrapper>
1776 Vehicle::makeWrapper() {
1777     return std::make_shared<Helper::SubscriptionWrapper>(handleVariable, mySubscriptionResults, myContextSubscriptionResults);
1778 }
1779 
1780 
1781 bool
1782 Vehicle::handleVariable(const std::string& objID, const int variable, VariableWrapper* wrapper) {
1783     switch (variable) {
1784         case TRACI_ID_LIST:
1785             return wrapper->wrapStringList(objID, variable, getIDList());
1786         case ID_COUNT:
1787             return wrapper->wrapInt(objID, variable, getIDCount());
1788         case VAR_POSITION:
1789             return wrapper->wrapPosition(objID, variable, getPosition(objID));
1790         case VAR_POSITION3D:
1791             return wrapper->wrapPosition(objID, variable, getPosition(objID, true));
1792         case VAR_ANGLE:
1793             return wrapper->wrapDouble(objID, variable, getAngle(objID));
1794         case VAR_SPEED:
1795             return wrapper->wrapDouble(objID, variable, getSpeed(objID));
1796         case VAR_ROAD_ID:
1797             return wrapper->wrapString(objID, variable, getRoadID(objID));
1798         case VAR_SPEED_WITHOUT_TRACI:
1799             return wrapper->wrapDouble(objID, variable, getSpeedWithoutTraCI(objID));
1800         case VAR_SLOPE:
1801             return wrapper->wrapDouble(objID, variable, getSlope(objID));
1802         case VAR_LANE_ID:
1803             return wrapper->wrapString(objID, variable, getLaneID(objID));
1804         case VAR_LANE_INDEX:
1805             return wrapper->wrapInt(objID, variable, getLaneIndex(objID));
1806         case VAR_TYPE:
1807             return wrapper->wrapString(objID, variable, getTypeID(objID));
1808         case VAR_ROUTE_ID:
1809             return wrapper->wrapString(objID, variable, getRouteID(objID));
1810         case VAR_ROUTE_INDEX:
1811             return wrapper->wrapInt(objID, variable, getRouteIndex(objID));
1812         case VAR_COLOR:
1813             return wrapper->wrapColor(objID, variable, getColor(objID));
1814         case VAR_LANEPOSITION:
1815             return wrapper->wrapDouble(objID, variable, getLanePosition(objID));
1816         case VAR_LANEPOSITION_LAT:
1817             return wrapper->wrapDouble(objID, variable, getLateralLanePosition(objID));
1818         case VAR_CO2EMISSION:
1819             return wrapper->wrapDouble(objID, variable, getCO2Emission(objID));
1820         case VAR_COEMISSION:
1821             return wrapper->wrapDouble(objID, variable, getCOEmission(objID));
1822         case VAR_HCEMISSION:
1823             return wrapper->wrapDouble(objID, variable, getHCEmission(objID));
1824         case VAR_PMXEMISSION:
1825             return wrapper->wrapDouble(objID, variable, getPMxEmission(objID));
1826         case VAR_NOXEMISSION:
1827             return wrapper->wrapDouble(objID, variable, getNOxEmission(objID));
1828         case VAR_FUELCONSUMPTION:
1829             return wrapper->wrapDouble(objID, variable, getFuelConsumption(objID));
1830         case VAR_NOISEEMISSION:
1831             return wrapper->wrapDouble(objID, variable, getNoiseEmission(objID));
1832         case VAR_ELECTRICITYCONSUMPTION:
1833             return wrapper->wrapDouble(objID, variable, getElectricityConsumption(objID));
1834         case VAR_PERSON_NUMBER:
1835             return wrapper->wrapInt(objID, variable, getPersonNumber(objID));
1836         case LAST_STEP_PERSON_ID_LIST:
1837             return wrapper->wrapStringList(objID, variable, getPersonIDList(objID));
1838         case VAR_WAITING_TIME:
1839             return wrapper->wrapDouble(objID, variable, getWaitingTime(objID));
1840         case VAR_ACCUMULATED_WAITING_TIME:
1841             return wrapper->wrapDouble(objID, variable, getAccumulatedWaitingTime(objID));
1842         case VAR_ROUTE_VALID:
1843             return wrapper->wrapInt(objID, variable, isRouteValid(objID));
1844         case VAR_EDGES:
1845             return wrapper->wrapStringList(objID, variable, getRoute(objID));
1846         case VAR_SIGNALS:
1847             return wrapper->wrapInt(objID, variable, getSignals(objID));
1848         case VAR_STOPSTATE:
1849             return wrapper->wrapInt(objID, variable, getStopState(objID));
1850         case VAR_DISTANCE:
1851             return wrapper->wrapDouble(objID, variable, getDistance(objID));
1852         case VAR_ALLOWED_SPEED:
1853             return wrapper->wrapDouble(objID, variable, getAllowedSpeed(objID));
1854         case VAR_SPEED_FACTOR:
1855             return wrapper->wrapDouble(objID, variable, getSpeedFactor(objID));
1856         case VAR_SPEEDSETMODE:
1857             return wrapper->wrapInt(objID, variable, getSpeedMode(objID));
1858         case VAR_LANECHANGE_MODE:
1859             return wrapper->wrapInt(objID, variable, getLaneChangeMode(objID));
1860         case VAR_ROUTING_MODE:
1861             return wrapper->wrapInt(objID, variable, getRoutingMode(objID));
1862         case VAR_LINE:
1863             return wrapper->wrapString(objID, variable, getLine(objID));
1864         case VAR_VIA:
1865             return wrapper->wrapStringList(objID, variable, getVia(objID));
1866         case VAR_ACCELERATION:
1867             return wrapper->wrapDouble(objID, variable, getAcceleration(objID));
1868         case VAR_LASTACTIONTIME:
1869             return wrapper->wrapDouble(objID, variable, getLastActionTime(objID));
1870         case VAR_LEADER: {
1871             const auto& lead = getLeader(objID);
1872             TraCIRoadPosition rp;
1873             rp.edgeID = lead.first;
1874             rp.pos = lead.second;
1875             return wrapper->wrapRoadPosition(objID, variable, rp);
1876         }
1877         default:
1878             return false;
1879     }
1880 }
1881 
1882 
1883 }
1884 
1885 /****************************************************************************/
1886