1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-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    MSBaseVehicle.cpp
11 /// @author  Michael Behrisch
12 /// @author  Daniel Krajzewicz
13 /// @author  Jakob Erdmann
14 /// @date    Mon, 8 Nov 2010
15 /// @version $Id$
16 ///
17 // A base class for vehicle implementations
18 /****************************************************************************/
19 
20 
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25 
26 #include <iostream>
27 #include <cassert>
28 #include <utils/common/StdDefs.h>
29 #include <utils/common/MsgHandler.h>
30 #include <utils/options/OptionsCont.h>
31 #include <utils/iodevices/OutputDevice.h>
32 #include <microsim/pedestrians/MSPerson.h>
33 #include "MSGlobals.h"
34 #include "MSTransportable.h"
35 #include "MSVehicleControl.h"
36 #include "MSVehicleType.h"
37 #include "MSEdge.h"
38 #include "MSLane.h"
39 #include "MSMoveReminder.h"
40 #include "MSBaseVehicle.h"
41 #include "MSNet.h"
42 #include "devices/MSDevice.h"
43 #include "devices/MSDevice_Routing.h"
44 #include <microsim/devices/MSDevice_Transportable.h>
45 #include "MSInsertionControl.h"
46 
47 //#define DEBUG_REROUTE
48 //#define DEBUG_COND (getID() == "follower")
49 //#define DEBUG_COND (true)
50 #define DEBUG_COND (isSelected())
51 
52 // ===========================================================================
53 // static members
54 // ===========================================================================
55 const SUMOTime MSBaseVehicle::NOT_YET_DEPARTED = SUMOTime_MAX;
56 std::vector<MSTransportable*> MSBaseVehicle::myEmptyTransportableVector;
57 #ifdef _DEBUG
58 std::set<std::string> MSBaseVehicle::myShallTraceMoveReminders;
59 #endif
60 SUMOVehicle::NumericalID MSBaseVehicle::myCurrentNumericalIndex = 0;
61 
62 
63 // ===========================================================================
64 // method definitions
65 // ===========================================================================
66 
67 double
getPreviousSpeed() const68 MSBaseVehicle::getPreviousSpeed() const {
69     throw ProcessError("getPreviousSpeed() is not available for non-MSVehicles.");
70 }
71 
72 
MSBaseVehicle(SUMOVehicleParameter * pars,const MSRoute * route,MSVehicleType * type,const double speedFactor)73 MSBaseVehicle::MSBaseVehicle(SUMOVehicleParameter* pars, const MSRoute* route,
74                              MSVehicleType* type, const double speedFactor) :
75     myParameter(pars),
76     myRoute(route),
77     myType(type),
78     myCurrEdge(route->begin()),
79     myChosenSpeedFactor(speedFactor),
80     myMoveReminders(0),
81     myPersonDevice(nullptr),
82     myContainerDevice(nullptr),
83     myDeparture(NOT_YET_DEPARTED),
84     myDepartPos(-1),
85     myArrivalPos(-1),
86     myArrivalLane(-1),
87     myNumberReroutes(0),
88     myNumericalID(myCurrentNumericalIndex++)
89 #ifdef _DEBUG
90     , myTraceMoveReminders(myShallTraceMoveReminders.count(pars->id) > 0)
91 #endif
92 {
93     if ((*myRoute->begin())->isTazConnector() || myRoute->getLastEdge()->isTazConnector()) {
94         pars->parametersSet |= VEHPARS_FORCE_REROUTE;
95     }
96     myRoute->addReference();
97     if (!pars->wasSet(VEHPARS_FORCE_REROUTE)) {
98         calculateArrivalParams();
99         if (MSGlobals::gCheckRoutes) {
100             std::string msg;
101             if (!hasValidRoute(msg)) {
102                 throw ProcessError("Vehicle '" + pars->id + "' has no valid route. " + msg);
103             }
104         }
105     }
106     // init devices
107     MSDevice::buildVehicleDevices(*this, myDevices);
108     for (MSVehicleDevice* dev : myDevices) {
109         myMoveReminders.push_back(std::make_pair(dev, 0.));
110     }
111 }
112 
113 
~MSBaseVehicle()114 MSBaseVehicle::~MSBaseVehicle() {
115     myRoute->release();
116     if (myParameter->repetitionNumber == 0) {
117         MSRoute::checkDist(myParameter->routeid);
118     }
119     for (MSVehicleDevice* dev : myDevices) {
120         delete dev;
121     }
122     delete myParameter;
123 }
124 
125 
126 const std::string&
getID() const127 MSBaseVehicle::getID() const {
128     return myParameter->id;
129 }
130 
131 
132 const SUMOVehicleParameter&
getParameter() const133 MSBaseVehicle::getParameter() const {
134     return *myParameter;
135 }
136 
137 void
replaceParameter(const SUMOVehicleParameter * newParameter)138 MSBaseVehicle::replaceParameter(const SUMOVehicleParameter* newParameter) {
139     delete myParameter;
140     myParameter = newParameter;
141 }
142 
143 double
getMaxSpeed() const144 MSBaseVehicle::getMaxSpeed() const {
145     return myType->getMaxSpeed();
146 }
147 
148 
149 const MSEdge*
succEdge(int nSuccs) const150 MSBaseVehicle::succEdge(int nSuccs) const {
151     if (myCurrEdge + nSuccs < myRoute->end() && std::distance(myCurrEdge, myRoute->begin()) <= nSuccs) {
152         return *(myCurrEdge + nSuccs);
153     } else {
154         return nullptr;
155     }
156 }
157 
158 
159 const MSEdge*
getEdge() const160 MSBaseVehicle::getEdge() const {
161     return *myCurrEdge;
162 }
163 
164 
165 void
reroute(SUMOTime t,const std::string & info,SUMOAbstractRouter<MSEdge,SUMOVehicle> & router,const bool onInit,const bool withTaz,const bool silent)166 MSBaseVehicle::reroute(SUMOTime t, const std::string& info, SUMOAbstractRouter<MSEdge, SUMOVehicle>& router, const bool onInit, const bool withTaz, const bool silent) {
167     // check whether to reroute
168     const MSEdge* source = withTaz && onInit ? MSEdge::dictionary(myParameter->fromTaz + "-source") : getRerouteOrigin();
169     if (source == nullptr) {
170         source = getRerouteOrigin();
171     }
172     const MSEdge* sink = withTaz ? MSEdge::dictionary(myParameter->toTaz + "-sink") : myRoute->getLastEdge();
173     if (sink == nullptr) {
174         sink = myRoute->getLastEdge();
175     }
176     ConstMSEdgeVector oldEdgesRemaining(source == *myCurrEdge ? myCurrEdge : myCurrEdge + 1, myRoute->end());
177     ConstMSEdgeVector edges;
178     ConstMSEdgeVector stops;
179     if (myParameter->via.size() == 0) {
180         double firstPos = -1;
181         double lastPos = -1;
182         stops = getStopEdges(firstPos, lastPos);
183         if (stops.size() > 0) {
184             const double sourcePos = onInit ? 0 : getPositionOnLane();
185             // avoid superfluous waypoints for first and last edge
186             const bool skipFirst = stops.front() == source && sourcePos < firstPos;
187             const bool skipLast = stops.back() == sink && myArrivalPos > lastPos;
188 #ifdef DEBUG_REROUTE
189             if (DEBUG_COND) {
190                 std::cout << SIMTIME << " reroute " << info << " veh=" << getID() << " lane=" << getLane()->getID()
191                           << " source=" << source->getID() << " sourcePos=" << sourcePos << " firstPos=" << firstPos << " arrivalPos=" << myArrivalPos << " lastPos=" << lastPos
192                           << " route=" << toString(myRoute->getEdges()) << " stopEdges=" << toString(stops) << " skipFirst=" << skipFirst << " skipLast=" << skipLast << "\n";
193             }
194 #endif
195             if (stops.size() == 1 && (skipFirst || skipLast)) {
196                 stops.clear();
197             } else {
198                 if (skipFirst) {
199                     stops.erase(stops.begin());
200                 }
201                 if (skipLast) {
202                     stops.erase(stops.end() - 1);
203                 }
204             }
205         }
206     } else {
207         // via takes precedence over stop edges
208         // XXX check for inconsistencies #2275
209         for (std::vector<std::string>::const_iterator it = myParameter->via.begin(); it != myParameter->via.end(); ++it) {
210             MSEdge* viaEdge = MSEdge::dictionary(*it);
211             if (viaEdge == source || viaEdge == sink) {
212                 continue;
213             }
214             assert(viaEdge != 0);
215             if (!viaEdge->isTazConnector() && viaEdge->allowedLanes(getVClass()) == nullptr) {
216                 throw ProcessError("Vehicle '" + getID() + "' is not allowed on any lane of via edge '" + viaEdge->getID() + "'.");
217             }
218             stops.push_back(viaEdge);
219         }
220     }
221 
222     for (MSRouteIterator s = stops.begin(); s != stops.end(); ++s) {
223         // !!! need to adapt t here
224         ConstMSEdgeVector into;
225         router.computeLooped(source, *s, this, t, into, silent);
226         if (into.size() > 0) {
227             into.pop_back();
228             edges.insert(edges.end(), into.begin(), into.end());
229             if ((*s)->isTazConnector()) {
230                 source = into.back();
231                 edges.pop_back();
232             } else {
233                 source = *s;
234             }
235         } else {
236             std::string error = "Vehicle '" + getID() + "' has no valid route from edge '" + source->getID() + "' to stop edge '" + (*s)->getID() + "'.";
237             if (MSGlobals::gCheckRoutes || silent) {
238                 throw ProcessError(error);
239             } else {
240                 WRITE_WARNING(error);
241                 edges.push_back(source);
242             }
243             source = *s;
244         }
245     }
246     router.compute(source, sink, this, t, edges, silent);
247     if (!edges.empty() && edges.front()->isTazConnector()) {
248         edges.erase(edges.begin());
249     }
250     if (!edges.empty() && edges.back()->isTazConnector()) {
251         edges.pop_back();
252     }
253     const double routeCost = router.recomputeCosts(edges, this, t);
254     const double previousCost = onInit ? routeCost : router.recomputeCosts(oldEdgesRemaining, this, t);
255     const double savings = previousCost - routeCost;
256     //if (getID() == "43") std::cout << SIMTIME << " pCost=" << previousCost << " cost=" << routeCost
257     //    << " onInit=" << onInit
258     //        << " prevEdges=" << toString(oldEdgesRemaining)
259     //        << " newEdges=" << toString(edges)
260     //        << "\n";
261     replaceRouteEdges(edges, routeCost, savings, info, onInit);
262     // this must be called even if the route could not be replaced
263     if (onInit) {
264         if (edges.empty()) {
265             if (MSGlobals::gCheckRoutes) {
266                 throw ProcessError("Vehicle '" + getID() + "' has no valid route.");
267             } else if (source->isTazConnector()) {
268                 WRITE_WARNING("Removing vehicle '" + getID() + "' which has no valid route.");
269                 MSNet::getInstance()->getInsertionControl().descheduleDeparture(this);
270                 return;
271             }
272         }
273         calculateArrivalParams();
274     }
275 }
276 
277 
278 bool
replaceRouteEdges(ConstMSEdgeVector & edges,double cost,double savings,const std::string & info,bool onInit,bool check,bool removeStops)279 MSBaseVehicle::replaceRouteEdges(ConstMSEdgeVector& edges, double cost, double savings, const std::string& info, bool onInit, bool check, bool removeStops) {
280     if (edges.empty()) {
281         WRITE_WARNING("No route for vehicle '" + getID() + "' found.");
282         return false;
283     }
284     // build a new id, first
285     std::string id = getID();
286     if (id[0] != '!') {
287         id = "!" + id;
288     }
289     if (myRoute->getID().find("!var#") != std::string::npos) {
290         id = myRoute->getID().substr(0, myRoute->getID().rfind("!var#") + 5) + toString(getNumberReroutes() + 1);
291     } else {
292         id = id + "!var#1";
293     }
294     int oldSize = (int)edges.size();
295     if (!onInit) {
296         const MSEdge* const origin = getRerouteOrigin();
297         if (origin != *myCurrEdge && edges.front() == origin) {
298             edges.insert(edges.begin(), *myCurrEdge);
299             oldSize = (int)edges.size();
300         }
301         edges.insert(edges.begin(), myRoute->begin(), myCurrEdge);
302     }
303     if (edges == myRoute->getEdges() && !StringUtils::endsWith(info, toString(SUMO_TAG_PARKING_ZONE_REROUTE))) {
304         // re-assign stop iterators when rerouting to a new parkingArea
305         return true;
306     }
307     const RGBColor& c = myRoute->getColor();
308     MSRoute* newRoute = new MSRoute(id, edges, false, &c == &RGBColor::DEFAULT_COLOR ? nullptr : new RGBColor(c), std::vector<SUMOVehicleParameter::Stop>());
309     newRoute->setCosts(cost);
310     newRoute->setSavings(savings);
311     if (!MSRoute::dictionary(id, newRoute)) {
312         delete newRoute;
313         return false;
314     }
315 
316     std::string msg;
317     if (check && !hasValidRoute(msg, newRoute)) {
318         WRITE_WARNING("Invalid route replacement for vehicle '" + getID() + "'. " + msg);
319         if (MSGlobals::gCheckRoutes) {
320             newRoute->addReference();
321             newRoute->release();
322             return false;
323         }
324     }
325     if (!replaceRoute(newRoute, info, onInit, (int)edges.size() - oldSize, false, removeStops)) {
326         newRoute->addReference();
327         newRoute->release();
328         return false;
329     }
330     return true;
331 }
332 
333 
334 double
getAcceleration() const335 MSBaseVehicle::getAcceleration() const {
336     return 0;
337 }
338 
339 
340 double
getSlope() const341 MSBaseVehicle::getSlope() const {
342     return 0;
343 }
344 
345 
346 void
onDepart()347 MSBaseVehicle::onDepart() {
348     myDeparture = MSNet::getInstance()->getCurrentTimeStep();
349     myDepartPos = getPositionOnLane();
350     MSNet::getInstance()->getVehicleControl().vehicleDeparted(*this);
351 }
352 
353 
354 bool
hasDeparted() const355 MSBaseVehicle::hasDeparted() const {
356     return myDeparture != NOT_YET_DEPARTED;
357 }
358 
359 
360 bool
hasArrived() const361 MSBaseVehicle::hasArrived() const {
362     return succEdge(1) == nullptr;
363 }
364 
365 void
addPerson(MSTransportable * person)366 MSBaseVehicle::addPerson(MSTransportable* person) {
367     if (myPersonDevice == nullptr) {
368         myPersonDevice = MSDevice_Transportable::buildVehicleDevices(*this, myDevices, false);
369         myMoveReminders.push_back(std::make_pair(myPersonDevice, 0.));
370         if (myParameter->departProcedure == DEPART_TRIGGERED && myParameter->depart == -1) {
371             const_cast<SUMOVehicleParameter*>(myParameter)->depart = MSNet::getInstance()->getCurrentTimeStep();
372         }
373     }
374     myPersonDevice->addTransportable(person);
375 }
376 
377 void
addContainer(MSTransportable * container)378 MSBaseVehicle::addContainer(MSTransportable* container) {
379     if (myContainerDevice == nullptr) {
380         myContainerDevice = MSDevice_Transportable::buildVehicleDevices(*this, myDevices, true);
381         myMoveReminders.push_back(std::make_pair(myContainerDevice, 0.));
382         if (myParameter->departProcedure == DEPART_TRIGGERED && myParameter->depart == -1) {
383             const_cast<SUMOVehicleParameter*>(myParameter)->depart = MSNet::getInstance()->getCurrentTimeStep();
384         }
385     }
386     myContainerDevice->addTransportable(container);
387 }
388 
389 bool
hasValidRoute(std::string & msg,const MSRoute * route) const390 MSBaseVehicle::hasValidRoute(std::string& msg, const MSRoute* route) const {
391     MSRouteIterator start = myCurrEdge;
392     if (route == nullptr) {
393         route = myRoute;
394     } else {
395         start = route->begin();
396     }
397     MSRouteIterator last = route->end() - 1;
398     // check connectivity, first
399     for (MSRouteIterator e = start; e != last; ++e) {
400         if ((*e)->allowedLanes(**(e + 1), myType->getVehicleClass()) == nullptr) {
401             msg = "No connection between edge '" + (*e)->getID() + "' and edge '" + (*(e + 1))->getID() + "'.";
402             return false;
403         }
404     }
405     last = route->end();
406     // check usable lanes, then
407     for (MSRouteIterator e = start; e != last; ++e) {
408         if ((*e)->prohibits(this)) {
409             msg = "Edge '" + (*e)->getID() + "' prohibits.";
410             return false;
411         }
412     }
413     return true;
414 }
415 
416 
417 void
addReminder(MSMoveReminder * rem)418 MSBaseVehicle::addReminder(MSMoveReminder* rem) {
419 #ifdef _DEBUG
420     if (myTraceMoveReminders) {
421         traceMoveReminder("add", rem, 0, true);
422     }
423 #endif
424     myMoveReminders.push_back(std::make_pair(rem, 0.));
425 }
426 
427 
428 void
removeReminder(MSMoveReminder * rem)429 MSBaseVehicle::removeReminder(MSMoveReminder* rem) {
430     for (MoveReminderCont::iterator r = myMoveReminders.begin(); r != myMoveReminders.end(); ++r) {
431         if (r->first == rem) {
432 #ifdef _DEBUG
433             if (myTraceMoveReminders) {
434                 traceMoveReminder("remove", rem, 0, false);
435             }
436 #endif
437             myMoveReminders.erase(r);
438             return;
439         }
440     }
441 }
442 
443 
444 void
activateReminders(const MSMoveReminder::Notification reason,const MSLane * enteredLane)445 MSBaseVehicle::activateReminders(const MSMoveReminder::Notification reason, const MSLane* enteredLane) {
446     for (MoveReminderCont::iterator rem = myMoveReminders.begin(); rem != myMoveReminders.end();) {
447         if (rem->first->notifyEnter(*this, reason, enteredLane)) {
448 #ifdef _DEBUG
449             if (myTraceMoveReminders) {
450                 traceMoveReminder("notifyEnter", rem->first, rem->second, true);
451             }
452 #endif
453             ++rem;
454         } else {
455 #ifdef _DEBUG
456             if (myTraceMoveReminders) {
457                 traceMoveReminder("notifyEnter", rem->first, rem->second, false);
458             }
459 #endif
460             rem = myMoveReminders.erase(rem);
461         }
462     }
463 }
464 
465 
466 void
calculateArrivalParams()467 MSBaseVehicle::calculateArrivalParams() {
468     if (myRoute->getLastEdge()->isTazConnector()) {
469         return;
470     }
471     const std::vector<MSLane*>& lanes = myRoute->getLastEdge()->getLanes();
472     const double lastLaneLength = lanes[0]->getLength();
473     switch (myParameter->arrivalPosProcedure) {
474         case ARRIVAL_POS_GIVEN:
475             if (fabs(myParameter->arrivalPos) > lastLaneLength) {
476                 WRITE_WARNING("Vehicle '" + getID() + "' will not be able to arrive at the given position!");
477             }
478             // Maybe we should warn the user about invalid inputs!
479             myArrivalPos = MIN2(myParameter->arrivalPos, lastLaneLength);
480             if (myArrivalPos < 0) {
481                 myArrivalPos = MAX2(myArrivalPos + lastLaneLength, 0.);
482             }
483             break;
484         case ARRIVAL_POS_RANDOM:
485             myArrivalPos = RandHelper::rand(lastLaneLength);
486             break;
487         case ARRIVAL_POS_CENTER:
488             myArrivalPos = lastLaneLength / 2.;
489             break;
490         default:
491             myArrivalPos = lastLaneLength;
492             break;
493     }
494     if (myParameter->arrivalLaneProcedure == ARRIVAL_LANE_GIVEN) {
495         if (myParameter->arrivalLane >= (int)lanes.size() || !lanes[myParameter->arrivalLane]->allowsVehicleClass(myType->getVehicleClass())) {
496             WRITE_WARNING("Vehicle '" + getID() + "' will not be able to arrive at the given lane '" + myRoute->getLastEdge()->getID() + "_" + toString(myParameter->arrivalLane) + "'!");
497         }
498         myArrivalLane = MIN2(myParameter->arrivalLane, (int)(lanes.size() - 1));
499     }
500     if (myParameter->arrivalSpeedProcedure == ARRIVAL_SPEED_GIVEN) {
501         for (std::vector<MSLane*>::const_iterator l = lanes.begin(); l != lanes.end(); ++l) {
502             if (myParameter->arrivalSpeed <= (*l)->getVehicleMaxSpeed(this)) {
503                 return;
504             }
505         }
506         WRITE_WARNING("Vehicle '" + getID() + "' will not be able to arrive with the given speed!");
507     }
508 }
509 
510 
511 double
getImpatience() const512 MSBaseVehicle::getImpatience() const {
513     return MAX2(0., MIN2(1., getVehicleType().getImpatience() +
514                          (MSGlobals::gTimeToImpatience > 0 ? (double)getWaitingTime() / MSGlobals::gTimeToImpatience : 0)));
515 }
516 
517 
518 MSVehicleDevice*
getDevice(const std::type_info & type) const519 MSBaseVehicle::getDevice(const std::type_info& type) const {
520     for (MSVehicleDevice* const dev : myDevices) {
521         if (typeid(*dev) == type) {
522             return dev;
523         }
524     }
525     return nullptr;
526 }
527 
528 
529 void
saveState(OutputDevice & out)530 MSBaseVehicle::saveState(OutputDevice& out) {
531     // this saves lots of departParameters which are only needed for vehicles that did not yet depart
532     // the parameters may hold the name of a vTypeDistribution but we are interested in the actual type
533     myParameter->write(out, OptionsCont::getOptions(), SUMO_TAG_VEHICLE, getVehicleType().getID());
534     // params and stops must be written in child classes since they may wish to add additional attributes first
535     out.writeAttr(SUMO_ATTR_ROUTE, myRoute->getID());
536     out.writeAttr(SUMO_ATTR_SPEEDFACTOR, myChosenSpeedFactor);
537     if (myParameter->wasSet(VEHPARS_FORCE_REROUTE) && !hasDeparted()) {
538         out.writeAttr(SUMO_ATTR_REROUTE, true);
539     }
540     // here starts the vehicle internal part (see loading)
541     // @note: remember to close the vehicle tag when calling this in a subclass!
542 }
543 
544 
545 void
addStops(const bool ignoreStopErrors)546 MSBaseVehicle::addStops(const bool ignoreStopErrors) {
547     for (std::vector<SUMOVehicleParameter::Stop>::const_iterator i = myRoute->getStops().begin(); i != myRoute->getStops().end(); ++i) {
548         std::string errorMsg;
549         if (!addStop(*i, errorMsg, myParameter->depart) && !ignoreStopErrors) {
550             throw ProcessError(errorMsg);
551         }
552         if (errorMsg != "") {
553             WRITE_WARNING(errorMsg);
554         }
555     }
556     const SUMOTime untilOffset = myParameter->repetitionOffset > 0 ? myParameter->repetitionsDone * myParameter->repetitionOffset : 0;
557     for (std::vector<SUMOVehicleParameter::Stop>::const_iterator i = myParameter->stops.begin(); i != myParameter->stops.end(); ++i) {
558         std::string errorMsg;
559         if (!addStop(*i, errorMsg, untilOffset) && !ignoreStopErrors) {
560             throw ProcessError(errorMsg);
561         }
562         if (errorMsg != "") {
563             WRITE_WARNING(errorMsg);
564         }
565     }
566 }
567 
568 
569 int
getPersonNumber() const570 MSBaseVehicle::getPersonNumber() const {
571     int boarded = myPersonDevice == nullptr ? 0 : myPersonDevice->size();
572     return boarded + myParameter->personNumber;
573 }
574 
575 std::vector<std::string>
getPersonIDList() const576 MSBaseVehicle::getPersonIDList() const {
577     std::vector<std::string> ret;
578     const std::vector<MSTransportable*>& persons = getPersons();
579     for (std::vector<MSTransportable*>::const_iterator it_p = persons.begin(); it_p != persons.end(); ++it_p) {
580         ret.push_back((*it_p)->getID());
581     }
582     return ret;
583 }
584 
585 int
getContainerNumber() const586 MSBaseVehicle::getContainerNumber() const {
587     int loaded = myContainerDevice == nullptr ? 0 : myContainerDevice->size();
588     return loaded + myParameter->containerNumber;
589 }
590 
591 
592 void
removeTransportable(MSTransportable * t)593 MSBaseVehicle::removeTransportable(MSTransportable* t) {
594     // this might be called from the MSTransportable destructor so we cannot do a dynamic cast to determine the type
595     if (myPersonDevice != nullptr) {
596         myPersonDevice->removeTransportable(t);
597     }
598     if (myContainerDevice != nullptr) {
599         myContainerDevice->removeTransportable(t);
600     }
601 }
602 
603 
604 const std::vector<MSTransportable*>&
getPersons() const605 MSBaseVehicle::getPersons() const {
606     if (myPersonDevice == nullptr) {
607         return myEmptyTransportableVector;
608     } else {
609         return myPersonDevice->getTransportables();
610     }
611 }
612 
613 
614 const std::vector<MSTransportable*>&
getContainers() const615 MSBaseVehicle::getContainers() const {
616     if (myContainerDevice == nullptr) {
617         return myEmptyTransportableVector;
618     } else {
619         return myContainerDevice->getTransportables();
620     }
621 }
622 
623 
624 
625 bool
hasDevice(const std::string & deviceName) const626 MSBaseVehicle::hasDevice(const std::string& deviceName) const {
627     for (MSDevice* const dev : myDevices) {
628         if (dev->deviceName() == deviceName) {
629             return true;
630         }
631     }
632     return false;
633 }
634 
635 
636 void
createDevice(const std::string & deviceName)637 MSBaseVehicle::createDevice(const std::string& deviceName) {
638     if (!hasDevice(deviceName)) {
639         if (deviceName == "rerouting") {
640             ((SUMOVehicleParameter*)myParameter)->setParameter("has." + deviceName + ".device", "true");
641             MSDevice_Routing::buildVehicleDevices(*this, myDevices);
642             if (hasDeparted()) {
643                 // vehicle already departed: disable pre-insertion rerouting and enable regular routing behavior
644                 MSDevice_Routing* routingDevice = static_cast<MSDevice_Routing*>(getDevice(typeid(MSDevice_Routing)));
645                 assert(routingDevice != 0);
646                 routingDevice->notifyEnter(*this, MSMoveReminder::NOTIFICATION_DEPARTED);
647             }
648         } else {
649             throw InvalidArgument("Creating device of type '" + deviceName + "' is not supported");
650         }
651     }
652 }
653 
654 
655 std::string
getDeviceParameter(const std::string & deviceName,const std::string & key) const656 MSBaseVehicle::getDeviceParameter(const std::string& deviceName, const std::string& key) const {
657     for (MSVehicleDevice* const dev : myDevices) {
658         if (dev->deviceName() == deviceName) {
659             return dev->getParameter(key);
660         }
661     }
662     throw InvalidArgument("No device of type '" + deviceName + "' exists");
663 }
664 
665 
666 void
setDeviceParameter(const std::string & deviceName,const std::string & key,const std::string & value)667 MSBaseVehicle::setDeviceParameter(const std::string& deviceName, const std::string& key, const std::string& value) {
668     for (MSVehicleDevice* const dev : myDevices) {
669         if (dev->deviceName() == deviceName) {
670             dev->setParameter(key, value);
671             return;
672         }
673     }
674     throw InvalidArgument("No device of type '" + deviceName + "' exists");
675 }
676 
677 
678 void
replaceVehicleType(MSVehicleType * type)679 MSBaseVehicle::replaceVehicleType(MSVehicleType* type) {
680     assert(type != nullptr);
681     if (myType->isVehicleSpecific() && type != myType) {
682         MSNet::getInstance()->getVehicleControl().removeVType(myType);
683     }
684     myType = type;
685 }
686 
687 
688 MSVehicleType&
getSingularType()689 MSBaseVehicle::getSingularType() {
690     if (myType->isVehicleSpecific()) {
691         return *myType;
692     }
693     MSVehicleType* type = myType->buildSingularType(myType->getID() + "@" + getID());
694     replaceVehicleType(type);
695     return *type;
696 }
697 
698 std::mt19937*
getRNG() const699 MSBaseVehicle::getRNG() const {
700     const MSLane* lane = getLane();
701     if (lane == nullptr) {
702         return getEdge()->getLanes()[0]->getRNG();
703     } else {
704         return lane->getRNG();
705     }
706 }
707 
708 #ifdef _DEBUG
709 void
initMoveReminderOutput(const OptionsCont & oc)710 MSBaseVehicle::initMoveReminderOutput(const OptionsCont& oc) {
711     if (oc.isSet("movereminder-output.vehicles")) {
712         const std::vector<std::string> vehicles = oc.getStringVector("movereminder-output.vehicles");
713         myShallTraceMoveReminders.insert(vehicles.begin(), vehicles.end());
714     }
715 }
716 
717 
718 void
traceMoveReminder(const std::string & type,MSMoveReminder * rem,double pos,bool keep) const719 MSBaseVehicle::traceMoveReminder(const std::string& type, MSMoveReminder* rem, double pos, bool keep) const {
720     OutputDevice& od = OutputDevice::getDeviceByOption("movereminder-output");
721     od.openTag("movereminder");
722     od.writeAttr(SUMO_ATTR_TIME, STEPS2TIME(MSNet::getInstance()->getCurrentTimeStep()));
723     od.writeAttr("veh", getID());
724     od.writeAttr(SUMO_ATTR_ID, rem->getDescription());
725     od.writeAttr("type", type);
726     od.writeAttr("pos", toString(pos));
727     od.writeAttr("keep", toString(keep));
728     od.closeTag();
729 }
730 #endif
731 
732 /****************************************************************************/
733 
734