LCOV - code coverage report
Current view: top level - src/microsim - MSBaseVehicle.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 88.4 % 1426 1260
Test Date: 2025-11-13 15:38:19 Functions: 89.3 % 122 109

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    MSBaseVehicle.cpp
      15              : /// @author  Michael Behrisch
      16              : /// @author  Daniel Krajzewicz
      17              : /// @author  Jakob Erdmann
      18              : /// @author  Mirko Barthauer
      19              : /// @date    Mon, 8 Nov 2010
      20              : ///
      21              : // A base class for vehicle implementations
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : 
      25              : #include <iostream>
      26              : #include <cassert>
      27              : #include <algorithm>
      28              : #include <functional>
      29              : #include <utils/common/StdDefs.h>
      30              : #include <utils/common/MsgHandler.h>
      31              : #include <utils/options/OptionsCont.h>
      32              : #include <utils/iodevices/OutputDevice.h>
      33              : #include <utils/emissions/PollutantsInterface.h>
      34              : #include <utils/emissions/HelpersHarmonoise.h>
      35              : #include <libsumo/TraCIConstants.h>
      36              : #include <mesosim/MELoop.h>
      37              : #include <mesosim/MEVehicle.h>
      38              : #include <microsim/devices/MSRoutingEngine.h>
      39              : #include <microsim/devices/MSDevice_Transportable.h>
      40              : #include <microsim/devices/MSDevice_Emissions.h>
      41              : #include <microsim/devices/MSDevice_Battery.h>
      42              : #include <microsim/devices/MSDevice_ElecHybrid.h>
      43              : #include <microsim/devices/MSDevice_Taxi.h>
      44              : #include <microsim/devices/MSDevice_Routing.h>
      45              : #include <microsim/lcmodels/MSAbstractLaneChangeModel.h>
      46              : #include <microsim/transportables/MSPerson.h>
      47              : #include <microsim/transportables/MSStageDriving.h>
      48              : #include <microsim/trigger/MSChargingStation.h>
      49              : #include <microsim/trigger/MSStoppingPlaceRerouter.h>
      50              : #include <microsim/trigger/MSTriggeredRerouter.h>
      51              : #include <microsim/traffic_lights/MSRailSignalConstraint.h>
      52              : #include <microsim/traffic_lights/MSRailSignalControl.h>
      53              : #include "MSEventControl.h"
      54              : #include "MSGlobals.h"
      55              : #include "MSVehicleControl.h"
      56              : #include "MSVehicleType.h"
      57              : #include "MSEdge.h"
      58              : #include "MSLane.h"
      59              : #include "MSMoveReminder.h"
      60              : #include "MSEdgeWeightsStorage.h"
      61              : #include "MSNet.h"
      62              : #include "MSStop.h"
      63              : #include "MSParkingArea.h"
      64              : #include "MSInsertionControl.h"
      65              : #include "MSBaseVehicle.h"
      66              : 
      67              : //#define DEBUG_REROUTE
      68              : //#define DEBUG_ADD_STOP
      69              : //#define DEBUG_COND (getID() == "")
      70              : //#define DEBUG_COND (true)
      71              : //#define DEBUG_REPLACE_ROUTE
      72              : //#define DEBUG_OPTIMIZE_SKIPPED
      73              : #define DEBUG_COND (isSelected())
      74              : 
      75              : // ===========================================================================
      76              : // static members
      77              : // ===========================================================================
      78              : const SUMOTime MSBaseVehicle::NOT_YET_DEPARTED = SUMOTime_MAX;
      79              : std::vector<MSTransportable*> MSBaseVehicle::myEmptyTransportableVector;
      80              : #ifdef _DEBUG
      81              : std::set<std::string> MSBaseVehicle::myShallTraceMoveReminders;
      82              : #endif
      83              : SUMOTrafficObject::NumericalID MSBaseVehicle::myCurrentNumericalIndex = 0;
      84              : 
      85              : // ===========================================================================
      86              : // Influencer method definitions
      87              : // ===========================================================================
      88              : 
      89         3556 : MSBaseVehicle::BaseInfluencer::BaseInfluencer()
      90         3556 : {}
      91              : 
      92              : // ===========================================================================
      93              : // method definitions
      94              : // ===========================================================================
      95              : 
      96              : double
      97            0 : MSBaseVehicle::getPreviousSpeed() const {
      98            0 :     throw ProcessError("getPreviousSpeed() is not available for non-MSVehicles.");
      99              : }
     100              : 
     101              : 
     102      5371080 : MSBaseVehicle::MSBaseVehicle(SUMOVehicleParameter* pars, ConstMSRoutePtr route,
     103      5371080 :                              MSVehicleType* type, const double speedFactor) :
     104              :     SUMOVehicle(pars->id),
     105      5371080 :     myParameter(pars),
     106              :     myRoute(route),
     107      5371080 :     myType(type),
     108      5371080 :     myCurrEdge(route->begin()),
     109      5382913 :     myChosenSpeedFactor(pars->speedFactor < 0 ? speedFactor : pars->speedFactor),
     110      5371080 :     myMoveReminders(0),
     111      5371080 :     myPersonDevice(nullptr),
     112      5371080 :     myContainerDevice(nullptr),
     113      5371080 :     myEnergyParams(nullptr),
     114      5371080 :     myDeparture(NOT_YET_DEPARTED),
     115      5371080 :     myDepartPos(-1),
     116      5371080 :     myArrivalPos(-1),
     117      5371080 :     myArrivalLane(-1),
     118      5371080 :     myNumberReroutes(0),
     119      5371080 :     myStopUntilOffset(0),
     120      5371080 :     myOdometer(0.),
     121      5371080 :     myRouteValidity(ROUTE_UNCHECKED),
     122      5371080 :     myRoutingMode(libsumo::ROUTING_MODE_DEFAULT),
     123      5371080 :     myNumericalID(myCurrentNumericalIndex++),
     124     10742160 :     myEdgeWeights(nullptr)
     125              : #ifdef _DEBUG
     126              :     , myTraceMoveReminders(myShallTraceMoveReminders.count(pars->id) > 0)
     127              : #endif
     128              : {
     129      5371080 :     if ((*myRoute->begin())->isTazConnector() || myRoute->getLastEdge()->isTazConnector()) {
     130       583746 :         pars->parametersSet |= VEHPARS_FORCE_REROUTE;
     131              :     }
     132      5371080 :     if ((pars->parametersSet & VEHPARS_FORCE_REROUTE) == 0) {
     133      3826459 :         setDepartAndArrivalEdge();
     134              :     }
     135      5371080 :     if (!pars->wasSet(VEHPARS_FORCE_REROUTE)) {
     136      3826459 :         calculateArrivalParams(true);
     137              :     }
     138      5371080 :     initTransientModelParams();
     139      5371080 : }
     140              : 
     141              : 
     142      5370988 : MSBaseVehicle::~MSBaseVehicle() {
     143      5370988 :     delete myEdgeWeights;
     144      5370988 :     if (myParameter->repetitionNumber == -1) {
     145              :         // this is not a flow (flows call checkDist in MSInsertionControl::determineCandidates)
     146       458756 :         MSRoute::checkDist(myParameter->routeid);
     147              :     }
     148      9333709 :     for (MSVehicleDevice* dev : myDevices) {
     149      3962721 :         delete dev;
     150              :     }
     151      5370988 :     delete myEnergyParams;
     152      5370988 :     delete myParkingMemory;
     153      5370988 :     delete myChargingMemory;
     154      5370988 :     checkRouteRemoval();
     155      5370988 :     delete myParameter;
     156      5370988 : }
     157              : 
     158              : 
     159              : void
     160      7409602 : MSBaseVehicle::checkRouteRemoval() {
     161              :     // the check for an instance is needed for the unit tests which do not construct a network
     162              :     // TODO Optimize for speed and there should be a better way to check whether a vehicle is part of a flow
     163     14819202 :     if (MSNet::hasInstance() && !MSNet::getInstance()->hasFlow(getFlowID())) {
     164      1367790 :         myRoute->checkRemoval();
     165              :     }
     166      7409602 : }
     167              : 
     168              : 
     169              : std::string
     170      7409600 : MSBaseVehicle::getFlowID() const {
     171      7409600 :     return getID().substr(0, getID().rfind('.'));
     172              : }
     173              : 
     174              : 
     175              : void
     176      5371080 : MSBaseVehicle::initDevices() {
     177      5371080 :     MSDevice::buildVehicleDevices(*this, myDevices);
     178      9328904 :     for (MSVehicleDevice* dev : myDevices) {
     179      3957841 :         myMoveReminders.push_back(std::make_pair(dev, 0.));
     180              :     }
     181      5371063 :     if (MSGlobals::gHaveEmissions) {
     182              :         // ensure we have the emission parameters even if we don't have the device
     183       911156 :         getEmissionParameters();
     184              :     }
     185      5371063 : }
     186              : 
     187              : 
     188              : void
     189            0 : MSBaseVehicle::setID(const std::string& /*newID*/) {
     190            0 :     throw ProcessError(TL("Changing a vehicle ID is not permitted"));
     191              : }
     192              : 
     193              : const SUMOVehicleParameter&
     194  15407865389 : MSBaseVehicle::getParameter() const {
     195  15407865389 :     return *myParameter;
     196              : }
     197              : 
     198              : 
     199              : void
     200          571 : MSBaseVehicle::replaceParameter(const SUMOVehicleParameter* newParameter) {
     201          571 :     delete myParameter;
     202          571 :     myParameter = newParameter;
     203          571 : }
     204              : 
     205              : 
     206              : bool
     207    655743349 : MSBaseVehicle::ignoreTransientPermissions() const {
     208    655743349 :     return (getRoutingMode() & libsumo::ROUTING_MODE_IGNORE_TRANSIENT_PERMISSIONS) != 0;
     209              : }
     210              : 
     211              : double
     212   5681157659 : MSBaseVehicle::getMaxSpeed() const {
     213   5681157659 :     return MIN2(myType->getMaxSpeed(), myType->getDesiredMaxSpeed() * myChosenSpeedFactor);
     214              : }
     215              : 
     216              : 
     217              : const MSEdge*
     218   2031527655 : MSBaseVehicle::succEdge(int nSuccs) const {
     219   2031527655 :     if (myCurrEdge + nSuccs < myRoute->end() && std::distance(myCurrEdge, myRoute->begin()) <= nSuccs) {
     220   1428187392 :         return *(myCurrEdge + nSuccs);
     221              :     } else {
     222    603340263 :         return nullptr;
     223              :     }
     224              : }
     225              : 
     226              : 
     227              : const MSEdge*
     228   3134205829 : MSBaseVehicle::getEdge() const {
     229   3134205829 :     return *myCurrEdge;
     230              : }
     231              : 
     232              : 
     233              : const std::set<SUMOTrafficObject::NumericalID>
     234        64066 : MSBaseVehicle::getUpcomingEdgeIDs() const {
     235              :     std::set<SUMOTrafficObject::NumericalID> result;
     236       288181 :     for (auto e = myCurrEdge; e != myRoute->end(); ++e) {
     237       224115 :         result.insert((*e)->getNumericalID());
     238              :     }
     239        64066 :     return result;
     240              : }
     241              : 
     242              : 
     243              : bool
     244      1439150 : MSBaseVehicle::stopsAt(MSStoppingPlace* stop) const {
     245      1439150 :     if (stop == nullptr) {
     246              :         return false;
     247              :     }
     248      2840032 :     for (const MSStop& s : myStops) {
     249      2838369 :         if (s.busstop == stop
     250      2799643 :                 || s.containerstop == stop
     251      1400898 :                 || s.parkingarea == stop
     252      1400892 :                 || s.chargingStation == stop) {
     253              :             return true;
     254              :         }
     255              :     }
     256              :     return false;
     257              : }
     258              : 
     259              : bool
     260       188694 : MSBaseVehicle::stopsAtEdge(const MSEdge* edge) const {
     261       398417 :     for (const MSStop& s : myStops) {
     262       368373 :         if (&s.lane->getEdge() == edge) {
     263              :             return true;
     264              :         }
     265              :     }
     266        30044 :     return myRoute->getLastEdge() == edge;
     267              : }
     268              : 
     269              : 
     270              : bool
     271      2786497 : MSBaseVehicle::reroute(SUMOTime t, const std::string& info, SUMOAbstractRouter<MSEdge, SUMOVehicle>& router, const bool onInit, const bool withTaz, const bool silent, const MSEdge* sink) {
     272              :     // check whether to reroute
     273      2786497 :     const MSEdge* source = withTaz && onInit ? MSEdge::dictionary(myParameter->fromTaz + "-source") : *getRerouteOrigin();
     274      2786497 :     if (source == nullptr) {
     275            5 :         source = *getRerouteOrigin();
     276              :     }
     277      2786497 :     if (sink == nullptr) {
     278      2760758 :         sink = withTaz ? MSEdge::dictionary(myParameter->toTaz + "-sink") : myRoute->getLastEdge();
     279      2760758 :         if (sink == nullptr) {
     280            5 :             sink = myRoute->getLastEdge();
     281              :         }
     282              :     }
     283      2794925 :     ConstMSEdgeVector oldEdgesRemaining(source == *myCurrEdge ? myCurrEdge : myCurrEdge + 1, myRoute->end());
     284              :     ConstMSEdgeVector edges;
     285              :     std::vector<StopEdgeInfo> stops;
     286              :     std::set<int> jumps;
     287              :     double sinkPriority = -1;
     288              :     bool stopAtSink = false;
     289      2786497 :     double sourcePos = onInit ? 0 : getPositionOnLane();
     290      2786497 :     if (myParameter->via.size() == 0) {
     291      2777986 :         double firstPos = INVALID_DOUBLE;
     292      2777986 :         double lastPos = INVALID_DOUBLE;
     293      2777986 :         stops = getStopEdges(firstPos, lastPos, jumps);
     294      2777986 :         if (stops.size() > 0) {
     295        39380 :             if (MSGlobals::gUseMesoSim && isStopped()) {
     296          264 :                 sourcePos = getNextStop().pars.endPos;
     297              :             }
     298              :             // avoid superfluous waypoints for first and last edge
     299        39380 :             const bool skipFirst = stops.front().edge == source && (source != getEdge() || sourcePos + getBrakeGap() <= firstPos + NUMERICAL_EPS);
     300        39380 :             const bool skipLast = (stops.back().edge == sink
     301        23359 :                                    && myArrivalPos >= lastPos
     302        15074 :                                    && (stops.size() < 2 || stops.back() != stops[stops.size() - 2])
     303        54439 :                                    && (stops.size() > 1 || skipFirst));
     304        39380 :             if (stops.back().edge == sink && myArrivalPos >= lastPos) {
     305        15074 :                 sinkPriority = stops.back().priority;
     306              :             }
     307              : #ifdef DEBUG_REROUTE
     308              :             if (DEBUG_COND) {
     309              :                 std::cout << SIMTIME << " reroute " << info << " veh=" << getID() << " lane=" << Named::getIDSecure(getLane())
     310              :                           << " source=" << source->getID() << " sourcePos=" << sourcePos << " firstPos=" << firstPos << " arrivalPos=" << myArrivalPos << " lastPos=" << lastPos
     311              :                           << " route=" << toString(myRoute->getEdges()) << " stopEdges=" << toString(stops) << " skipFirst=" << skipFirst << " skipLast=" << skipLast << "\n";
     312              :             }
     313              : #endif
     314        39380 :             if (stops.size() == 1 && (skipFirst || skipLast)) {
     315              :                 stops.clear();
     316              :             } else {
     317        31805 :                 if (skipFirst) {
     318              :                     stops.erase(stops.begin());
     319              :                 }
     320        31805 :                 if (skipLast) {
     321              :                     stops.erase(stops.end() - 1);
     322              :                 }
     323              :             }
     324        39380 :             stopAtSink = stops.size() > 0 && stops.back().edge == sink && jumps.size() == 0;
     325              :         }
     326              :     } else {
     327              :         std::set<const MSEdge*> jumpEdges;
     328              :         std::map<const MSEdge*, StopEdgeInfo> stopsOnVia;
     329        23452 :         for (const MSStop& stop : myStops) {
     330        14941 :             if (stop.pars.jump >= 0) {
     331              :                 jumpEdges.insert(*stop.edge);
     332              :             }
     333              :             auto itsov = stopsOnVia.find(*stop.edge);
     334        14941 :             if (itsov == stopsOnVia.end()) {
     335        14140 :                 stopsOnVia.insert({*stop.edge, StopEdgeInfo(*stop.edge, stop.pars.priority, stop.getArrivalFallback(), stop.getEndPos(*this))});
     336              :             } else {
     337          801 :                 itsov->second.priority = addStopPriority(itsov->second.priority, stop.pars.priority);
     338              :             }
     339              :         }
     340              :         // via takes precedence over stop edges
     341              :         // there is a consistency check in MSRouteHandler::addStop that warns when a stop edge is not part of the via edges
     342        28629 :         for (std::vector<std::string>::const_iterator it = myParameter->via.begin(); it != myParameter->via.end(); ++it) {
     343        20126 :             MSEdge* viaEdge = MSEdge::dictionary(*it);
     344        20126 :             if ((viaEdge == source && it == myParameter->via.begin()) || (viaEdge == sink && myParameter->via.end() - it == 1)) {
     345              :                 continue;
     346              :             }
     347              :             assert(viaEdge != 0);
     348        17043 :             if (!viaEdge->isTazConnector() && viaEdge->allowedLanes(getVClass()) == nullptr) {
     349           24 :                 throw ProcessError(TLF("Vehicle '%' is not allowed on any lane of via edge '%'.", getID(), viaEdge->getID()));
     350              :             }
     351              :             auto itsov = stopsOnVia.find(viaEdge);
     352        17035 :             const double priority = (itsov == stopsOnVia.end() ? -1 : itsov->second.priority);
     353        17035 :             const SUMOTime arrival = (itsov == stopsOnVia.end() ? -1 : itsov->second.arrival);
     354        17035 :             const double pos = (itsov == stopsOnVia.end() ? viaEdge->getLength() : itsov->second.pos);
     355        17035 :             stops.push_back(StopEdgeInfo(viaEdge, priority, arrival, pos));
     356              :             // @todo determine wether the viaEdge is also used by a stop and then use the stop priority here
     357              :             if (jumpEdges.count(viaEdge) != 0) {
     358           15 :                 jumps.insert((int)stops.size());
     359              :             }
     360              :         }
     361              :     }
     362              : 
     363              :     int stopIndex = -1;
     364              :     auto stopIt = myStops.begin();
     365              :     SUMOTime startTime = t;
     366              :     bool hasSkipped = false;
     367              :     const double origSourcePos = sourcePos;
     368              :     const MSEdge* origSource = source;
     369     13932445 :     const SUMOTime maxDelay = TIME2STEPS(getFloatParam(toString(SUMO_TAG_CLOSING_REROUTE) + ".maxDelay", false, MSTriggeredRerouter::DEFAULT_MAXDELAY, false));
     370      2863351 :     for (auto& stopEdgeInfo : stops) {
     371        76867 :         const MSEdge* const stopEdge = stopEdgeInfo.edge;
     372        76867 :         const double priority = stopEdgeInfo.priority;
     373        76867 :         stopIndex++;
     374              :         ConstMSEdgeVector into;
     375          987 :         if (jumps.count(stopIndex) != 0) {
     376          987 :             edges.push_back(source);
     377          987 :             source = stopEdge;
     378          987 :             continue;
     379              :         }
     380              :         // !!! need to adapt t here
     381        75907 :         router.computeLooped(source, stopEdge, this, t, into, silent || priority >= 0);
     382              :         //std::cout << SIMTIME << " reroute veh=" << getID() << " source=" << source->getID() << " target=" << (*s)->getID() << " edges=" << toString(into) << "\n";
     383        75880 :         if (into.size() > 0) {
     384       127878 :             while (stopIt != myStops.end() && stopIt->pars.edge != stopEdge->getID()) {
     385              :                 stopIt++;
     386              :             }
     387              : 
     388        75851 :             startTime += TIME2STEPS(router.recomputeCostsPos(into, this, sourcePos, stopEdgeInfo.pos, startTime));
     389        75851 :             if (stopIt != myStops.end()) {
     390        69366 :                 if (stopIt->pars.priority >= 0 && info != "device.rerouting") {
     391              :                     // consider skipping this stop if it cannot be reached in a timely manner
     392              :                     if (stopIt != myStops.end()) {
     393           15 :                         SUMOTime arrival = stopEdgeInfo.arrival;
     394           15 :                         if (arrival > 0) {
     395           15 :                             SUMOTime delay = startTime - arrival;
     396              :                             //std::cout << " t=" << time2string(t) << " veh=" << getID() << " info=" << info << " stopIndex=" << stopIndex
     397              :                             //   << " into=" << toString(into) << " sourcePos=" << sourcePos << " stopPos=" << stopPos
     398              :                             //   << " startTime=" << time2string(startTime) << " arrival=" << time2string(arrival) << " delay=" << time2string(delay) << "\n";
     399           15 :                             if (delay > 0) {
     400            6 :                                 if (delay > maxDelay) {
     401            6 :                                     stopEdgeInfo.skipped = true;
     402            6 :                                     stopEdgeInfo.delay = delay;
     403              :                                     hasSkipped = true;
     404            6 :                                     continue;
     405              :                                 }
     406              :                             }
     407              :                         }
     408              :                     }
     409              :                 }
     410        69360 :                 sourcePos = stopEdgeInfo.pos;
     411        69360 :                 startTime += stopIt->getMinDuration(startTime);
     412              :             }
     413              :             into.pop_back();
     414        75845 :             edges.insert(edges.end(), into.begin(), into.end());
     415        75845 :             if (stopEdge->isTazConnector()) {
     416           63 :                 source = into.back();
     417              :                 edges.pop_back();
     418              :             } else {
     419        75782 :                 source = stopEdge;
     420              :             }
     421        75845 :             stopEdgeInfo.routeIndex = (int)edges.size() - 1;
     422              :         } else {
     423           29 :             if ((source != sink || !stopAtSink)) {
     424           13 :                 if (priority >= 0) {
     425            6 :                     stopEdgeInfo.skipped = true;
     426              :                     hasSkipped = true;
     427            6 :                     continue;
     428              :                 } else {
     429           14 :                     std::string error = TLF("Vehicle '%' has no valid route from edge '%' to stop edge '%'.", getID(), source->getID(), stopEdge->getID());
     430            7 :                     if (MSGlobals::gCheckRoutes || silent) {
     431            5 :                         throw ProcessError(error);
     432              :                     } else {
     433            9 :                         WRITE_WARNING(error);
     434            2 :                         edges.push_back(source);
     435            2 :                         source = stopEdge;
     436              :                     }
     437              :                 }
     438              :             }
     439              :         }
     440        76867 :     }
     441      2753310 :     if (stops.empty() && source == sink && onInit
     442        18828 :             && myParameter->departPosProcedure == DepartPosDefinition::GIVEN
     443           91 :             && myParameter->arrivalPosProcedure == ArrivalPosDefinition::GIVEN
     444      2786505 :             && myParameter->departPos > myParameter->arrivalPos) {
     445           14 :         router.computeLooped(source, sink, this, t, edges, silent);
     446              :     } else {
     447      2787994 :         if (!router.compute(source, sink, this, t, edges, silent || sinkPriority >= 0)) {
     448          692 :             if (sinkPriority >= 0) {
     449            6 :                 edges.push_back(source);
     450              :                 hasSkipped = true;
     451           12 :                 stops.push_back(StopEdgeInfo(sink, sinkPriority, -1, getArrivalPos()));
     452            6 :                 stops.back().skipped = true;
     453              :             } else {
     454              :                 edges.clear();
     455              :             }
     456              :         }
     457              :     }
     458      2786478 :     if (hasSkipped) {
     459           18 :         edges = optimizeSkipped(t, router, origSource, origSourcePos, stops, edges, maxDelay);
     460           42 :         for (auto stop : stops) {
     461           24 :             if (stop.skipped) {
     462           18 :                 if (stop.delay > 0) {
     463           18 :                     WRITE_WARNING(TLF("Vehicle '%' skips stop on edge '%' with delay % at time %.", getID(), stop.edge->getID(), time2string(stop.delay), time2string(SIMSTEP)));
     464           12 :                 } else if (stop.backtracked) {
     465            9 :                     WRITE_WARNING(TLF("Vehicle '%' skips stop on edge '%' with priority % at time %.", getID(), stop.edge->getID(), stop.priority, time2string(SIMSTEP)));
     466              :                 } else {
     467           27 :                     WRITE_WARNING(TLF("Vehicle '%' skips unreachable stop on edge '%' with priority % at time %.", getID(), stop.edge->getID(), stop.priority, time2string(SIMSTEP)));
     468              :                 }
     469              :             }
     470              :         }
     471              :     }
     472              : 
     473              :     // router.setHint(myCurrEdge, myRoute->end(), this, t);
     474      2786484 :     if (edges.empty() && silent) {
     475              :         return false;
     476              :     }
     477      2786360 :     if (!edges.empty() && edges.front()->isTazConnector()) {
     478              :         edges.erase(edges.begin());
     479              :     }
     480      2786360 :     if (!edges.empty() && edges.back()->isTazConnector()) {
     481              :         edges.pop_back();
     482              :     }
     483      2786360 :     const double routeCost = router.recomputeCosts(edges, this, t);
     484      2786360 :     const double previousCost = onInit ? routeCost : router.recomputeCosts(oldEdgesRemaining, this, t);
     485      2786360 :     const double savings = previousCost - routeCost;
     486      2786360 :     bool savingsOk = onInit || info != "device.rerouting" || gWeightsRandomFactor != 1;
     487              :     if (!savingsOk) {
     488       594567 :         MSDevice_Routing* routingDevice = static_cast<MSDevice_Routing*>(getDevice(typeid(MSDevice_Routing)));
     489              :         assert(routingDevice != 0);
     490       594567 :         savingsOk = routingDevice->sufficientSaving(previousCost, routeCost);
     491       594567 :         if (!savingsOk) {
     492              :             std::string dummyMsg;
     493       571353 :             if (!hasValidRoute(dummyMsg, oldEdgesRemaining.begin(), oldEdgesRemaining.end(), true)) {
     494              :                 // the old route is prohibted (i.e. due to temporary permission changes)
     495              :                 savingsOk = true;
     496              :             }
     497              :         }
     498              :     }
     499              :     //if (getID() == "43") std::cout << SIMTIME << " pCost=" << previousCost << " cost=" << routeCost
     500              :     //    << " onInit=" << onInit
     501              :     //        << " prevEdges=" << toString(oldEdgesRemaining)
     502              :     //        << " newEdges=" << toString(edges)
     503              :     //        << "\n";
     504       571353 :     if (savingsOk) {
     505      2215174 :         replaceRouteEdges(edges, routeCost, savings, info, onInit);
     506              :     }
     507              :     // this must be called even if the route could not be replaced
     508      2786359 :     if (onInit) {
     509      2163379 :         if (edges.empty()) {
     510          197 :             if (MSGlobals::gCheckRoutes) {
     511          360 :                 throw ProcessError(TLF("Vehicle '%' has no valid route.", getID()));
     512           77 :             } else if (source->isTazConnector()) {
     513          215 :                 WRITE_WARNINGF(TL("Removing vehicle '%' which has no valid route."), getID());
     514           27 :                 MSNet::getInstance()->getInsertionControl().descheduleDeparture(this);
     515              :                 return false;
     516              :             }
     517              :         }
     518      2163232 :         setDepartAndArrivalEdge();
     519      2163232 :         calculateArrivalParams(onInit);
     520              :     }
     521      2786212 :     return !edges.empty();
     522      2786765 : }
     523              : 
     524              : 
     525              : bool
     526      2254262 : MSBaseVehicle::replaceRouteEdges(ConstMSEdgeVector& edges, double cost, double savings, const std::string& info, bool onInit, bool check, bool removeStops, std::string* msgReturn) {
     527      2254262 :     if (edges.empty()) {
     528         1707 :         WRITE_WARNINGF(TL("No route for vehicle '%' found."), getID());
     529          569 :         if (msgReturn != nullptr) {
     530              :             *msgReturn = "No route found";
     531              :         }
     532          569 :         return false;
     533              :     }
     534              :     // build a new id, first
     535              :     std::string id = getID();
     536      2253693 :     if (id[0] != '!') {
     537      4507387 :         id = "!" + id;
     538              :     }
     539      2253693 :     const std::string idSuffix = id + "!var#";
     540      2253693 :     int varIndex = 1;
     541      2253693 :     id = idSuffix + toString(varIndex);
     542      4149177 :     while (MSRoute::hasRoute(id)) {
     543      3790968 :         id = idSuffix + toString(++varIndex);
     544              :     }
     545      2253693 :     int oldSize = (int)edges.size();
     546      2253693 :     if (!onInit) {
     547        88849 :         const MSEdge* const origin = *getRerouteOrigin();
     548        88849 :         if (origin != *myCurrEdge && edges.front() == origin) {
     549          995 :             edges.insert(edges.begin(), *myCurrEdge);
     550          995 :             oldSize = (int)edges.size();
     551              :         }
     552       177698 :         edges.insert(edges.begin(), myRoute->begin(), myCurrEdge);
     553              :     }
     554      2253693 :     if (edges == myRoute->getEdges() && haveValidStopEdges(true)) {
     555              :         // re-assign stop iterators when rerouting to a new parkingArea / insertStop
     556              :         return true;
     557              :     }
     558      1514135 :     const RGBColor& c = myRoute->getColor();
     559      1514135 :     MSRoute* newRoute = new MSRoute(id, edges, false, &c == &RGBColor::DEFAULT_COLOR ? nullptr : new RGBColor(c), StopParVector());
     560              :     newRoute->setCosts(cost);
     561              :     newRoute->setSavings(savings);
     562      1514136 :     ConstMSRoutePtr constRoute = std::shared_ptr<MSRoute>(newRoute);
     563      3028270 :     if (!MSRoute::dictionary(id, constRoute)) {
     564            0 :         delete newRoute;
     565            0 :         if (msgReturn != nullptr) {
     566            1 :             *msgReturn = "duplicate routeID '" + id + "'";
     567              :         }
     568            0 :         return false;
     569              :     }
     570              : 
     571              :     std::string msg;
     572      1514168 :     if (check && !hasValidRoute(msg, constRoute)) {
     573            3 :         WRITE_WARNINGF(TL("Invalid route replacement for vehicle '%'. %"), getID(), msg);
     574            1 :         if (MSGlobals::gCheckRoutes) {
     575            1 :             if (msgReturn != nullptr) {
     576              :                 *msgReturn = msg;
     577              :             }
     578            1 :             return false;
     579              :         }
     580              :     }
     581      3028268 :     if (!replaceRoute(constRoute, info, onInit, (int)edges.size() - oldSize, false, removeStops, msgReturn)) {
     582              :         return false;
     583              :     }
     584              :     return true;
     585              : }
     586              : 
     587              : 
     588              : bool
     589      2038632 : MSBaseVehicle::replaceRoute(ConstMSRoutePtr newRoute, const std::string& info, bool onInit, int offset, bool addRouteStops, bool removeStops, std::string* msgReturn) {
     590              :     const ConstMSEdgeVector& edges = newRoute->getEdges();
     591              :     // rebuild in-vehicle route information
     592      2038632 :     if (onInit) {
     593      1445959 :         myCurrEdge = newRoute->begin();
     594              :     } else {
     595       592673 :         MSRouteIterator newCurrEdge = std::find(edges.begin() + offset, edges.end(), *myCurrEdge);
     596       592673 :         if (newCurrEdge == edges.end()) {
     597            2 :             if (msgReturn != nullptr) {
     598            6 :                 *msgReturn = TLF("current edge '%' not found in new route", (*myCurrEdge)->getID());
     599              :             }
     600              : #ifdef DEBUG_REPLACE_ROUTE
     601              :             if (DEBUG_COND) {
     602              :                 std::cout << "  newCurrEdge not found\n";
     603              :             }
     604              : #endif
     605            2 :             return false;
     606              :         }
     607       592671 :         if (getLane() != nullptr) {
     608       454590 :             if (getLane()->getEdge().isInternal() && (
     609       454599 :                         (newCurrEdge + 1) == edges.end() || (*(newCurrEdge + 1)) != &(getLane()->getOutgoingViaLanes().front().first->getEdge()))) {
     610            9 :                 if (msgReturn != nullptr) {
     611            1 :                     *msgReturn = TL("Vehicle is on junction-internal edge leading elsewhere");
     612              :                 }
     613              : #ifdef DEBUG_REPLACE_ROUTE
     614              :                 if (DEBUG_COND) {
     615              :                     std::cout << "  Vehicle is on junction-internal edge leading elsewhere\n";
     616              :                 }
     617              : #endif
     618            9 :                 return false;
     619       454581 :             } else if (getPositionOnLane() > getLane()->getLength()
     620       454586 :                        && (myCurrEdge + 1) != myRoute->end()
     621           12 :                        && (newCurrEdge + 1) != edges.end()
     622       454593 :                        && *(myCurrEdge + 1) != *(newCurrEdge + 1)) {
     623            7 :                 if (msgReturn != nullptr) {
     624            0 :                     *msgReturn = TL("Vehicle is moving past junction and committed to move to another successor edge");
     625              :                 }
     626              : #ifdef DEBUG_REPLACE_ROUTE
     627              :                 if (DEBUG_COND) {
     628              :                     std::cout << "  Vehicle is moving past junction and committed to move to another successor edge\n";
     629              :                 }
     630              : #endif
     631            7 :                 return false;
     632              :             }
     633              :         }
     634       592655 :         myCurrEdge = newCurrEdge;
     635              :     }
     636      2038614 :     const bool stopsFromScratch = onInit && myRoute->getStops().empty();
     637              :     // assign new route
     638      2038614 :     checkRouteRemoval();
     639              :     myRoute = newRoute;
     640              :     // update arrival definition
     641      2038614 :     calculateArrivalParams(onInit);
     642              :     // save information that the vehicle was rerouted
     643      2038614 :     myNumberReroutes++;
     644      2038614 :     myStopUntilOffset += myRoute->getPeriod();
     645      2038614 :     MSNet::getInstance()->informVehicleStateListener(this, MSNet::VehicleState::NEWROUTE, info);
     646      2038614 :     if (!onInit && isRail() && MSRailSignalControl::hasInstance()) {
     647              :         // we need to update driveways (add/remove reminders) before the next call to MSRailSignalControl::updateSignals
     648              :         //
     649              :         // rerouting may be triggered through
     650              :         // - MoveReminders (executeMove->activateReminders)
     651              :         //   - rerouters
     652              :         //   - devices (MSDevice_Stationfinder)
     653              :         // - TraCI (changeTarget, replaceStop, ...
     654              :         // - events (MSDevice_Routing::myRerouteCommand, MSDevice_Taxi::triggerDispatch)
     655              :         //
     656              :         // Since activateReminders actively modifies reminders, adding/deleting reminders would create a mess
     657              :         // hence, we use an event to be safe for all case
     658              : 
     659         1498 :         MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(new WrappingCommand<MSBaseVehicle>(this,
     660          749 :                 &MSBaseVehicle::activateRemindersOnReroute), SIMSTEP);
     661              :     }
     662              : #ifdef DEBUG_REPLACE_ROUTE
     663              :     if (DEBUG_COND) {
     664              :         std::cout << SIMTIME << " veh=" << getID() << " replaceRoute info=" << info << " on " << (*myCurrEdge)->getID()
     665              :                   << " lane=" << Named::getIDSecure(getLane())
     666              :                   << " stopsFromScratch=" << stopsFromScratch
     667              :                   << "  newSize=" << newRoute->getEdges().size()
     668              :                   << " newIndex=" << (myCurrEdge - newRoute->begin())
     669              :                   << " edges=" << toString(newRoute->getEdges())
     670              :                   << "\n";
     671              :     }
     672              : #endif
     673              :     // remove past stops which are not on the route anymore
     674      2114478 :     for (StopParVector::iterator it = myPastStops.begin(); it != myPastStops.end();) {
     675       122897 :         const MSEdge* stopEdge = (it->edge.empty()) ? &MSLane::dictionary(it->lane)->getEdge() : MSEdge::dictionary(it->edge);
     676        75864 :         if (std::find(myRoute->begin(), myRoute->end(), stopEdge) == myRoute->end()) {
     677            5 :             it = myPastStops.erase(it);
     678              :         } else {
     679              :             ++it;
     680              :         }
     681              :     }
     682              :     // if we did not drive yet it may be best to simply reassign the stops from scratch
     683      2038614 :     if (stopsFromScratch) {
     684              :         myStops.clear();
     685      1445895 :         addStops(!MSGlobals::gCheckRoutes);
     686              :     } else {
     687              :         // recheck old stops
     688       592719 :         MSRouteIterator searchStart = myCurrEdge;
     689       592719 :         double lastPos = getPositionOnLane() + getBrakeGap();
     690      1047293 :         if (getLane() != nullptr && getLane()->isInternal()
     691       593603 :                 && myStops.size() > 0 && !myStops.front().lane->isInternal()) {
     692              :             // searchStart is still incoming to the intersection so lastPos
     693              :             // relative to that edge must be adapted
     694          298 :             lastPos += (*myCurrEdge)->getLength();
     695              :         }
     696              :         int stopIndex = 0;
     697       629773 :         for (std::list<MSStop>::iterator iter = myStops.begin(); iter != myStops.end();) {
     698        37054 :             double endPos = iter->getEndPos(*this);
     699              : #ifdef DEBUG_REPLACE_ROUTE
     700              :             if (DEBUG_COND) {
     701              :                 std::cout << "     stopEdge=" << iter->lane->getEdge().getID() << " start=" << (searchStart - myCurrEdge) << " endPos=" << endPos << " lastPos=" << lastPos << "\n";
     702              :             }
     703              : #endif
     704        37054 :             if (*searchStart != &iter->lane->getEdge()
     705        37054 :                     || endPos + NUMERICAL_EPS < lastPos) {
     706        30200 :                 if (searchStart != edges.end() && !iter->reached) {
     707              :                     searchStart++;
     708              :                 }
     709              :             }
     710              :             lastPos = endPos;
     711              : 
     712        37054 :             iter->edge = std::find(searchStart, edges.end(), &iter->lane->getEdge());
     713              : #ifdef DEBUG_REPLACE_ROUTE
     714              :             if (DEBUG_COND) {
     715              :                 std::cout << "        foundIndex=" << (iter->edge - myCurrEdge) << " end=" << (edges.end() - myCurrEdge) << "\n";
     716              :             }
     717              : #endif
     718        37054 :             if (iter->edge == edges.end()) {
     719          182 :                 if (!removeStops) {
     720           24 :                     WRITE_ERRORF(TL("Vehicle '%' could not assign stop '%' after rerouting (%) at time=%."), getID(), iter->getDescription(), info, time2string(SIMSTEP));
     721              :                 }
     722          182 :                 iter = myStops.erase(iter);
     723          182 :                 continue;
     724              :             } else {
     725        36872 :                 setSkips(*iter, stopIndex);
     726        36872 :                 searchStart = iter->edge;
     727              :             }
     728              :             ++iter;
     729        36872 :             stopIndex++;
     730              :         }
     731              :         // add new stops
     732       592719 :         if (addRouteStops) {
     733       524765 :             for (StopParVector::const_iterator i = newRoute->getStops().begin(); i != newRoute->getStops().end(); ++i) {
     734              :                 std::string error;
     735          307 :                 addStop(*i, error, myParameter->depart + myStopUntilOffset);
     736          307 :                 if (error != "") {
     737            0 :                     WRITE_WARNING(error);
     738              :                 }
     739              :             }
     740              :         }
     741              :     }
     742              :     return true;
     743              : }
     744              : 
     745              : 
     746              : ConstMSEdgeVector
     747           18 : MSBaseVehicle::optimizeSkipped(SUMOTime t, SUMOAbstractRouter<MSEdge, SUMOVehicle>& router, const MSEdge* source, double sourcePos,
     748              :                                std::vector<StopEdgeInfo>& stops, ConstMSEdgeVector edges, SUMOTime maxDelay) const {
     749              :     double skippedPrio = 0;
     750              :     double minPrio = std::numeric_limits<double>::max();
     751              :     std::vector<int> skipped;
     752           42 :     for (int i = 0; i < (int)stops.size(); i++) {
     753           24 :         if (stops[i].skipped) {
     754           18 :             skipped.push_back(i);
     755              :         }
     756           24 :         minPrio = MIN2(minPrio, stops[i].priority);
     757              :     }
     758           36 :     for (int i : skipped) {
     759           18 :         skippedPrio += stops[i].priority;
     760              :     }
     761              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     762              :     std::cout << SIMTIME << " veh=" << getID() << " optimzeSkipped=" << toString(skipped) << " source=" << source->getID() << "\n";
     763              :     for (int i = 0; i < (int)stops.size(); i++) {
     764              :         const auto& stop = stops[i];
     765              :         std::cout << "  " << i << " edge=" << stop.edge->getID() << " routeIndex=" << stop.routeIndex << " prio=" << stop.priority << " skipped=" << stop.skipped << " arrival=" << stop.arrival << "\n";
     766              :     }
     767              : #endif
     768           18 :     if (skippedPrio == minPrio) {
     769              :         // case A: only one stop was skipped and it had the lowest priority (or multiple stops with prio 0 were skipped): this is already optimal
     770              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     771              :         std::cout << "  skippedPrio=" << skippedPrio << " minPrio=" << minPrio << "\n";
     772              : #endif
     773              :         return edges;
     774              :     }
     775              :     // check reachability of skipped stops
     776              :     std::vector<int> skippedReachable;
     777           12 :     for (int si : skipped) {
     778              :         ConstMSEdgeVector into;
     779            6 :         router.computeLooped(source, stops[si].edge, this, t, into, true);
     780            6 :         if (into.size() > 0) {
     781            3 :             SUMOTime arrival = t + TIME2STEPS(router.recomputeCostsPos(into, this, sourcePos, stops[si].pos, t));
     782            3 :             if (arrival - stops[si].arrival <= maxDelay) {
     783            3 :                 skippedReachable.push_back(si);
     784              :             }
     785              :         }
     786            6 :     }
     787            6 :     if (skippedReachable.size() == 0) {
     788              :         // case B: skipped stops are not reachable with backtracking
     789              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     790              :         std::cout << "  noneReachable\n";
     791              : #endif
     792              :         return edges;
     793              :     }
     794              :     std::set<int> unskippedBefore;
     795            9 :     for (int i = 0; i < (int)stops.size(); i++) {
     796            6 :         if (i < skipped.back()) {
     797              :             unskippedBefore.insert(i);
     798              :         }
     799              :     }
     800            6 :     for (int i : skipped) {
     801              :         unskippedBefore.erase(i);
     802              :     }
     803              :     // otherwise, skippedReachable should have been empty
     804              :     assert(unskippedBefore.size() > 0);
     805              :     // the unskipped stops may form several non contiguous sequences. We care about the last element of each sequence
     806              :     std::vector<int> unskippedEnds;
     807              :     std::vector<int> skippedStarts;
     808            6 :     for (int i : unskippedBefore) {
     809            3 :         if (unskippedBefore.count(i + 1) == 0) {
     810            3 :             for (int i2 : skippedReachable) {
     811            3 :                 if (i2 >= i + 1) {
     812            3 :                     unskippedEnds.push_back(i);
     813            3 :                     skippedStarts.push_back(i2);
     814              :                     break;
     815              :                 }
     816              :             }
     817              :         }
     818              :     }
     819            3 :     std::sort(unskippedEnds.begin(), unskippedEnds.end()); // ascending
     820            3 :     std::set<int> skippedSet(skipped.begin(), skipped.end());
     821              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     822              :     std::cout << "  unskippedEnds=" << toString(unskippedEnds) << " skippedStarts=" << toString(skippedStarts) << "\n";
     823              : #endif
     824              : 
     825            3 :     ConstMSEdgeVector bestEdges = edges;
     826              :     double altSkippedPrio = 0;
     827            3 :     const MSEdge* firstSkipped = stops[skippedStarts.back()].edge;
     828            6 :     for (int i = unskippedEnds.back(); i >= 0; i--) {
     829            3 :         double prio = stops[i].priority;
     830            3 :         altSkippedPrio += prio;
     831            0 :         if (skippedSet.count(i)  // found start of another skip sequence
     832            3 :                 || prio < 0 // cannot backtrack past unskippable stop
     833            3 :                 || altSkippedPrio >= skippedPrio // backtracking past this stop cannot improve result
     834              :            ) {
     835              :             unskippedEnds.pop_back();
     836              :             skippedStarts.pop_back();
     837            0 :             if (unskippedEnds.empty()) {
     838            0 :                 return edges;
     839              :             }
     840              :             // try to optimize earlier sequence of skips
     841            0 :             i = unskippedEnds.back();
     842            0 :             firstSkipped = stops[skippedStarts.back()].edge;
     843              :             altSkippedPrio = 0;
     844            0 :             continue;
     845              :         }
     846            3 :         const MSEdge* prev = i > 0 ? stops[i - 1].edge : source;
     847            0 :         const double prevPos = i > 0 ? stops[i - 1].pos : sourcePos;
     848              :         ConstMSEdgeVector into;
     849            3 :         SUMOTime start = stops[i - 1].arrival;
     850            3 :         router.computeLooped(prev, firstSkipped, this, start, into, true);
     851            3 :         if (into.size() == 0) {
     852              :             // cannot reach firstSkipped and need to backtrack further
     853              :             continue;
     854              :         }
     855            3 :         start += TIME2STEPS(router.recomputeCostsPos(into, this, prevPos, stops[skippedStarts.back()].pos, start));
     856              :         // initialize skipped priority with stops skipped during backtracking and any skipped before that
     857            3 :         std::vector<StopEdgeInfo> stops2 = stops;
     858            3 :         double skippedPrio2 = altSkippedPrio;
     859            3 :         for (int i2 = 0; i2 < i - 1; i2++) {
     860            0 :             if (stops[i2].skipped) {
     861            0 :                 skippedPrio2 += stops[i2].priority;
     862              :             }
     863              :         }
     864            6 :         for (int i2 = i; i2 <= unskippedEnds.back(); i2++) {
     865            3 :             stops2[i2].skipped = true;
     866            3 :             stops2[i2].backtracked = true;
     867              :         }
     868            3 :         int prevRouteIndex = i > 0 ? stops[i - 1].routeIndex : getDepartEdge();
     869              :         assert(prevRouteIndex >= 0 && prevRouteIndex < (int)edges.size());
     870            3 :         ConstMSEdgeVector edges2(edges.begin(), edges.begin() + prevRouteIndex);
     871            3 :         stops2[skippedStarts.back()].skipped = false;
     872            3 :         edges2.insert(edges2.begin(), into.begin(), into.end());
     873            3 :         edges2 = routeAlongStops(start, router, stops2, edges2, skippedStarts.back(), maxDelay, skippedPrio2);
     874            3 :         if (skippedPrio2 < skippedPrio) {
     875              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     876              :             std::cout << " skippedPrio=" << skippedPrio << " skippedPrio2=" << skippedPrio2 << "\n";
     877              : #endif
     878            3 :             bestEdges = edges2;
     879            3 :             skippedPrio = skippedPrio2;
     880            3 :             stops = stops2;
     881              :         }
     882            3 :     }
     883              :     return bestEdges;
     884           24 : }
     885              : 
     886              : 
     887              : ConstMSEdgeVector
     888            3 : MSBaseVehicle::routeAlongStops(SUMOTime t, SUMOAbstractRouter<MSEdge, SUMOVehicle>& router,
     889              :                                std::vector<StopEdgeInfo>& stops, ConstMSEdgeVector edges,
     890              :                                int originStop, SUMOTime maxDelay, double& skippedPrio2) const {
     891              :     // originStop was already reached an the edges appended
     892            3 :     for (int i = originStop + 1; i < (int)stops.size(); i++) {
     893              :         ConstMSEdgeVector into;
     894            0 :         router.computeLooped(edges.back(), stops[i].edge, this, t, into, true);
     895            0 :         if (into.size() == 0) {
     896            0 :             if (stops[i].priority < 0) {
     897              :                 // failure: cannot reach required stop
     898            0 :                 skippedPrio2 = std::numeric_limits<double>::max();
     899              :                 return edges;
     900              :             }
     901            0 :             skippedPrio2 += stops[i].priority;
     902            0 :             stops[i].skipped = true;
     903              :         } else {
     904            0 :             t += TIME2STEPS(router.recomputeCostsPos(into, this, stops[i - 1].pos, stops[i].pos, t));
     905            0 :             SUMOTime delay = t - stops[i].arrival;
     906            0 :             if (delay > maxDelay) {
     907            0 :                 if (stops[i].priority < 0) {
     908              :                     // failure: cannot reach required stop in time
     909            0 :                     skippedPrio2 = std::numeric_limits<double>::max();
     910              :                     return edges;
     911              :                 }
     912            0 :                 skippedPrio2 += stops[i].priority;
     913            0 :                 stops[i].skipped = true;
     914            0 :                 stops[i].delay = true;
     915              :             } else {
     916              :                 edges.pop_back();
     917            0 :                 edges.insert(edges.end(), into.begin(), into.end());
     918              :             }
     919              :         }
     920            0 :     }
     921              :     return edges;
     922              : }
     923              : 
     924              : 
     925              : double
     926       162847 : MSBaseVehicle::getAcceleration() const {
     927       162847 :     return 0;
     928              : }
     929              : 
     930              : 
     931              : void
     932      3737243 : MSBaseVehicle::onDepart() {
     933      3737243 :     myDeparture = MSNet::getInstance()->getCurrentTimeStep();
     934      3737243 :     myDepartPos = getPositionOnLane();
     935      3737243 :     MSNet::getInstance()->getVehicleControl().vehicleDeparted(*this);
     936      3737243 : }
     937              : 
     938              : 
     939              : SUMOTime
     940      3008155 : MSBaseVehicle:: getDepartDelay() const {
     941      3008155 :     const SUMOTime dep = getParameter().depart;
     942      3008155 :     if (dep < 0) {
     943              :         return 0;
     944              :     }
     945      3008048 :     return hasDeparted() ? getDeparture() - dep : SIMSTEP - dep;
     946              : }
     947              : 
     948              : 
     949              : bool
     950            0 : MSBaseVehicle::hasArrived() const {
     951            0 :     return succEdge(1) == nullptr;
     952              : }
     953              : 
     954              : 
     955              : int
     956     31182058 : MSBaseVehicle::getRoutePosition() const {
     957     31182058 :     return (int) std::distance(myRoute->begin(), myCurrEdge);
     958              : }
     959              : 
     960              : 
     961              : int
     962       325056 : MSBaseVehicle::getNumRemainingEdges() const {
     963       325056 :     if (myParameter->arrivalEdge >= 0) {
     964            0 :         return myParameter->arrivalEdge - getRoutePosition() + 1;
     965              :     } else {
     966       325056 :         return myRoute->size() - getRoutePosition();
     967              :     }
     968              : }
     969              : 
     970              : 
     971              : void
     972       103608 : MSBaseVehicle::resetRoutePosition(int index, DepartLaneDefinition departLaneProcedure) {
     973       103608 :     myCurrEdge = myRoute->begin() + index;
     974       103608 :     const_cast<SUMOVehicleParameter*>(myParameter)->departLaneProcedure = departLaneProcedure;
     975              :     // !!! hack
     976       103608 :     myArrivalPos = (*(myRoute->end() - 1))->getLanes()[0]->getLength();
     977       103608 : }
     978              : 
     979              : double
     980        26489 : MSBaseVehicle::getOdometer() const {
     981        26489 :     return -myDepartPos + myOdometer + (hasArrived() ? myArrivalPos : getPositionOnLane());
     982              : }
     983              : 
     984              : bool
     985      1640506 : MSBaseVehicle::allowsBoarding(const MSTransportable* t) const {
     986      1640506 :     if (t->isPerson() && getPersonNumber() >= getVehicleType().getPersonCapacity()) {
     987              :         return false;
     988      1536525 :     } else if (!t->isPerson() && getContainerNumber() >= getVehicleType().getContainerCapacity()) {
     989              :         return false;
     990              :     }
     991      2956139 :     if (isStopped() && myStops.begin()->pars.permitted.size() > 0
     992      1478125 :             && myStops.begin()->pars.permitted.count(t->getID()) == 0) {
     993         6181 :         return false;
     994              :     }
     995      1471944 :     MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(getDevice(typeid(MSDevice_Taxi)));
     996              :     if (taxiDevice != nullptr) {
     997        27070 :         return taxiDevice->allowsBoarding(t);
     998              :     }
     999              :     return true;
    1000              : }
    1001              : 
    1002              : 
    1003              : void
    1004      1336116 : MSBaseVehicle::addTransportable(MSTransportable* transportable) {
    1005      1336116 :     if (transportable->isPerson()) {
    1006        12815 :         if (myPersonDevice == nullptr) {
    1007         4472 :             myPersonDevice = MSDevice_Transportable::buildVehicleDevices(*this, myDevices, false);
    1008         4472 :             myMoveReminders.insert(myMoveReminders.begin(), std::make_pair(myPersonDevice, 0.));
    1009         4472 :             if (myParameter->departProcedure == DepartDefinition::TRIGGERED && myParameter->depart == -1) {
    1010         1938 :                 const_cast<SUMOVehicleParameter*>(myParameter)->depart = MSNet::getInstance()->getCurrentTimeStep();
    1011              :             }
    1012              :         }
    1013        12815 :         myPersonDevice->addTransportable(transportable);
    1014              :     } else {
    1015      1323301 :         if (myContainerDevice == nullptr) {
    1016      1322867 :             myContainerDevice = MSDevice_Transportable::buildVehicleDevices(*this, myDevices, true);
    1017          424 :             myMoveReminders.insert(myMoveReminders.begin(), std::make_pair(myContainerDevice, 0.));
    1018          424 :             if (myParameter->departProcedure == DepartDefinition::CONTAINER_TRIGGERED && myParameter->depart == -1) {
    1019           79 :                 const_cast<SUMOVehicleParameter*>(myParameter)->depart = MSNet::getInstance()->getCurrentTimeStep();
    1020              :             }
    1021              :         }
    1022          858 :         myContainerDevice->addTransportable(transportable);
    1023              :     }
    1024        13673 :     if (myEnergyParams != nullptr) {
    1025         8844 :         myEnergyParams->setTransportableMass(myEnergyParams->getTransportableMass() + transportable->getVehicleType().getMass());
    1026              :     }
    1027        13673 : }
    1028              : 
    1029              : 
    1030              : bool
    1031         2906 : MSBaseVehicle::hasJump(const MSRouteIterator& it) const {
    1032         3367 :     for (const MSStop& stop : myStops) {
    1033         1127 :         if (stop.edge == it && stop.pars.jump >= 0) {
    1034              :             return true;
    1035         1007 :         } else if (stop.edge > it) {
    1036              :             return false;
    1037              :         }
    1038              :     }
    1039              :     return false;
    1040              : }
    1041              : 
    1042              : 
    1043              : bool
    1044      4477401 : MSBaseVehicle::hasValidRoute(std::string& msg, ConstMSRoutePtr route) const {
    1045      4477401 :     MSRouteIterator start = myCurrEdge;
    1046      4477401 :     if (route == nullptr) {
    1047              :         route = myRoute;
    1048              :     } else {
    1049      3783410 :         start = route->begin();
    1050              :     }
    1051              :     const bool checkJumps = route == myRoute;  // the edge iterators in the stops are invalid otherwise
    1052      4477401 :     return hasValidRoute(msg, start, route->end(), checkJumps);
    1053              : }
    1054              : 
    1055              : 
    1056              : bool
    1057      5048754 : MSBaseVehicle::hasValidRoute(std::string& msg, MSRouteIterator start, MSRouteIterator last, bool checkJumps) const {
    1058              :     MSRouteIterator lastValid = last - 1;
    1059              :     // check connectivity, first
    1060     22761777 :     for (MSRouteIterator e = start; e != lastValid; ++e) {
    1061     17762337 :         const MSEdge& next = **(e + 1);
    1062     17762337 :         if ((*e)->allowedLanes(next, myType->getVehicleClass()) == nullptr) {
    1063        49990 :             if (!checkJumps || !hasJump(e)) {
    1064        49870 :                 if ((myRoutingMode & libsumo::ROUTING_MODE_IGNORE_TRANSIENT_PERMISSIONS) == 0
    1065        49870 :                         || (!next.hasTransientPermissions() && !(*e)->hasTransientPermissions())) {
    1066       147942 :                     msg = TLF("No connection between edge '%' and edge '%'.", (*e)->getID(), (*(e + 1))->getID());
    1067        49314 :                     return false;
    1068              :                 }
    1069              :             }
    1070              :         }
    1071              :     }
    1072              :     // check usable lanes, then
    1073     27710991 :     for (MSRouteIterator e = start; e != last; ++e) {
    1074     22711551 :         if ((*e)->prohibits(this)) {
    1075            0 :             msg = TLF("Edge '%' prohibits.", (*e)->getID());
    1076              :             return false;
    1077              :         }
    1078              :     }
    1079              :     return true;
    1080              : }
    1081              : 
    1082              : 
    1083              : bool
    1084    503689182 : MSBaseVehicle::hasValidRouteStart(std::string& msg) {
    1085    503689182 :     if (!(*myCurrEdge)->isTazConnector()) {
    1086    503552120 :         if (myParameter->departSpeedProcedure == DepartSpeedDefinition::GIVEN && myParameter->departSpeed > myType->getMaxSpeed() + SPEED_EPS) {
    1087           12 :             msg = TLF("Departure speed for vehicle '%' is too high for the vehicle type '%'.", getID(), myType->getID());
    1088            6 :             myRouteValidity |= ROUTE_START_INVALID_LANE;
    1089            6 :             return false;
    1090              :         }
    1091              :     }
    1092    503689176 :     if (myRoute->getEdges().size() > 0 && !(*myCurrEdge)->prohibits(this)) {
    1093    503689118 :         myRouteValidity &= ~ROUTE_START_INVALID_PERMISSIONS;
    1094    503689118 :         return true;
    1095              :     } else {
    1096          174 :         msg = TLF("Vehicle '%' is not allowed to depart on any lane of edge '%'.", getID(), (*myCurrEdge)->getID());
    1097           58 :         myRouteValidity |= ROUTE_START_INVALID_PERMISSIONS;
    1098           58 :         return false;
    1099              :     }
    1100              : }
    1101              : 
    1102              : 
    1103              : int
    1104   5038804573 : MSBaseVehicle::getRouteValidity(bool update, bool silent, std::string* msgReturn) {
    1105   5038804573 :     if (!update) {
    1106   2135368831 :         return myRouteValidity;
    1107              :     }
    1108              :     // insertion check must be done in any case
    1109              :     std::string msg;
    1110   2903435742 :     if (!hasValidRouteStart(msg)) {
    1111          186 :         if (MSGlobals::gCheckRoutes) {
    1112          114 :             throw ProcessError(msg);
    1113           72 :         } else if (!silent) {
    1114              :             // vehicle will be discarded
    1115          138 :             WRITE_WARNING(msg);
    1116           26 :         } else if (msgReturn != nullptr) {
    1117              :             *msgReturn = msg;
    1118              :         }
    1119              :     }
    1120        20620 :     if ((MSGlobals::gCheckRoutes || myRoute->getFirstEdge()->isInternal())
    1121   2903415014 :             && (myRouteValidity & ROUTE_UNCHECKED) != 0
    1122              :             // we could check after the first rerouting
    1123   2908668278 :             && (!myParameter->wasSet(VEHPARS_FORCE_REROUTE))) {
    1124      7468426 :         if (!hasValidRoute(msg, myRoute)) {
    1125           78 :             myRouteValidity |= ROUTE_INVALID;
    1126          234 :             throw ProcessError(TLF("Vehicle '%' has no valid route. %", getID(), msg));
    1127              :         }
    1128              :     }
    1129   2903435550 :     myRouteValidity &= ~ROUTE_UNCHECKED;
    1130              :     return myRouteValidity;
    1131              : }
    1132              : 
    1133              : 
    1134              : bool
    1135          114 : MSBaseVehicle::hasReminder(MSMoveReminder* rem) const {
    1136          423 :     for (auto item : myMoveReminders) {
    1137          344 :         if (item.first == rem) {
    1138              :             return true;
    1139              :         }
    1140              :     }
    1141           79 :     return false;
    1142              : }
    1143              : 
    1144              : 
    1145              : void
    1146     24931515 : MSBaseVehicle::addReminder(MSMoveReminder* rem, double pos) {
    1147              : #ifdef _DEBUG
    1148              :     if (myTraceMoveReminders) {
    1149              :         traceMoveReminder("add", rem, pos, true);
    1150              :     }
    1151              : #endif
    1152     24931515 :     myMoveReminders.push_back(std::make_pair(rem, pos));
    1153     24931515 : }
    1154              : 
    1155              : 
    1156              : void
    1157            0 : MSBaseVehicle::removeReminder(MSMoveReminder* rem) {
    1158            0 :     for (MoveReminderCont::iterator r = myMoveReminders.begin(); r != myMoveReminders.end(); ++r) {
    1159            0 :         if (r->first == rem) {
    1160              : #ifdef _DEBUG
    1161              :             if (myTraceMoveReminders) {
    1162              :                 traceMoveReminder("remove", rem, 0, false);
    1163              :             }
    1164              : #endif
    1165              :             myMoveReminders.erase(r);
    1166              :             return;
    1167              :         }
    1168              :     }
    1169              : }
    1170              : 
    1171              : 
    1172              : void
    1173     45933871 : MSBaseVehicle::activateReminders(const MSMoveReminder::Notification reason, const MSLane* enteredLane) {
    1174              :     // notifyEnter may cause new reminders to be added so we cannot use an iterator
    1175    129219964 :     for (int i = 0; i < (int)myMoveReminders.size();) {
    1176     83286096 :         MSMoveReminder* rem = myMoveReminders[i].first;
    1177     83286096 :         const double remPos = myMoveReminders[i].second;
    1178              :         // skip the reminder if it is a lane reminder but not for my lane (indicated by rem->second > 0.)
    1179     83286096 :         if (rem->getLane() != nullptr && remPos > 0.) {
    1180              : #ifdef _DEBUG
    1181              :             if (myTraceMoveReminders) {
    1182              :                 traceMoveReminder("notifyEnter_skipped", rem, remPos, true);
    1183              :             }
    1184              : #endif
    1185     16693262 :             ++i;
    1186              :         } else {
    1187     66592834 :             if (rem->notifyEnter(*this, reason, enteredLane)) {
    1188              : #ifdef _DEBUG
    1189              :                 if (myTraceMoveReminders) {
    1190              :                     traceMoveReminder("notifyEnter", rem, remPos, true);
    1191              :                 }
    1192              : #endif
    1193     64810469 :                 ++i;
    1194              :             } else {
    1195              : #ifdef _DEBUG
    1196              :                 if (myTraceMoveReminders) {
    1197              :                     traceMoveReminder("notifyEnter", rem, remPos, false);
    1198              :                 }
    1199              : #endif
    1200              :                 myMoveReminders.erase(myMoveReminders.begin() + i);
    1201              :             }
    1202              :         }
    1203              :     }
    1204     45933868 : }
    1205              : 
    1206              : 
    1207              : bool
    1208   2720773733 : MSBaseVehicle::isRail() const {
    1209   2720773733 :     return isRailway(getVClass()) || isRailway(getCurrentEdge()->getPermissions());
    1210              : }
    1211              : 
    1212              : void
    1213      8030091 : MSBaseVehicle::calculateArrivalParams(bool onInit) {
    1214      8030091 :     if (myRoute->getLastEdge()->isTazConnector()) {
    1215              :         return;
    1216              :     }
    1217      8030091 :     const int arrivalEdgeIndex = MIN2(myParameter->arrivalEdge, (int)myRoute->getEdges().size() - 1);
    1218      8030091 :     if (arrivalEdgeIndex != myParameter->arrivalEdge) {
    1219           42 :         WRITE_WARNINGF(TL("Vehicle '%' ignores attribute arrivalEdge=% after rerouting at time=% (routeLength=%)"),
    1220              :                        getID(), myParameter->arrivalEdge, time2string(SIMSTEP), myRoute->getEdges().size() - 1);
    1221              :     }
    1222      8030091 :     const MSEdge* arrivalEdge = myParameter->arrivalEdge >= 0 ? myRoute->getEdges()[arrivalEdgeIndex] : myRoute->getLastEdge();
    1223      8030091 :     if (!onInit) {
    1224       592655 :         arrivalEdge = myRoute->getLastEdge();
    1225              :         // ignore arrivalEdge parameter after rerouting
    1226       592655 :         const_cast<SUMOVehicleParameter*>(myParameter)->arrivalEdge = -1;
    1227              :     }
    1228              :     const std::vector<MSLane*>& lanes = arrivalEdge->getLanes();
    1229      8030091 :     const double lastLaneLength = lanes[0]->getLength();
    1230      8030091 :     switch (myParameter->arrivalPosProcedure) {
    1231       201538 :         case ArrivalPosDefinition::GIVEN:
    1232       201538 :             if (fabs(myParameter->arrivalPos) > lastLaneLength) {
    1233         3048 :                 WRITE_WARNINGF(TL("Vehicle '%' will not be able to arrive at the given position!"), getID());
    1234              :             }
    1235              :             // Maybe we should warn the user about invalid inputs!
    1236       201538 :             myArrivalPos = MIN2(myParameter->arrivalPos, lastLaneLength);
    1237       201538 :             if (myArrivalPos < 0) {
    1238        20597 :                 myArrivalPos = MAX2(myArrivalPos + lastLaneLength, 0.);
    1239              :             }
    1240              :             break;
    1241              :         case ArrivalPosDefinition::RANDOM:
    1242        74019 :             myArrivalPos = RandHelper::rand(lastLaneLength);
    1243        74019 :             break;
    1244            0 :         case ArrivalPosDefinition::CENTER:
    1245            0 :             myArrivalPos = lastLaneLength / 2.;
    1246            0 :             break;
    1247      7754534 :         default:
    1248      7754534 :             myArrivalPos = lastLaneLength;
    1249      7754534 :             break;
    1250              :     }
    1251      8030091 :     if (myParameter->arrivalLaneProcedure == ArrivalLaneDefinition::GIVEN) {
    1252       117503 :         if (myParameter->arrivalLane >= (int)lanes.size() || !lanes[myParameter->arrivalLane]->allowsVehicleClass(myType->getVehicleClass())) {
    1253       284376 :             WRITE_WARNINGF(TL("Vehicle '%' will not be able to arrive at the given lane '%_%'!"), getID(), arrivalEdge->getID(), toString(myParameter->arrivalLane));
    1254              :         }
    1255       117503 :         myArrivalLane = MIN2(myParameter->arrivalLane, (int)(lanes.size() - 1));
    1256      7912588 :     } else if (myParameter->arrivalLaneProcedure == ArrivalLaneDefinition::FIRST_ALLOWED) {
    1257            7 :         myArrivalLane = -1;
    1258            7 :         for (MSLane* lane : lanes) {
    1259            7 :             if (lane->allowsVehicleClass(myType->getVehicleClass())) {
    1260            7 :                 myArrivalLane = lane->getIndex();
    1261            7 :                 break;
    1262              :             }
    1263              :         }
    1264            7 :         if (myArrivalLane == -1) {
    1265            0 :             WRITE_WARNINGF(TL("Vehicle '%' has no usable arrivalLane on edge '%'."), getID(), arrivalEdge->getID());
    1266            0 :             myArrivalLane = 0;
    1267              :         }
    1268      7912581 :     } else if (myParameter->arrivalLaneProcedure == ArrivalLaneDefinition::RANDOM) {
    1269              :         // pick random lane among all usable lanes
    1270              :         std::vector<MSLane*> usable;
    1271       366745 :         for (MSLane* lane : lanes) {
    1272       281143 :             if (lane->allowsVehicleClass(myType->getVehicleClass())) {
    1273       263225 :                 usable.push_back(lane);
    1274              :             }
    1275              :         }
    1276        85602 :         if (usable.empty()) {
    1277            0 :             WRITE_WARNINGF(TL("Vehicle '%' has no usable arrivalLane on edge '%'."), getID(), arrivalEdge->getID());
    1278            0 :             myArrivalLane = 0;
    1279              :         } else {
    1280        85602 :             myArrivalLane = usable[RandHelper::rand(0, (int)usable.size())]->getIndex();
    1281              :         }
    1282        85602 :     }
    1283      8030091 :     if (myParameter->arrivalSpeedProcedure == ArrivalSpeedDefinition::GIVEN) {
    1284       212823 :         for (std::vector<MSLane*>::const_iterator l = lanes.begin(); l != lanes.end(); ++l) {
    1285       163742 :             if (myParameter->arrivalSpeed <= (*l)->getVehicleMaxSpeed(this)) {
    1286              :                 return;
    1287              :             }
    1288              :         }
    1289       147243 :         WRITE_WARNINGF(TL("Vehicle '%' will not be able to arrive with the given speed!"), getID());
    1290              :     }
    1291              : }
    1292              : 
    1293              : void
    1294      5989691 : MSBaseVehicle::setDepartAndArrivalEdge() {
    1295      5989691 :     SUMOVehicleParameter* pars = const_cast<SUMOVehicleParameter*>(myParameter);
    1296      5989691 :     if (pars->departEdgeProcedure != RouteIndexDefinition::DEFAULT) {
    1297         7982 :         const int routeEdges = (int)myRoute->getEdges().size();
    1298         7982 :         if (pars->departEdgeProcedure == RouteIndexDefinition::RANDOM) {
    1299              :             // write specific edge in vehroute output for reproducibility
    1300         4185 :             pars->departEdge = RandHelper::rand(0, routeEdges);
    1301         4185 :             pars->departEdgeProcedure = RouteIndexDefinition::GIVEN;
    1302              :         }
    1303              :         assert(pars->departEdge >= 0);
    1304         7982 :         if (pars->departEdge >= routeEdges) {
    1305            0 :             WRITE_WARNINGF(TL("Ignoring departEdge % for vehicle '%' with % route edges"), toString(pars->departEdge), getID(), toString(routeEdges));
    1306              :         } else {
    1307              :             myCurrEdge += pars->departEdge;
    1308              :         }
    1309              :     }
    1310      5989691 :     if (pars->arrivalEdgeProcedure == RouteIndexDefinition::RANDOM) {
    1311          147 :         const int routeEdges = (int)myRoute->getEdges().size();
    1312          147 :         const int begin = (int)(myCurrEdge - myRoute->begin());
    1313              :         // write specific edge in vehroute output for reproducibility
    1314          147 :         pars->arrivalEdge = RandHelper::rand(begin, routeEdges);
    1315          147 :         pars->arrivalEdgeProcedure = RouteIndexDefinition::GIVEN;
    1316              :         assert(pars->arrivalEdge >= begin);
    1317              :         assert(pars->arrivalEdge < routeEdges);
    1318              :     }
    1319      5989691 : }
    1320              : 
    1321              : int
    1322        12836 : MSBaseVehicle::getDepartEdge() const {
    1323        12836 :     return myParameter->departEdge <= myRoute->size() ? myParameter->departEdge : 0;
    1324              : }
    1325              : 
    1326              : int
    1327     28169531 : MSBaseVehicle::getInsertionChecks() const {
    1328     28169531 :     if (getParameter().wasSet(VEHPARS_INSERTION_CHECKS_SET)) {
    1329       257360 :         return getParameter().insertionChecks;
    1330              :     } else {
    1331     27912171 :         return MSGlobals::gInsertionChecks;
    1332              :     }
    1333              : }
    1334              : 
    1335              : double
    1336    604199296 : MSBaseVehicle::getImpatience() const {
    1337   1208398592 :     return MAX2(0., MIN2(1., getVehicleType().getImpatience()
    1338    604199296 :                          + (hasInfluencer() ? getBaseInfluencer()->getExtraImpatience() : 0)
    1339    604199296 :                          + (MSGlobals::gTimeToImpatience > 0 ? (double)getWaitingTime() / (double)MSGlobals::gTimeToImpatience : 0.)));
    1340              : }
    1341              : 
    1342              : 
    1343              : MSDevice*
    1344    926949506 : MSBaseVehicle::getDevice(const std::type_info& type) const {
    1345   1600329764 :     for (MSVehicleDevice* const dev : myDevices) {
    1346    730882029 :         if (typeid(*dev) == type) {
    1347     57501771 :             return dev;
    1348              :         }
    1349              :     }
    1350              :     return nullptr;
    1351              : }
    1352              : 
    1353              : 
    1354              : void
    1355         4267 : MSBaseVehicle::saveState(OutputDevice& out) {
    1356              :     // the parameters may hold the name of a vTypeDistribution but we are interested in the actual type
    1357         4267 :     const std::string& typeID = myParameter->vtypeid != getVehicleType().getID() ? getVehicleType().getID() : "";
    1358         4267 :     myParameter->write(out, OptionsCont::getOptions(), SUMO_TAG_VEHICLE, typeID);
    1359              :     // params and stops must be written in child classes since they may wish to add additional attributes first
    1360         4267 :     out.writeAttr(SUMO_ATTR_ROUTE, myRoute->getID());
    1361         4267 :     std::ostringstream os;
    1362         8534 :     os << myOdometer << " " << myNumberReroutes;
    1363         4267 :     out.writeAttr(SUMO_ATTR_DISTANCE, os.str());
    1364         4267 :     if (myParameter->arrivalPosProcedure == ArrivalPosDefinition::RANDOM) {
    1365           13 :         out.writeAttr(SUMO_ATTR_ARRIVALPOS_RANDOMIZED, myArrivalPos);
    1366              :     }
    1367         4267 :     if (!myParameter->wasSet(VEHPARS_SPEEDFACTOR_SET)) {
    1368              :         const int precision = out.getPrecision();
    1369         4267 :         out.setPrecision(MAX2(gPrecisionRandom, precision));
    1370         4267 :         out.writeAttr(SUMO_ATTR_SPEEDFACTOR, myChosenSpeedFactor);
    1371         4267 :         out.setPrecision(precision);
    1372              :     }
    1373         4267 :     if (myParameter->wasSet(VEHPARS_FORCE_REROUTE)) {
    1374         2882 :         out.writeAttr(SUMO_ATTR_REROUTE, true);
    1375              :     }
    1376         4267 :     if (!myParameter->wasSet(VEHPARS_LINE_SET) && myParameter->line != "") {
    1377              :         // could be set from stop
    1378            2 :         out.writeAttr(SUMO_ATTR_LINE, myParameter->line);
    1379              :     }
    1380              :     // here starts the vehicle internal part (see loading)
    1381              :     // @note: remember to close the vehicle tag when calling this in a subclass!
    1382         8534 : }
    1383              : 
    1384              : 
    1385              : bool
    1386            0 : MSBaseVehicle::handleCollisionStop(MSStop& stop, const double distToStop) {
    1387              :     UNUSED_PARAMETER(stop);
    1388              :     UNUSED_PARAMETER(distToStop);
    1389            0 :     return true;
    1390              : }
    1391              : 
    1392              : 
    1393              : bool
    1394   9147287315 : MSBaseVehicle::isStopped() const {
    1395   9147287315 :     return !myStops.empty() && myStops.begin()->reached /*&& myState.mySpeed < SUMO_const_haltingSpeed @todo #1864#*/;
    1396              : }
    1397              : 
    1398              : 
    1399              : bool
    1400   2563043825 : MSBaseVehicle::isParking() const {
    1401   2615863924 :     return (isStopped() && (myStops.begin()->pars.parking == ParkingType::OFFROAD)
    1402      1969422 :             && (myStops.begin()->parkingarea == nullptr || !myStops.begin()->parkingarea->parkOnRoad())
    1403   2564867188 :             && (myStops.begin()->getSpeed() == 0 || getSpeed() < SUMO_const_haltingSpeed));
    1404              : }
    1405              : 
    1406              : 
    1407              : bool
    1408    656032884 : MSBaseVehicle::isJumping() const {
    1409    656032884 :     return myPastStops.size() > 0 && myPastStops.back().jump >= 0 && getEdge()->getID() == myPastStops.back().edge && myPastStops.back().ended == SIMSTEP;
    1410              : }
    1411              : 
    1412              : 
    1413              : bool
    1414      7073138 : MSBaseVehicle::isStoppedTriggered() const {
    1415      7073138 :     return isStopped() && (myStops.begin()->triggered || myStops.begin()->containerTriggered || myStops.begin()->joinTriggered);
    1416              : }
    1417              : 
    1418              : 
    1419              : bool
    1420        27723 : MSBaseVehicle::isStoppedParking() const {
    1421        27723 :     return isStopped() && (myStops.begin()->pars.parking == ParkingType::OFFROAD);
    1422              : }
    1423              : 
    1424              : 
    1425              : bool
    1426      1340562 : MSBaseVehicle::isStoppedInRange(const double pos, const double tolerance, bool checkFuture) const {
    1427      1340562 :     if (isStopped() || (checkFuture && hasStops())) {
    1428              :         const MSStop& stop = myStops.front();
    1429      1341758 :         return stop.pars.startPos - tolerance <= pos && stop.pars.endPos + tolerance >= pos;
    1430              :     }
    1431              :     return false;
    1432              : }
    1433              : 
    1434              : bool
    1435        18237 : MSBaseVehicle::replaceParkingArea(MSParkingArea* parkingArea, std::string& errorMsg) {
    1436              :     // Check if there is a parking area to be replaced
    1437        18237 :     if (parkingArea == 0) {
    1438              :         errorMsg = "new parkingArea is NULL";
    1439            0 :         return false;
    1440              :     }
    1441        18237 :     if (myStops.size() == 0) {
    1442              :         errorMsg = "vehicle has no stops";
    1443            0 :         return false;
    1444              :     }
    1445        18237 :     if (myStops.front().parkingarea == 0) {
    1446              :         errorMsg = "first stop is not at parkingArea";
    1447            0 :         return false;
    1448              :     }
    1449              :     MSStop& first = myStops.front();
    1450              :     SUMOVehicleParameter::Stop& stopPar = const_cast<SUMOVehicleParameter::Stop&>(first.pars);
    1451        18237 :     std::string oldStopEdgeID = first.lane->getEdge().getID();
    1452              :     // merge subsequent duplicate stops equals to parking area
    1453        18256 :     for (std::list<MSStop>::iterator iter = ++myStops.begin(); iter != myStops.end();) {
    1454          292 :         if (iter->parkingarea == parkingArea) {
    1455           19 :             stopPar.duration += iter->duration;
    1456           19 :             myStops.erase(iter++);
    1457              :         } else {
    1458              :             break;
    1459              :         }
    1460              :     }
    1461        18237 :     stopPar.lane = parkingArea->getLane().getID();
    1462        18237 :     stopPar.parkingarea = parkingArea->getID();
    1463        18237 :     stopPar.startPos = parkingArea->getBeginLanePosition();
    1464        18237 :     stopPar.endPos = parkingArea->getEndLanePosition();
    1465        18237 :     first.edge = myRoute->end(); // will be patched in replaceRoute
    1466        18237 :     first.lane = &parkingArea->getLane();
    1467        18237 :     first.parkingarea = parkingArea;
    1468              : 
    1469              :     // patch via edges
    1470        18237 :     std::string newStopEdgeID = parkingArea->getLane().getEdge().getID();
    1471        18237 :     if (myParameter->via.size() > 0 && myParameter->via.front() != newStopEdgeID) {
    1472          210 :         myParameter->via.erase(myParameter->via.begin());
    1473          210 :         myParameter->via.insert(myParameter->via.begin(), newStopEdgeID);
    1474              :     }
    1475              :     return true;
    1476              : }
    1477              : 
    1478              : 
    1479              : MSParkingArea*
    1480        91510 : MSBaseVehicle::getNextParkingArea() {
    1481              :     MSParkingArea* nextParkingArea = nullptr;
    1482        91510 :     if (!myStops.empty()) {
    1483        90227 :         SUMOVehicleParameter::Stop stopPar;
    1484        90227 :         MSStop stop = myStops.front();
    1485        90227 :         if (!stop.reached && stop.parkingarea != nullptr) {
    1486              :             nextParkingArea = stop.parkingarea;
    1487              :         }
    1488        90227 :     }
    1489        91510 :     return nextParkingArea;
    1490              : }
    1491              : 
    1492              : 
    1493              : MSParkingArea*
    1494       118634 : MSBaseVehicle::getCurrentParkingArea() {
    1495              :     MSParkingArea* currentParkingArea = nullptr;
    1496       118634 :     if (isParking()) {
    1497       118565 :         currentParkingArea = myStops.begin()->parkingarea;
    1498              :     }
    1499       118634 :     return currentParkingArea;
    1500              : }
    1501              : 
    1502              : 
    1503              : const std::vector<std::string>&
    1504         1656 : MSBaseVehicle::getParkingBadges() const {
    1505         1656 :     if (myParameter->wasSet(VEHPARS_PARKING_BADGES_SET)) {
    1506            7 :         return myParameter->parkingBadges;
    1507              :     } else {
    1508         1649 :         return getVehicleType().getParkingBadges();
    1509              :     }
    1510              : }
    1511              : 
    1512              : 
    1513              : double
    1514      9877598 : MSBaseVehicle::basePos(const MSEdge* edge) const {
    1515      9877598 :     double result = MIN2(getVehicleType().getLength() + POSITION_EPS, edge->getLength());
    1516      9877598 :     if (hasStops()
    1517      9901437 :             && myStops.front().edge == myRoute->begin()
    1518      9901437 :             && (&myStops.front().lane->getEdge()) == *myStops.front().edge) {
    1519        23799 :         result = MIN2(result, MAX2(0.0, myStops.front().getEndPos(*this)));
    1520              :     }
    1521      9877598 :     return result;
    1522              : }
    1523              : 
    1524              : 
    1525              : MSLane*
    1526          298 : MSBaseVehicle::interpretOppositeStop(SUMOVehicleParameter::Stop& stop) {
    1527          298 :     const std::string edgeID = SUMOXMLDefinitions::getEdgeIDFromLane(stop.lane);
    1528          298 :     const MSEdge* edge = MSEdge::dictionary(edgeID);
    1529          298 :     if (edge == nullptr || edge->getOppositeEdge() == nullptr || stop.lane.find("_") == std::string::npos) {
    1530            8 :         return nullptr;
    1531              :     }
    1532          580 :     const int laneIndex = SUMOXMLDefinitions::getIndexFromLane(stop.lane);
    1533          290 :     if (laneIndex < (edge->getNumLanes() + edge->getOppositeEdge()->getNumLanes())) {
    1534          290 :         const int oppositeIndex = edge->getOppositeEdge()->getNumLanes() + edge->getNumLanes() - 1 - laneIndex;
    1535          290 :         stop.edge = edgeID;
    1536          290 :         return edge->getOppositeEdge()->getLanes()[oppositeIndex];
    1537              :     }
    1538              :     return nullptr;
    1539              : }
    1540              : 
    1541              : 
    1542              : bool
    1543       136986 : MSBaseVehicle::addStop(const SUMOVehicleParameter::Stop& stopPar, std::string& errorMsg, SUMOTime untilOffset,
    1544              :                        MSRouteIterator* searchStart) {
    1545       136986 :     MSStop stop(stopPar);
    1546       136986 :     if (stopPar.lane == "") {
    1547         2871 :         MSEdge* e = MSEdge::dictionary(stopPar.edge);
    1548         2871 :         stop.lane = e->getFirstAllowed(getVClass());
    1549         2871 :         if (stop.lane == nullptr) {
    1550            0 :             errorMsg = "Vehicle '" + myParameter->id + "' is not allowed to stop on any lane of edge '" + stopPar.edge + "'.";
    1551            0 :             return false;
    1552              :         }
    1553              :     } else {
    1554       134115 :         stop.lane = MSLane::dictionary(stopPar.lane);
    1555       134115 :         if (stop.lane == nullptr) {
    1556              :             // must be an opposite stop
    1557          145 :             SUMOVehicleParameter::Stop tmp = stopPar;
    1558          145 :             stop.lane = interpretOppositeStop(tmp);
    1559              :             assert(stop.lane != nullptr);
    1560          145 :         }
    1561       134115 :         if (!stop.lane->allowsVehicleClass(myType->getVehicleClass())) {
    1562           24 :             errorMsg = "Vehicle '" + myParameter->id + "' is not allowed to stop on lane '" + stopPar.lane + "'.";
    1563           12 :             return false;
    1564              :         }
    1565              :     }
    1566       136974 :     if (MSGlobals::gUseMesoSim) {
    1567        28337 :         stop.segment = MSGlobals::gMesoNet->getSegmentForEdge(stop.lane->getEdge(), stop.getEndPos(*this));
    1568        28337 :         if (stop.lane->isInternal()) {
    1569            2 :             errorMsg = "Mesoscopic simulation does not allow stopping on internal edge '" + stopPar.edge + "' for vehicle '" + myParameter->id + "'.";
    1570            1 :             return false;
    1571              :         }
    1572              :     }
    1573       136973 :     stop.initPars(stopPar);
    1574       136973 :     if (stopPar.until != -1) {
    1575              :         // !!! it would be much cleaner to invent a constructor for stops which takes "until" as an argument
    1576        52926 :         const_cast<SUMOVehicleParameter::Stop&>(stop.pars).until += untilOffset;
    1577              :     }
    1578       136973 :     if (stopPar.arrival != -1) {
    1579          620 :         const_cast<SUMOVehicleParameter::Stop&>(stop.pars).arrival += untilOffset;
    1580              :     }
    1581       136973 :     std::string stopType = "stop";
    1582       136973 :     std::string stopID = "";
    1583       136973 :     double parkingLength = stop.pars.endPos - stop.pars.startPos;
    1584       136973 :     if (stop.busstop != nullptr) {
    1585              :         stopType = "busStop";
    1586        37215 :         stopID = stop.busstop->getID();
    1587        37215 :         parkingLength = stop.busstop->getParkingLength();
    1588        99758 :     } else if (stop.containerstop != nullptr) {
    1589              :         stopType = "containerStop";
    1590         1046 :         stopID = stop.containerstop->getID();
    1591         1046 :         parkingLength = stop.containerstop->getParkingLength();
    1592        98712 :     } else if (stop.chargingStation != nullptr) {
    1593              :         stopType = "chargingStation";
    1594         8170 :         stopID = stop.chargingStation->getID();
    1595         8170 :         parkingLength = stop.chargingStation->getParkingLength();
    1596        90542 :     } else if (stop.overheadWireSegment != nullptr) {
    1597              :         stopType = "overheadWireSegment";
    1598            0 :         stopID = stop.overheadWireSegment->getID();
    1599            0 :         parkingLength = stop.overheadWireSegment->getParkingLength();
    1600        90542 :     } else if (stop.parkingarea != nullptr) {
    1601              :         stopType = "parkingArea";
    1602        28799 :         stopID = stop.parkingarea->getID();
    1603              :     }
    1604       273946 :     const std::string errorMsgStart = stopID == "" ? stopType : stopType + " '" + stopID + "'";
    1605              : 
    1606       136973 :     if (stop.pars.startPos < 0 || stop.pars.endPos > stop.lane->getLength()) {
    1607            0 :         errorMsg = errorMsgStart + " for vehicle '" + myParameter->id + "' on lane '" + stop.lane->getID() + "' has an invalid position.";
    1608            0 :         return false;
    1609              :     }
    1610        75230 :     if (stopType != "stop" && stopType != "parkingArea" && myType->getLength() / 2. > parkingLength
    1611              :             // do not warn for stops that fill the whole lane
    1612          307 :             && parkingLength < stop.lane->getLength()
    1613              :             // do not warn twice for the same stop
    1614       213040 :             && MSNet::getInstance()->warnOnce(stopType + ":" + stopID)) {
    1615          837 :         errorMsg = errorMsgStart + " on lane '" + stop.lane->getID() + "' is too short for vehicle '" + myParameter->id + "'.";
    1616              :     }
    1617       136973 :     if (stopType == "parkingArea" && !stop.parkingarea->accepts(this)) {
    1618              :         // forbid access in case the parking requests other badges
    1619           14 :         errorMsg = errorMsgStart + "on lane '" + stop.lane->getID() + "' forbids access because vehicle '" + myParameter->id + "' does not provide any valid badge.";
    1620            7 :         return false;
    1621              :     }
    1622       136966 :     const MSEdge* stopLaneEdge = &stop.lane->getEdge();
    1623              :     const MSEdge* stopEdge;
    1624       136966 :     if (stopLaneEdge->getOppositeEdge() != nullptr && stopLaneEdge->getOppositeEdge()->getID() == stopPar.edge) {
    1625              :         // stop lane is on the opposite side
    1626          145 :         stopEdge = stopLaneEdge->getOppositeEdge();
    1627          145 :         stop.isOpposite = true;
    1628              :     } else {
    1629              :         // if stop is on an internal edge the normal edge before the intersection is used
    1630       136821 :         stopEdge = stopLaneEdge->getNormalBefore();
    1631              :     }
    1632       136966 :     MSRouteIterator succ = myCurrEdge + 1; // we're using the address but only within the scope of this function (and recursive calls)
    1633       136966 :     if (searchStart == nullptr) {
    1634       130135 :         searchStart = &myCurrEdge;
    1635       130135 :         if (stopLaneEdge->isNormal() && getLane() != nullptr && getLane()->isInternal()) {
    1636              :             // already on the intersection but myCurrEdge is before it
    1637              :             searchStart = &succ;
    1638              :         }
    1639              :     }
    1640              : #ifdef DEBUG_ADD_STOP
    1641              :     if (DEBUG_COND) {
    1642              :         std::cout << "addStop desc=" << stop.getDescription() << " stopEdge=" << stopEdge->getID()
    1643              :                   << " searchStart=" << ((*searchStart) == myRoute->end() ? "END" : (**searchStart)->getID())
    1644              :                   << " index=" << (int)((*searchStart) - myRoute->begin()) << " route=" << toString(myRoute->getEdges())
    1645              :                   << "\n";
    1646              :     }
    1647              : #endif
    1648       136966 :     stop.edge = std::find(*searchStart, myRoute->end(), stopEdge);
    1649       136966 :     MSRouteIterator prevStopEdge = myCurrEdge;
    1650       136966 :     const MSEdge* prevEdge = (getLane() == nullptr ? getEdge() : &getLane()->getEdge());
    1651       136966 :     double prevStopPos = getPositionOnLane();
    1652              :     // where to insert the stop
    1653              :     std::list<MSStop>::iterator iter = myStops.begin();
    1654       136966 :     if (stopPar.index == STOP_INDEX_END || stopPar.index >= static_cast<int>(myStops.size()) || stopPar.index == STOP_INDEX_REPEAT) {
    1655              :         iter = myStops.end();
    1656       114188 :         if (myStops.size() > 0 && myStops.back().edge >= *searchStart) {
    1657              :             prevStopEdge = myStops.back().edge;
    1658        48734 :             prevEdge = &myStops.back().lane->getEdge();
    1659        48734 :             prevStopPos = myStops.back().pars.endPos;
    1660        48734 :             stop.edge = std::find(prevStopEdge, myRoute->end(), stopEdge);
    1661              :             if (prevStopEdge == stop.edge                // laneEdge check is insufficient for looped routes
    1662         8007 :                     && prevEdge == &stop.lane->getEdge() // route iterator check insufficient for internal lane stops
    1663        56729 :                     && (prevStopPos > stop.pars.endPos ||
    1664         2233 :                         (prevStopPos == stop.pars.endPos && stopPar.index == STOP_INDEX_REPEAT))) {
    1665          434 :                 stop.edge = std::find(prevStopEdge + 1, myRoute->end(), stopEdge);
    1666              :             }
    1667              : #ifdef DEBUG_ADD_STOP
    1668              :             if (DEBUG_COND) {
    1669              :                 std::cout << " (@end) prevStopEdge=" << (*prevStopEdge)->getID() << " prevStopPos=" << prevStopPos << " index=" << (int)(prevStopEdge - myRoute->begin())
    1670              :                           << " foundIndex=" << (stop.edge == myRoute->end() ? -1 : (int)(stop.edge - myRoute->begin())) << "\n";
    1671              :             }
    1672              : #endif
    1673              :         }
    1674              :         // skip a number of occurences of stopEdge in looped route
    1675       114188 :         int skipLooped = stopPar.index - static_cast<int>(myStops.size());
    1676       114289 :         for (int j = 0; j < skipLooped; j++) {
    1677          247 :             auto nextIt = std::find(stop.edge + 1, myRoute->end(), stopEdge);
    1678          247 :             if (nextIt == myRoute->end()) {
    1679          146 :                 if (std::find(myRoute->begin(), stop.edge, stopEdge) != stop.edge) {
    1680              :                     // only warn if the route loops over the stop edge at least once
    1681           28 :                     errorMsg = errorMsgStart + " for vehicle '" + myParameter->id + "' could not skip " + toString(skipLooped) + " occurences of stop edge '" + stopEdge->getID() + "' in looped route.";
    1682              :                 }
    1683          146 :                 break;
    1684              :             } else {
    1685          101 :                 stop.edge = nextIt;
    1686              :             }
    1687              :         }
    1688              :     } else {
    1689        22778 :         if (stopPar.index == STOP_INDEX_FIT) {
    1690        34541 :             while (iter != myStops.end() && (iter->edge < stop.edge ||
    1691         1215 :                                              (iter->pars.endPos < stop.pars.endPos && iter->edge == stop.edge) ||
    1692         1215 :                                              (stop.lane->getEdge().isInternal() && iter->edge == stop.edge))) {
    1693              :                 prevStopEdge = iter->edge;
    1694        11862 :                 prevStopPos = iter->pars.endPos;
    1695              :                 ++iter;
    1696              :             }
    1697              :         } else {
    1698              :             int index = stopPar.index;
    1699           99 :             while (index > 0) {
    1700            0 :                 prevStopEdge = iter->edge;
    1701            0 :                 prevStopPos = iter->pars.endPos;
    1702              :                 ++iter;
    1703            0 :                 --index;
    1704              :             }
    1705              : #ifdef DEBUG_ADD_STOP
    1706              :             if (DEBUG_COND) {
    1707              :                 std::cout << " (@fit) prevStopEdge=" << (*prevStopEdge)->getID() << " index=" << (int)(prevStopEdge - myRoute->begin()) << "\n";
    1708              :             }
    1709              : #endif
    1710           99 :             stop.edge = std::find(prevStopEdge, myRoute->end(), stopEdge);
    1711              :         }
    1712              :     }
    1713       137280 :     const bool wasTooClose = errorMsg != "" && errorMsg.find("too close") != std::string::npos;
    1714       136966 :     if (stop.edge == myRoute->end()) {
    1715           78 :         if (!wasTooClose) {
    1716          210 :             errorMsg = errorMsgStart + " for vehicle '" + myParameter->id + "' on lane '" + stop.lane->getID() + "' is not downstream the current route.";
    1717              :         }
    1718           78 :         return false;
    1719              :     }
    1720              : 
    1721       136888 :     const bool tooClose = (prevStopEdge == stop.edge && prevEdge == &stop.lane->getEdge() &&
    1722        34707 :                            prevStopPos + (iter == myStops.begin() ? getBrakeGap() : 0) > stop.pars.endPos + POSITION_EPS);
    1723              : 
    1724       136888 :     if (prevStopEdge > stop.edge ||
    1725              :             // a collision-stop happens after vehicle movement and may move the
    1726              :             // vehicle backwards on its lane (prevStopPos is the vehicle position)
    1727         1734 :             (tooClose && !stop.pars.collision)
    1728       273748 :             || (stop.lane->getEdge().isInternal() && stop.lane->getNextNormal() != *(stop.edge + 1))) {
    1729              :         // check if the edge occurs again later in the route
    1730              :         //std::cout << " could not add stop " << errorMsgStart << " prevStops=" << myStops.size() << " searchStart=" << (*searchStart - myRoute->begin()) << " route=" << toString(myRoute->getEdges())  << "\n";
    1731           28 :         if (tooClose && prevStopPos <= stop.pars.endPos + POSITION_EPS) {
    1732           84 :             errorMsg = errorMsgStart + " for vehicle '" + myParameter->id + "' on lane '" + stop.pars.lane + "' is too close to brake.";
    1733              :         }
    1734           28 :         MSRouteIterator next = stop.edge + 1;
    1735           28 :         return addStop(stopPar, errorMsg, untilOffset, &next);
    1736              :     }
    1737       136860 :     if (wasTooClose) {
    1738              :         errorMsg = "";
    1739              :     }
    1740              :     // David.C:
    1741              :     //if (!stop.parking && (myCurrEdge == stop.edge && myState.myPos > stop.endPos - getCarFollowModel().brakeGap(myState.mySpeed))) {
    1742       136860 :     const double endPosOffset = stop.lane->getEdge().isInternal() ? (*stop.edge)->getLength() : 0;
    1743       136860 :     const double distToStop = stop.pars.endPos + endPosOffset - getPositionOnLane();
    1744       136860 :     if (stop.pars.collision && !handleCollisionStop(stop, distToStop)) {
    1745              :         return false;
    1746              :     }
    1747       136860 :     if (!hasDeparted() && myCurrEdge == stop.edge) {
    1748              :         double pos = -1;
    1749        25923 :         if (myParameter->departPosProcedure == DepartPosDefinition::GIVEN) {
    1750         4658 :             pos = myParameter->departPos;
    1751         4658 :             if (pos < 0.) {
    1752          219 :                 pos += (*myCurrEdge)->getLength();
    1753              :             }
    1754              :         }
    1755        25923 :         if (myParameter->departPosProcedure == DepartPosDefinition::BASE || myParameter->departPosProcedure == DepartPosDefinition::DEFAULT) {
    1756        18131 :             pos = MIN2(stop.pars.endPos + endPosOffset, basePos(*myCurrEdge));
    1757              :         }
    1758        25923 :         if (pos > stop.pars.endPos + endPosOffset) {
    1759           20 :             if (stop.edge != myRoute->end()) {
    1760              :                 // check if the edge occurs again later in the route
    1761           20 :                 MSRouteIterator next = stop.edge + 1;
    1762           20 :                 return addStop(stopPar, errorMsg, untilOffset, &next);
    1763              :             }
    1764            0 :             errorMsg = errorMsgStart + " for vehicle '" + myParameter->id + "' on lane '" + stop.lane->getID() + "' is before departPos.";
    1765            0 :             return false;
    1766              :         }
    1767              :     }
    1768       136840 :     if (iter != myStops.begin()) {
    1769              :         std::list<MSStop>::iterator iter2 = iter;
    1770              :         iter2--;
    1771        95757 :         if (stop.getUntil() >= 0 && iter2->getUntil() > stop.getUntil()
    1772        60577 :                 && (!MSGlobals::gUseStopEnded || iter2->pars.ended < 0 || stop.pars.ended >= 0)) {
    1773            6 :             errorMsg = errorMsgStart + " for vehicle '" + myParameter->id + "' on lane '" + stop.lane->getID()
    1774           18 :                        + "' set to end at " + time2string(stop.getUntil())
    1775           24 :                        + " earlier than previous stop at " + time2string(iter2->getUntil()) + ".";
    1776              :         }
    1777        60560 :         if (stop.pars.arrival >= 0 && iter2->pars.arrival > stop.pars.arrival) {
    1778            6 :             errorMsg = errorMsgStart + " for vehicle '" + myParameter->id + "' on lane '" + stop.lane->getID()
    1779           18 :                        + "' set to start at " + time2string(stop.pars.arrival)
    1780           24 :                        + " earlier than previous stop end at " + time2string(iter2->getUntil()) + ".";
    1781              :         }
    1782        60560 :         if (stop.pars.arrival >= 0 && iter2->pars.arrival > stop.pars.arrival) {
    1783            6 :             errorMsg = errorMsgStart + " for vehicle '" + myParameter->id + "' on lane '" + stop.lane->getID()
    1784           18 :                        + "' set to start at " + time2string(stop.pars.arrival)
    1785           24 :                        + " earlier than previous stop arrival at " + time2string(iter2->pars.arrival) + ".";
    1786              :         }
    1787              :     } else {
    1788        94005 :         if (stop.getUntil() >= 0 && getParameter().depart > stop.getUntil()
    1789        76292 :                 && (!MSGlobals::gUseStopEnded || stop.pars.ended < 0)) {
    1790            6 :             errorMsg = errorMsgStart + " for vehicle '" + myParameter->id + "' on lane '" + stop.lane->getID()
    1791           18 :                        + "' set to end at " + time2string(stop.getUntil())
    1792           24 :                        + " earlier than departure at " + time2string(getParameter().depart) + ".";
    1793              :         }
    1794              :     }
    1795       136840 :     if (stop.getUntil() >= 0 && stop.getArrival() > stop.getUntil() && errorMsg == "") {
    1796           18 :         errorMsg = errorMsgStart + " for vehicle '" + myParameter->id + "' on lane '" + stop.lane->getID()
    1797           54 :                    + "' set to end at " + time2string(stop.getUntil())
    1798           72 :                    + " earlier than arrival at " + time2string(stop.getArrival()) + ".";
    1799              :     }
    1800       136840 :     setSkips(stop, (int)myStops.size());
    1801       136840 :     myStops.insert(iter, stop);
    1802       136840 :     if (stopPar.tripId != "") {
    1803         2664 :         MSRailSignalConstraint::storeTripId(stopPar.tripId, getID());
    1804              :     }
    1805              :     //std::cout << " added stop " << errorMsgStart << " totalStops=" << myStops.size() << " searchStart=" << (*searchStart - myRoute->begin())
    1806              :     //    << " routeIndex=" << (stop.edge - myRoute->begin())
    1807              :     //    << " stopIndex=" << std::distance(myStops.begin(), iter)
    1808              :     //    << " route=" << toString(myRoute->getEdges())  << "\n";
    1809              :     return true;
    1810              : }
    1811              : 
    1812              : 
    1813              : void
    1814       173712 : MSBaseVehicle::setSkips(MSStop& stop, int prevActiveStops) {
    1815       173712 :     if (hasDeparted() && stop.edge > myRoute->begin()) {
    1816              :         // if the route is looped we must patch the index to ensure that state
    1817              :         // loading (and vehroute-output) encode the correct number of skips
    1818              :         int foundSkips = 0;
    1819              :         MSRouteIterator itPrev;
    1820              :         double prevEndPos;
    1821        69973 :         if (prevActiveStops > 0) {
    1822              :             assert((int)myStops.size() >= prevActiveStops);
    1823              :             auto prevStopIt = myStops.begin();
    1824        34287 :             std::advance(prevStopIt, prevActiveStops - 1);
    1825              :             const MSStop& prev = *prevStopIt;
    1826        34287 :             itPrev = prev.edge;
    1827        34287 :             prevEndPos = prev.pars.endPos;
    1828        35686 :         } else if (myPastStops.size() > 0) {
    1829        10879 :             itPrev = myRoute->begin() + myPastStops.back().routeIndex;
    1830        10879 :             prevEndPos = myPastStops.back().endPos;
    1831              :         } else {
    1832        24807 :             itPrev = myRoute->begin() + myParameter->departEdge;
    1833        24807 :             prevEndPos = myDepartPos;
    1834              :         }
    1835              :         //auto itPrevOrig = itPrev;
    1836        69973 :         if (*itPrev == *stop.edge && prevEndPos > stop.pars.endPos) {
    1837              :             itPrev++;
    1838              :         }
    1839              :         //std::cout << SIMTIME << " veh=" << getID() << " prevActive=" << prevActiveStops << " edge=" << (*stop.edge)->getID() << " routeIndex=" << (stop.edge - myRoute->begin()) << " prevIndex=" << (itPrev - myRoute->begin()) << "\n";
    1840       683739 :         while (itPrev < stop.edge) {
    1841       613766 :             if (*itPrev == *stop.edge) {
    1842        37373 :                 foundSkips++;
    1843              :             }
    1844              :             itPrev++;
    1845              :         }
    1846              :         int newIndex = STOP_INDEX_END;
    1847        69973 :         if (foundSkips > 0) {
    1848              :             //if (getID() == "77_0_0") {
    1849              :             //    std::cout << SIMTIME << " veh=" << getID() << " past=" << myPastStops.size() << " prevActive=" << prevActiveStops
    1850              :             //        << " edge=" << (*stop.edge)->getID() << " routeIndex=" << (stop.edge - myRoute->begin())
    1851              :             //        << " prevEdge=" << (*itPrevOrig)->getID()
    1852              :             //        << " prevIndex=" << (itPrevOrig - myRoute->begin())
    1853              :             //        << " skips=" << foundSkips << "\n";
    1854              :             //}
    1855        12625 :             newIndex = (int)myPastStops.size() + prevActiveStops + foundSkips;
    1856              :         }
    1857        69973 :         const_cast<SUMOVehicleParameter::Stop&>(stop.pars).index = newIndex;
    1858              :     }
    1859       173712 : }
    1860              : 
    1861              : 
    1862              : SUMOTime
    1863          744 : MSBaseVehicle::activateRemindersOnReroute(SUMOTime /*currentTime*/) {
    1864         3377 :     for (int i = 0; i < (int)myMoveReminders.size();) {
    1865         2633 :         auto rem = &myMoveReminders[i];
    1866         2633 :         if (rem->first->notifyReroute(*this)) {
    1867              : #ifdef _DEBUG
    1868              :             if (myTraceMoveReminders) {
    1869              :                 traceMoveReminder("notifyReroute", rem->first, rem->second, true);
    1870              :             }
    1871              : #endif
    1872         2579 :             ++i;
    1873              :         } else {
    1874              : #ifdef _DEBUG
    1875              :             if (myTraceMoveReminders) {
    1876              :                 traceMoveReminder("notifyReroute", rem->first, rem->second, false);
    1877              :             }
    1878              : #endif
    1879              :             myMoveReminders.erase(myMoveReminders.begin() + i);
    1880              :         }
    1881              :     }
    1882          744 :     resetApproachOnReroute();
    1883              :     // event only called once
    1884          744 :     return 0;
    1885              : }
    1886              : 
    1887              : 
    1888              : void
    1889      6816941 : MSBaseVehicle::addStops(const bool ignoreStopErrors, MSRouteIterator* searchStart, bool addRouteStops) {
    1890      6816941 :     if (addRouteStops) {
    1891      6821216 :         for (const SUMOVehicleParameter::Stop& stop : myRoute->getStops()) {
    1892              :             std::string errorMsg;
    1893         9831 :             if (!addStop(stop, errorMsg, myParameter->depart, searchStart) && !ignoreStopErrors) {
    1894            6 :                 throw ProcessError(errorMsg);
    1895              :             }
    1896         9825 :             if (errorMsg != "") {
    1897          687 :                 WRITE_WARNING(errorMsg);
    1898              :             }
    1899              :         }
    1900              :     }
    1901      6816935 :     const SUMOTime untilOffset = myParameter->repetitionOffset > 0 ? myParameter->repetitionsDone * myParameter->repetitionOffset : 0;
    1902      6914324 :     for (const SUMOVehicleParameter::Stop& stop : myParameter->stops) {
    1903              :         std::string errorMsg;
    1904        97418 :         if (!addStop(stop, errorMsg, untilOffset, searchStart) && !ignoreStopErrors) {
    1905           29 :             throw ProcessError(errorMsg);
    1906              :         }
    1907        97389 :         if (errorMsg != "") {
    1908          464 :             WRITE_WARNING(errorMsg);
    1909              :         }
    1910              :     }
    1911      6816906 : }
    1912              : 
    1913              : 
    1914              : bool
    1915       746477 : MSBaseVehicle::haveValidStopEdges(bool silent) const {
    1916       746477 :     MSRouteIterator start = myCurrEdge;
    1917              :     int i = 0;
    1918              :     bool ok = true;
    1919       769591 :     for (const MSStop& stop : myStops) {
    1920              :         MSRouteIterator it;
    1921        23114 :         if (stop.lane->isInternal()) {
    1922              :             // find the normal predecessor and ensure that the next route edge
    1923              :             // matches the successor of the internal edge successor
    1924            0 :             it = std::find(start, myRoute->end(), stop.lane->getEdge().getNormalBefore());
    1925            0 :             if (it != myRoute->end() && (
    1926            0 :                         it + 1 == myRoute->end() || *(it + 1) != stop.lane->getEdge().getNormalSuccessor())) {
    1927            0 :                 it = myRoute->end(); // signal failure
    1928              :             }
    1929              :         } else {
    1930        23114 :             it = std::find(start, myRoute->end(), &stop.lane->getEdge());
    1931              :         }
    1932        23114 :         if (it == myRoute->end()) {
    1933            0 :             if (!silent) {
    1934            0 :                 WRITE_ERRORF("Stop % on edge '%' is not found after edge '%' (% after current) for vehicle '%' at time=%.",
    1935              :                              i, stop.lane->getEdge().getID(), (*start)->getID(), toString(start - myCurrEdge), getID(), time2string(SIMSTEP));
    1936              :             }
    1937              :             ok = false;
    1938              :         } else {
    1939              :             MSRouteIterator it2;
    1940      1037556 :             for (it2 = myRoute->begin(); it2 != myRoute->end(); it2++) {
    1941      1030637 :                 if (it2 == stop.edge) {
    1942              :                     break;
    1943              :                 }
    1944              :             }
    1945        23114 :             if (it2 == myRoute->end()) {
    1946         6919 :                 if (!silent) {
    1947            0 :                     WRITE_ERRORF("Stop % on edge '%' used invalid route index for vehicle '%' at time=%.",
    1948              :                                  i, stop.lane->getEdge().getID(), getID(), time2string(SIMSTEP));
    1949              :                 }
    1950              :                 ok = false;
    1951        16195 :             } else if (it2 < start) {
    1952            0 :                 if (!silent) {
    1953            0 :                     WRITE_ERRORF("Stop % on edge '%' used invalid (relative) route index % expected after % for vehicle '%' at time=%.",
    1954              :                                  i, stop.lane->getEdge().getID(), toString(it2 - myCurrEdge), toString(start - myCurrEdge), getID(), time2string(SIMSTEP));
    1955              :                 }
    1956              :                 ok = false;
    1957              :             } else {
    1958        16195 :                 start = stop.edge;
    1959              :             }
    1960              :         }
    1961        23114 :         i++;
    1962              :     }
    1963       746477 :     return ok;
    1964              : }
    1965              : 
    1966              : 
    1967              : std::vector<MSBaseVehicle::StopEdgeInfo>
    1968      2777986 : MSBaseVehicle::getStopEdges(double& firstPos, double& lastPos, std::set<int>& jumps) const {
    1969              :     assert(haveValidStopEdges());
    1970              :     std::vector<StopEdgeInfo> result;
    1971              :     const MSStop* prev = nullptr;
    1972              :     const MSEdge* internalSuccessor = nullptr;
    1973      2872015 :     for (const MSStop& stop : myStops) {
    1974        94029 :         if (stop.reached) {
    1975         9585 :             if (stop.pars.jump >= 0) {
    1976            0 :                 jumps.insert((int)result.size());
    1977              :             }
    1978         9585 :             continue;
    1979              :         }
    1980        84444 :         const double stopPos = stop.getEndPos(*this);
    1981              :         if ((prev == nullptr
    1982        45064 :                 || prev->edge != stop.edge
    1983         1260 :                 || (prev->lane == stop.lane && prev->getEndPos(*this) > stopPos))
    1984       128248 :                 && *stop.edge != internalSuccessor) {
    1985        83184 :             result.push_back(StopEdgeInfo(*stop.edge, stop.pars.priority, stop.getArrivalFallback(), stopPos));
    1986        83184 :             if (stop.lane->isInternal()) {
    1987            5 :                 internalSuccessor = stop.lane->getNextNormal();
    1988            5 :                 result.push_back(StopEdgeInfo(internalSuccessor, stop.pars.priority, stop.getArrivalFallback(), stopPos));
    1989              :             } else {
    1990              :                 internalSuccessor = nullptr;
    1991              :             }
    1992         1260 :         } else if (prev != nullptr && prev->edge == stop.edge) {
    1993         1260 :             result.back().priority = addStopPriority(result.back().priority, stop.pars.priority);
    1994              :         }
    1995              :         prev = &stop;
    1996        84444 :         if (firstPos == INVALID_DOUBLE) {
    1997        39380 :             if (stop.parkingarea != nullptr) {
    1998         5053 :                 firstPos = MAX2(0., stopPos);
    1999              :             } else {
    2000        34327 :                 firstPos = stopPos;
    2001              :             }
    2002              :         }
    2003        84444 :         lastPos = stopPos;
    2004        84444 :         if (stop.pars.jump >= 0) {
    2005         1208 :             jumps.insert((int)result.size() - 1);
    2006              :         }
    2007              :     }
    2008              :     //std::cout << "getStopEdges veh=" << getID() << " result=" << toString(result) << "\n";
    2009      2777986 :     return result;
    2010            0 : }
    2011              : 
    2012              : 
    2013              : double
    2014         2061 : MSBaseVehicle::addStopPriority(double p1, double p2) {
    2015         2061 :     if (p1 < 0 || p2 < 0) {
    2016              :         return p1;
    2017              :     }
    2018            0 :     return p1 + p2;
    2019              : }
    2020              : 
    2021              : std::vector<std::pair<int, double> >
    2022        30390 : MSBaseVehicle::getStopIndices() const {
    2023              :     std::vector<std::pair<int, double> > result;
    2024        62088 :     for (std::list<MSStop>::const_iterator iter = myStops.begin(); iter != myStops.end(); ++iter) {
    2025        63396 :         result.push_back(std::make_pair(
    2026        31698 :                              (int)(iter->edge - myRoute->begin()),
    2027        31698 :                              iter->getEndPos(*this)));
    2028              :     }
    2029        30390 :     return result;
    2030            0 : }
    2031              : 
    2032              : 
    2033              : const MSStop&
    2034     16756595 : MSBaseVehicle::getNextStop() const {
    2035              :     assert(myStops.size() > 0);
    2036     16756595 :     return myStops.front();
    2037              : }
    2038              : 
    2039              : MSStop&
    2040        87075 : MSBaseVehicle::getNextStopMutable() {
    2041              :     assert(myStops.size() > 0);
    2042        87075 :     return myStops.front();
    2043              : }
    2044              : 
    2045              : SUMOTime
    2046      3937736 : MSBaseVehicle::getStopDuration() const {
    2047      3937736 :     if (isStopped()) {
    2048      1516639 :         return myStops.front().duration;
    2049              :     } else {
    2050              :         return 0;
    2051              :     }
    2052              : }
    2053              : 
    2054              : 
    2055              : MSStop&
    2056        13402 : MSBaseVehicle::getStop(int nextStopIndex) {
    2057        13402 :     if (nextStopIndex < 0 || (int)myStops.size() <= nextStopIndex) {
    2058            0 :         throw InvalidArgument(TLF("Invalid stop index % (has % stops).", nextStopIndex, myStops.size()));
    2059              :     }
    2060              :     auto stopIt = myStops.begin();
    2061              :     std::advance(stopIt, nextStopIndex);
    2062        13402 :     return *stopIt;
    2063              : }
    2064              : 
    2065              : 
    2066              : const SUMOVehicleParameter::Stop*
    2067       251409 : MSBaseVehicle::getNextStopParameter() const {
    2068       251409 :     if (hasStops()) {
    2069       115412 :         return &myStops.front().pars;
    2070              :     }
    2071              :     return nullptr;
    2072              : }
    2073              : 
    2074              : 
    2075              : bool
    2076        45202 : MSBaseVehicle::addTraciStop(SUMOVehicleParameter::Stop stop, std::string& errorMsg) {
    2077              :     //if the stop exists update the duration
    2078        69598 :     for (std::list<MSStop>::iterator iter = myStops.begin(); iter != myStops.end(); iter++) {
    2079        46676 :         if (iter->lane->getID() == stop.lane && fabs(iter->pars.endPos - stop.endPos) < POSITION_EPS) {
    2080              :             // update existing stop
    2081        22280 :             if (stop.duration == 0 && stop.until < 0 && !iter->reached) {
    2082        20790 :                 myStops.erase(iter);
    2083              :             } else {
    2084         1490 :                 iter->duration = stop.duration;
    2085         1490 :                 iter->triggered = stop.triggered;
    2086         1490 :                 iter->containerTriggered = stop.containerTriggered;
    2087         1490 :                 const_cast<SUMOVehicleParameter::Stop&>(iter->pars).until = stop.until;
    2088         1490 :                 const_cast<SUMOVehicleParameter::Stop&>(iter->pars).parking = stop.parking;
    2089              :             }
    2090              :             return true;
    2091              :         }
    2092              :     }
    2093        22922 :     const bool result = addStop(stop, errorMsg);
    2094        22922 :     if (result) {
    2095              :         /// XXX handle stops added out of order
    2096        22909 :         myParameter->stops.push_back(stop);
    2097              :     }
    2098              :     return result;
    2099              : }
    2100              : 
    2101              : 
    2102              : void
    2103          624 : MSBaseVehicle::unregisterWaiting() {
    2104          624 :     if (myAmRegisteredAsWaiting) {
    2105          431 :         MSNet::getInstance()->getVehicleControl().unregisterOneWaiting();
    2106          431 :         myAmRegisteredAsWaiting = false;
    2107              :     }
    2108          624 : }
    2109              : 
    2110              : 
    2111              : bool
    2112          933 : MSBaseVehicle::abortNextStop(int nextStopIndex) {
    2113          933 :     if (hasStops() && nextStopIndex < (int)myStops.size()) {
    2114          933 :         if (nextStopIndex == 0 && isStopped()) {
    2115           34 :             resumeFromStopping();
    2116              :         } else {
    2117              :             auto stopIt = myStops.begin();
    2118              :             std::advance(stopIt, nextStopIndex);
    2119          899 :             myStops.erase(stopIt);
    2120              :         }
    2121          933 :         if (!hasDeparted() && (int)myParameter->stops.size() > nextStopIndex) {
    2122              :             // stops will be rebuilt from scratch on rerouting so we must patch the stops in myParameter
    2123              :             auto stopIt2 = myParameter->stops.begin();
    2124              :             std::advance(stopIt2, nextStopIndex);
    2125            8 :             const_cast<SUMOVehicleParameter*>(myParameter)->stops.erase(stopIt2);
    2126              :         }
    2127          933 :         return true;
    2128              :     } else {
    2129              :         return false;
    2130              :     }
    2131              : }
    2132              : 
    2133              : 
    2134              : bool
    2135          146 : MSBaseVehicle::replaceStop(int nextStopIndex, SUMOVehicleParameter::Stop stop, const std::string& info, bool teleport, std::string& errorMsg) {
    2136          146 :     const int n = (int)myStops.size();
    2137          146 :     if (nextStopIndex < 0 || nextStopIndex >= n) {
    2138            5 :         errorMsg = TLF("invalid nextStopIndex % for % remaining stops", nextStopIndex, n);
    2139            5 :         return false;
    2140              :     }
    2141          141 :     if (nextStopIndex == 0 && isStopped()) {
    2142            7 :         errorMsg = TL("cannot replace reached stop");
    2143            7 :         return false;
    2144              :     }
    2145          134 :     const SUMOTime t = MSNet::getInstance()->getCurrentTimeStep();
    2146          134 :     MSLane* stopLane = MSLane::dictionary(stop.lane);
    2147          134 :     MSEdge* stopEdge = &stopLane->getEdge();
    2148              : 
    2149              :     auto itStop = myStops.begin();
    2150              :     std::advance(itStop, nextStopIndex);
    2151              :     MSStop& replacedStop = *itStop;
    2152              : 
    2153              :     // check parking access rights
    2154          134 :     if (stop.parkingarea != "") {
    2155            0 :         MSParkingArea* pa = dynamic_cast<MSParkingArea*>(MSNet::getInstance()->getStoppingPlace(stop.parkingarea, SUMO_TAG_PARKING_AREA));
    2156            0 :         if (pa != nullptr && !pa->accepts(this)) {
    2157            0 :             errorMsg = TLF("vehicle '%' does not have the right badge to access parkingArea '%'", getID(), stop.parkingarea);
    2158            0 :             return false;
    2159              :         }
    2160              :     }
    2161              : 
    2162          134 :     if (replacedStop.lane == stopLane && replacedStop.pars.endPos == stop.endPos && !teleport) {
    2163              :         // only replace stop attributes
    2164           10 :         const_cast<SUMOVehicleParameter::Stop&>(replacedStop.pars) = stop;
    2165           10 :         replacedStop.initPars(stop);
    2166           10 :         return true;
    2167              :     }
    2168              : 
    2169          124 :     if (!stopLane->allowsVehicleClass(getVClass(), myRoutingMode)) {
    2170            0 :         errorMsg = TLF("disallowed stop lane '%'", stopLane->getID());
    2171            0 :         return false;
    2172              :     }
    2173              : 
    2174          124 :     const ConstMSEdgeVector& oldEdges = getRoute().getEdges();
    2175          124 :     std::vector<MSStop> stops(myStops.begin(), myStops.end());
    2176          124 :     const int junctionOffset = getLane() != nullptr && getLane()->isInternal() ? 1 : 0;
    2177          124 :     MSRouteIterator itStart = nextStopIndex == 0 ? getCurrentRouteEdge() + junctionOffset : stops[nextStopIndex - 1].edge;
    2178          124 :     double startPos = nextStopIndex == 0 ? getPositionOnLane() : stops[nextStopIndex - 1].pars.endPos;
    2179          124 :     MSRouteIterator itEnd = nextStopIndex == n - 1 ? oldEdges.end() - 1 : stops[nextStopIndex + 1].edge;
    2180          124 :     auto endPos = nextStopIndex == n - 1 ? getArrivalPos() : stops[nextStopIndex + 1].pars.endPos;
    2181          124 :     SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = getRouterTT();
    2182              : 
    2183          124 :     bool newDestination = nextStopIndex == n - 1 && stops[nextStopIndex].edge == oldEdges.end() - 1;
    2184              : 
    2185              :     ConstMSEdgeVector toNewStop;
    2186          124 :     if (!teleport) {
    2187           96 :         router.compute(*itStart, startPos, stopEdge, stop.endPos, this, t, toNewStop, true);
    2188           96 :         if (toNewStop.size() == 0) {
    2189           15 :             errorMsg = TLF("no route found from edge '%' to stop edge '%'", (*itStart)->getID(), stopEdge->getID());
    2190            5 :             return false;
    2191              :         }
    2192              :     }
    2193              : 
    2194              :     ConstMSEdgeVector fromNewStop;
    2195          119 :     if (!newDestination) {
    2196          114 :         router.compute(stopEdge, stop.endPos, *itEnd, endPos, this, t, fromNewStop, true);
    2197          114 :         if (fromNewStop.size() == 0) {
    2198            0 :             errorMsg = TLF("no route found from stop edge '%' to edge '%'", stopEdge->getID(), (*itEnd)->getID());
    2199            0 :             return false;
    2200              :         }
    2201              :     }
    2202              : 
    2203          119 :     const_cast<SUMOVehicleParameter::Stop&>(replacedStop.pars) = stop;
    2204          119 :     replacedStop.initPars(stop);
    2205          119 :     replacedStop.edge = myRoute->end(); // will be patched in replaceRoute
    2206          119 :     replacedStop.lane = stopLane;
    2207          119 :     if (MSGlobals::gUseMesoSim) {
    2208           22 :         replacedStop.segment = MSGlobals::gMesoNet->getSegmentForEdge(replacedStop.lane->getEdge(), replacedStop.getEndPos(*this));
    2209           22 :         if (replacedStop.lane->isInternal()) {
    2210            0 :             errorMsg = TLF("Mesoscopic simulation does not allow stopping on internal edge '%' for vehicle '%'.", stop.edge, getID());
    2211            0 :             return false;
    2212              :         }
    2213              :     }
    2214              : 
    2215          119 :     ConstMSEdgeVector oldRemainingEdges(myCurrEdge, getRoute().end());
    2216              :     ConstMSEdgeVector newEdges; // only remaining
    2217          119 :     newEdges.insert(newEdges.end(), myCurrEdge, itStart);
    2218          119 :     if (!teleport) {
    2219           91 :         newEdges.insert(newEdges.end(), toNewStop.begin(), toNewStop.end() - 1);
    2220              :     } else {
    2221           28 :         newEdges.push_back(*itStart);
    2222              :     }
    2223          119 :     if (!newDestination) {
    2224          114 :         newEdges.insert(newEdges.end(), fromNewStop.begin(), fromNewStop.end() - 1);
    2225          114 :         newEdges.insert(newEdges.end(), itEnd, oldEdges.end());
    2226              :     } else {
    2227            5 :         newEdges.push_back(stopEdge);
    2228              :     }
    2229              :     //std::cout << SIMTIME << " replaceStop veh=" << getID()
    2230              :     //    << " teleport=" << teleport
    2231              :     //    << " busStop=" << stop.busstop
    2232              :     //    << " oldEdges=" << oldRemainingEdges.size()
    2233              :     //    << " newEdges=" << newEdges.size()
    2234              :     //    << " toNewStop=" << toNewStop.size()
    2235              :     //    << " fromNewStop=" << fromNewStop.size()
    2236              :     //    << "\n";
    2237              : 
    2238          119 :     const double routeCost = router.recomputeCosts(newEdges, this, t);
    2239          119 :     const double previousCost = router.recomputeCosts(oldRemainingEdges, this, t);
    2240          119 :     const double savings = previousCost - routeCost;
    2241          119 :     if (!hasDeparted() && (int)myParameter->stops.size() > nextStopIndex) {
    2242              :         // stops will be rebuilt from scratch so we must patch the stops in myParameter
    2243            5 :         const_cast<SUMOVehicleParameter*>(myParameter)->stops[nextStopIndex] = stop;
    2244              :     }
    2245          119 :     if (teleport) {
    2246              :         // let the vehicle jump rather than teleport
    2247              :         // we add a jump-stop at the end of the edge (unless the vehicle is
    2248              :         // already configure to jump before the replaced stop)
    2249           28 :         if (!insertJump(nextStopIndex, itStart, errorMsg)) {
    2250              :             return false;
    2251              :         };
    2252              :     }
    2253          119 :     return replaceRouteEdges(newEdges, routeCost, savings, info, !hasDeparted(), false, false, &errorMsg);
    2254          124 : }
    2255              : 
    2256              : 
    2257              : bool
    2258           52 : MSBaseVehicle::rerouteBetweenStops(int nextStopIndex, const std::string& info, bool teleport, std::string& errorMsg) {
    2259           52 :     const int n = (int)myStops.size();
    2260           52 :     if (nextStopIndex < 0 || nextStopIndex > n) {
    2261            0 :         errorMsg = TLF("invalid nextStopIndex % for % remaining stops", nextStopIndex, n);
    2262            0 :         return false;
    2263              :     }
    2264           52 :     if (nextStopIndex == 0 && isStopped()) {
    2265            0 :         errorMsg = TL("cannot reroute towards reached stop");
    2266            0 :         return false;
    2267              :     }
    2268           52 :     const SUMOTime t = MSNet::getInstance()->getCurrentTimeStep();
    2269              : 
    2270           52 :     const ConstMSEdgeVector& oldEdges = getRoute().getEdges();
    2271           52 :     std::vector<MSStop> stops(myStops.begin(), myStops.end());
    2272           52 :     const int junctionOffset = getLane() != nullptr && getLane()->isInternal() ? 1 : 0;
    2273           52 :     MSRouteIterator itStart = nextStopIndex == 0 ? getCurrentRouteEdge() + junctionOffset : stops[nextStopIndex - 1].edge;
    2274           52 :     double startPos = nextStopIndex == 0 ? getPositionOnLane() : stops[nextStopIndex - 1].pars.endPos;
    2275           52 :     MSRouteIterator itEnd = nextStopIndex == n ? oldEdges.end() - 1 : stops[nextStopIndex].edge;
    2276           52 :     auto endPos = nextStopIndex == n ? getArrivalPos() : stops[nextStopIndex].pars.endPos;
    2277           52 :     SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = getRouterTT();
    2278              : 
    2279              :     ConstMSEdgeVector newBetween;
    2280           52 :     if (!teleport) {
    2281           15 :         router.compute(*itStart, startPos, *itEnd, endPos, this, t, newBetween, true);
    2282           15 :         if (newBetween.size() == 0) {
    2283            0 :             errorMsg = TLF("no route found from edge '%' to stop edge '%'", (*itStart)->getID(), (*itEnd)->getID());
    2284            0 :             return false;
    2285              :         }
    2286              :     }
    2287              : 
    2288           52 :     ConstMSEdgeVector oldRemainingEdges(myCurrEdge, getRoute().end());
    2289              :     ConstMSEdgeVector newEdges; // only remaining
    2290           52 :     newEdges.insert(newEdges.end(), myCurrEdge, itStart);
    2291           52 :     if (!teleport) {
    2292           15 :         newEdges.insert(newEdges.end(), newBetween.begin(), newBetween.end() - 1);
    2293              :     } else {
    2294           37 :         newEdges.push_back(*itStart);
    2295              :     }
    2296           52 :     newEdges.insert(newEdges.end(), itEnd, oldEdges.end());
    2297              :     //std::cout << SIMTIME << " rerouteBetweenStops veh=" << getID()
    2298              :     //    << " oldEdges=" << oldRemainingEdges.size()
    2299              :     //    << " newEdges=" << newEdges.size()
    2300              :     //    << " toNewStop=" << toNewStop.size()
    2301              :     //    << " fromNewStop=" << fromNewStop.size()
    2302              :     //    << "\n";
    2303              : 
    2304           52 :     const double routeCost = router.recomputeCosts(newEdges, this, t);
    2305           52 :     const double previousCost = router.recomputeCosts(oldRemainingEdges, this, t);
    2306           52 :     const double savings = previousCost - routeCost;
    2307              : 
    2308           52 :     if (teleport) {
    2309              :         // let the vehicle jump rather than teleport
    2310              :         // we add a jump-stop at the end of the edge (unless the vehicle is
    2311              :         // already configure to jump before the replaced stop)
    2312           37 :         if (!insertJump(nextStopIndex, itStart, errorMsg)) {
    2313              :             return false;
    2314              :         };
    2315              :     }
    2316           52 :     return replaceRouteEdges(newEdges, routeCost, savings, info, !hasDeparted(), false, false, &errorMsg);
    2317           52 : }
    2318              : 
    2319              : 
    2320              : bool
    2321           65 : MSBaseVehicle::insertJump(int nextStopIndex, MSRouteIterator itStart, std::string& errorMsg) {
    2322              :     bool needJump = true;
    2323           65 :     if (nextStopIndex > 0) {
    2324              :         auto itPriorStop = myStops.begin();
    2325           47 :         std::advance(itPriorStop, nextStopIndex - 1);
    2326              :         const MSStop& priorStop = *itPriorStop;
    2327           47 :         if (priorStop.pars.jump >= 0) {
    2328              :             needJump = false;
    2329              :         }
    2330              :     }
    2331              :     if (needJump) {
    2332           47 :         SUMOVehicleParameter::Stop jumpStopPars;
    2333           47 :         jumpStopPars.endPos = (*itStart)->getLength();
    2334           47 :         jumpStopPars.speed = 1000;
    2335           47 :         jumpStopPars.jump = 0;
    2336              :         jumpStopPars.edge = (*itStart)->getID();
    2337           47 :         jumpStopPars.parametersSet = STOP_SPEED_SET | STOP_JUMP_SET;
    2338              :         MSLane* jumpStopLane = nullptr;
    2339           47 :         for (MSLane* cand : (*itStart)->getLanes()) {
    2340           47 :             if (cand->allowsVehicleClass(getVClass())) {
    2341              :                 jumpStopLane = cand;
    2342              :                 break;
    2343              :             }
    2344              :         }
    2345           47 :         if (jumpStopLane == nullptr) {
    2346            0 :             errorMsg = TL("unable to replace stop with teleporting");
    2347              :             return false;
    2348              :         }
    2349              :         auto itStop = myStops.begin();
    2350              :         std::advance(itStop, nextStopIndex);
    2351           47 :         MSStop jumpStop(jumpStopPars);
    2352           47 :         jumpStop.initPars(jumpStopPars);
    2353           47 :         jumpStop.lane = jumpStopLane;
    2354           47 :         jumpStop.edge = myRoute->end(); // will be patched in replaceRoute
    2355           47 :         myStops.insert(itStop, jumpStop);
    2356           47 :         if (!hasDeparted() && (int)myParameter->stops.size() > nextStopIndex) {
    2357              :             // stops will be rebuilt from scratch so we must patch the stops in myParameter
    2358              :             auto it = myParameter->stops.begin() + nextStopIndex;
    2359            0 :             const_cast<SUMOVehicleParameter*>(myParameter)->stops.insert(it, jumpStopPars);
    2360              :         }
    2361           47 :     }
    2362              :     return true;
    2363              : }
    2364              : 
    2365              : 
    2366              : bool
    2367          367 : MSBaseVehicle::insertStop(int nextStopIndex, SUMOVehicleParameter::Stop stop, const std::string& info, bool teleport, std::string& errorMsg) {
    2368          367 :     const int n = (int)myStops.size();
    2369          367 :     if (nextStopIndex < 0 || nextStopIndex > n) {
    2370            5 :         errorMsg = TLF("invalid nextStopIndex % for % remaining stops", nextStopIndex, n);
    2371            5 :         return false;
    2372              :     }
    2373          362 :     if (nextStopIndex == 0 && isStopped()) {
    2374            7 :         errorMsg = TL("cannot insert stop before the currently reached stop");
    2375            7 :         return false;
    2376              :     }
    2377          355 :     const SUMOTime t = MSNet::getInstance()->getCurrentTimeStep();
    2378          355 :     MSLane* stopLane = MSLane::dictionary(stop.lane);
    2379          355 :     MSEdge* stopEdge = &stopLane->getEdge();
    2380              : 
    2381          355 :     if (!stopLane->allowsVehicleClass(getVClass(), myRoutingMode)) {
    2382            0 :         errorMsg = TLF("disallowed stop lane '%'", stopLane->getID());
    2383            0 :         return false;
    2384              :     }
    2385              : 
    2386              :     // check parking access rights
    2387          355 :     if (stop.parkingarea != "") {
    2388          160 :         MSParkingArea* pa = dynamic_cast<MSParkingArea*>(MSNet::getInstance()->getStoppingPlace(stop.parkingarea, SUMO_TAG_PARKING_AREA));
    2389          160 :         if (pa != nullptr && !pa->accepts(this)) {
    2390            0 :             errorMsg = TLF("Vehicle '%' does not have the right badge to access parkingArea '%'.", getID(), stop.parkingarea);
    2391            0 :             return false;
    2392              :         }
    2393              :     }
    2394              : 
    2395          355 :     const ConstMSEdgeVector& oldEdges = getRoute().getEdges();
    2396          355 :     std::vector<MSStop> stops(myStops.begin(), myStops.end());
    2397          355 :     const int junctionOffset = getLane() != nullptr && getLane()->isInternal() ? 1 : 0;
    2398          355 :     MSRouteIterator itStart = nextStopIndex == 0 ? getCurrentRouteEdge() + junctionOffset : stops[nextStopIndex - 1].edge;
    2399          355 :     double startPos = nextStopIndex == 0 ? getPositionOnLane() : stops[nextStopIndex - 1].pars.endPos;
    2400          355 :     MSRouteIterator itEnd = nextStopIndex == n ? oldEdges.end() - 1 : stops[nextStopIndex].edge;
    2401          355 :     auto endPos = nextStopIndex == n ? getArrivalPos() : stops[nextStopIndex].pars.endPos;
    2402          355 :     SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = getRouterTT();
    2403              : 
    2404          355 :     bool newDestination = nextStopIndex == n && stopEdge == oldEdges.back();
    2405              : 
    2406              :     ConstMSEdgeVector toNewStop;
    2407          355 :     if (!teleport) {
    2408          328 :         router.compute(*itStart, startPos, stopEdge, stop.endPos, this, t, toNewStop, true);
    2409          328 :         if (toNewStop.size() == 0) {
    2410           15 :             errorMsg = TLF("no route found from edge '%' to stop edge '%'", (*itStart)->getID(), stopEdge->getID());
    2411            5 :             return false;
    2412              :         }
    2413              :     }
    2414              : 
    2415              :     ConstMSEdgeVector fromNewStop;
    2416          350 :     if (!newDestination) {
    2417          315 :         router.compute(stopEdge, stop.endPos, *itEnd, endPos, this, t, fromNewStop, true);
    2418          315 :         if (fromNewStop.size() == 0) {
    2419            0 :             errorMsg = TLF("no route found from stop edge '%' to edge '%'", stopEdge->getID(), (*itEnd)->getID());
    2420            0 :             return false;
    2421              :         }
    2422              :     }
    2423              : 
    2424              :     auto itStop = myStops.begin();
    2425              :     std::advance(itStop, nextStopIndex);
    2426          350 :     MSStop newStop(stop);
    2427          350 :     newStop.initPars(stop);
    2428          350 :     newStop.edge = myRoute->end(); // will be patched in replaceRoute
    2429          350 :     newStop.lane = stopLane;
    2430          350 :     if (MSGlobals::gUseMesoSim) {
    2431           73 :         newStop.segment = MSGlobals::gMesoNet->getSegmentForEdge(newStop.lane->getEdge(), newStop.getEndPos(*this));
    2432           73 :         if (newStop.lane->isInternal()) {
    2433            0 :             errorMsg = TLF("Mesoscopic simulation does not allow stopping on internal edge '%' for vehicle '%'.", stop.edge, getID());
    2434            0 :             return false;
    2435              :         }
    2436              :     }
    2437          350 :     myStops.insert(itStop, newStop);
    2438              : 
    2439          350 :     ConstMSEdgeVector oldRemainingEdges(myCurrEdge, getRoute().end());
    2440              :     ConstMSEdgeVector newEdges; // only remaining
    2441          350 :     newEdges.insert(newEdges.end(), myCurrEdge, itStart);
    2442          350 :     if (!teleport) {
    2443          323 :         newEdges.insert(newEdges.end(), toNewStop.begin(), toNewStop.end() - 1);
    2444              :     } else {
    2445           27 :         newEdges.push_back(*itStart);
    2446              :     }
    2447          350 :     if (!newDestination) {
    2448          315 :         newEdges.insert(newEdges.end(), fromNewStop.begin(), fromNewStop.end() - 1);
    2449          315 :         newEdges.insert(newEdges.end(), itEnd, oldEdges.end());
    2450              :     } else {
    2451           35 :         newEdges.push_back(stopEdge);
    2452              :     }
    2453              :     //std::cout << SIMTIME << " insertStop veh=" << getID()
    2454              :     //    << " teleport=" << teleport
    2455              :     //    << " busStop=" << stop.busstop
    2456              :     //    << " oldEdges=" << oldRemainingEdges.size()
    2457              :     //    << " newEdges=" << newEdges.size()
    2458              :     //    << " toNewStop=" << toNewStop.size()
    2459              :     //    << " fromNewStop=" << fromNewStop.size()
    2460              :     //    << "\n";
    2461              : 
    2462          350 :     const double routeCost = router.recomputeCosts(newEdges, this, t);
    2463          350 :     const double previousCost = router.recomputeCosts(oldRemainingEdges, this, t);
    2464          350 :     const double savings = previousCost - routeCost;
    2465              : 
    2466          350 :     if (!hasDeparted() && (int)myParameter->stops.size() >= nextStopIndex) {
    2467              :         // stops will be rebuilt from scratch so we must patch the stops in myParameter
    2468              :         auto it = myParameter->stops.begin() + nextStopIndex;
    2469           15 :         const_cast<SUMOVehicleParameter*>(myParameter)->stops.insert(it, stop);
    2470              :     }
    2471          350 :     return replaceRouteEdges(newEdges, routeCost, savings, info, !hasDeparted(), false, false, &errorMsg);
    2472          705 : }
    2473              : 
    2474              : 
    2475              : double
    2476            0 : MSBaseVehicle::getStateOfCharge() const {
    2477            0 :     if (static_cast<MSDevice_Battery*>(getDevice(typeid(MSDevice_Battery))) != 0) {
    2478            0 :         MSDevice_Battery* batteryOfVehicle = dynamic_cast<MSDevice_Battery*>(getDevice(typeid(MSDevice_Battery)));
    2479            0 :         return batteryOfVehicle->getActualBatteryCapacity();
    2480              :     } else {
    2481            0 :         if (static_cast<MSDevice_ElecHybrid*>(getDevice(typeid(MSDevice_ElecHybrid))) != 0) {
    2482            0 :             MSDevice_ElecHybrid* batteryOfVehicle = dynamic_cast<MSDevice_ElecHybrid*>(getDevice(typeid(MSDevice_ElecHybrid)));
    2483            0 :             return batteryOfVehicle->getActualBatteryCapacity();
    2484              :         }
    2485              :     }
    2486              :     return -1;
    2487              : }
    2488              : 
    2489              : 
    2490              : double
    2491            0 : MSBaseVehicle::getRelativeStateOfCharge() const {
    2492            0 :     if (static_cast<MSDevice_Battery*>(getDevice(typeid(MSDevice_Battery))) != 0) {
    2493            0 :         MSDevice_Battery* batteryOfVehicle = dynamic_cast<MSDevice_Battery*>(getDevice(typeid(MSDevice_Battery)));
    2494            0 :         return batteryOfVehicle->getActualBatteryCapacity() / batteryOfVehicle->getMaximumBatteryCapacity();
    2495              :     } else {
    2496            0 :         if (static_cast<MSDevice_ElecHybrid*>(getDevice(typeid(MSDevice_ElecHybrid))) != 0) {
    2497            0 :             MSDevice_ElecHybrid* batteryOfVehicle = dynamic_cast<MSDevice_ElecHybrid*>(getDevice(typeid(MSDevice_ElecHybrid)));
    2498            0 :             return batteryOfVehicle->getActualBatteryCapacity() / batteryOfVehicle->getMaximumBatteryCapacity();
    2499              :         }
    2500              :     }
    2501              :     return -1;
    2502              : }
    2503              : 
    2504              : 
    2505              : double
    2506            0 : MSBaseVehicle::getChargedEnergy() const {
    2507            0 :     if (static_cast<MSDevice_Battery*>(getDevice(typeid(MSDevice_Battery))) != 0) {
    2508            0 :         MSDevice_Battery* batteryOfVehicle = dynamic_cast<MSDevice_Battery*>(getDevice(typeid(MSDevice_Battery)));
    2509            0 :         return batteryOfVehicle->getEnergyCharged();
    2510              :     } else {
    2511            0 :         if (static_cast<MSDevice_ElecHybrid*>(getDevice(typeid(MSDevice_ElecHybrid))) != 0) {
    2512            0 :             MSDevice_ElecHybrid* batteryOfVehicle = dynamic_cast<MSDevice_ElecHybrid*>(getDevice(typeid(MSDevice_ElecHybrid)));
    2513            0 :             return batteryOfVehicle->getEnergyCharged();
    2514              :         }
    2515              :     }
    2516              :     return -1;
    2517              : }
    2518              : 
    2519              : 
    2520              : double
    2521            0 : MSBaseVehicle::getMaxChargeRate() const {
    2522            0 :     if (static_cast<MSDevice_Battery*>(getDevice(typeid(MSDevice_Battery))) != 0) {
    2523            0 :         MSDevice_Battery* batteryOfVehicle = dynamic_cast<MSDevice_Battery*>(getDevice(typeid(MSDevice_Battery)));
    2524            0 :         return batteryOfVehicle->getMaximumChargeRate();
    2525              :     }
    2526              :     return -1;
    2527              : }
    2528              : 
    2529              : 
    2530              : double
    2531            0 : MSBaseVehicle::getElecHybridCurrent() const {
    2532            0 :     if (static_cast<MSDevice_ElecHybrid*>(getDevice(typeid(MSDevice_ElecHybrid))) != 0) {
    2533            0 :         MSDevice_ElecHybrid* elecHybridDevice = dynamic_cast<MSDevice_ElecHybrid*>(getDevice(typeid(MSDevice_ElecHybrid)));
    2534            0 :         return elecHybridDevice->getCurrentFromOverheadWire();
    2535              :     }
    2536              : 
    2537              :     return NAN;
    2538              : }
    2539              : 
    2540              : double
    2541          766 : MSBaseVehicle::getHarmonoise_NoiseEmissions() const {
    2542          766 :     if (isOnRoad() || isIdling()) {
    2543          766 :         return HelpersHarmonoise::computeNoise(myType->getEmissionClass(), getSpeed(), getAcceleration());
    2544              :     } else {
    2545              :         return 0.;
    2546              :     }
    2547              : }
    2548              : 
    2549              : 
    2550              : const MSEdgeWeightsStorage&
    2551      5848094 : MSBaseVehicle::getWeightsStorage() const {
    2552      5848094 :     return _getWeightsStorage();
    2553              : }
    2554              : 
    2555              : 
    2556              : MSEdgeWeightsStorage&
    2557          666 : MSBaseVehicle::getWeightsStorage() {
    2558          666 :     return _getWeightsStorage();
    2559              : }
    2560              : 
    2561              : 
    2562              : MSEdgeWeightsStorage&
    2563      5848760 : MSBaseVehicle::_getWeightsStorage() const {
    2564      5848760 :     if (myEdgeWeights == nullptr) {
    2565        13726 :         myEdgeWeights = new MSEdgeWeightsStorage();
    2566              :     }
    2567      5848760 :     return *myEdgeWeights;
    2568              : }
    2569              : 
    2570              : 
    2571              : 
    2572              : 
    2573              : int
    2574      7933207 : MSBaseVehicle::getPersonNumber() const {
    2575      7933207 :     int boarded = myPersonDevice == nullptr ? 0 : myPersonDevice->size();
    2576      7933207 :     return boarded + myParameter->personNumber;
    2577              : }
    2578              : 
    2579              : int
    2580            0 : MSBaseVehicle::getLeavingPersonNumber() const {
    2581              :     int leavingPersonNumber = 0;
    2582            0 :     const std::vector<MSTransportable*>& persons = getPersons();
    2583            0 :     for (std::vector<MSTransportable*>::const_iterator it_p = persons.begin(); it_p != persons.end(); ++it_p) {
    2584            0 :         MSStageDriving* const stage = dynamic_cast<MSStageDriving*>((*it_p)->getCurrentStage());
    2585              :         const MSStop* stop = &myStops.front();
    2586            0 :         const MSVehicle* joinVeh = dynamic_cast<MSVehicle*>(MSNet::getInstance()->getVehicleControl().getVehicle((*stop).pars.join));
    2587            0 :         if (stop && stage->canLeaveVehicle(*it_p, *this, *stop) && !MSDevice_Transportable::willTransferAtJoin(*it_p, joinVeh)) {
    2588            0 :             leavingPersonNumber++;
    2589              :         }
    2590              :     }
    2591            0 :     return leavingPersonNumber;
    2592              : }
    2593              : 
    2594              : std::vector<std::string>
    2595          194 : MSBaseVehicle::getPersonIDList() const {
    2596              :     std::vector<std::string> ret;
    2597          194 :     const std::vector<MSTransportable*>& persons = getPersons();
    2598          669 :     for (std::vector<MSTransportable*>::const_iterator it_p = persons.begin(); it_p != persons.end(); ++it_p) {
    2599          475 :         ret.push_back((*it_p)->getID());
    2600              :     }
    2601          194 :     return ret;
    2602            0 : }
    2603              : 
    2604              : int
    2605      5926847 : MSBaseVehicle::getContainerNumber() const {
    2606      5926847 :     int loaded = myContainerDevice == nullptr ? 0 : myContainerDevice->size();
    2607      5926847 :     return loaded + myParameter->containerNumber;
    2608              : }
    2609              : 
    2610              : 
    2611              : void
    2612          163 : MSBaseVehicle::removeTransportable(MSTransportable* t) {
    2613              :     // this might be called from the MSTransportable destructor so we cannot do a dynamic cast to determine the type
    2614          163 :     if (myPersonDevice != nullptr) {
    2615          143 :         myPersonDevice->removeTransportable(t);
    2616              :     }
    2617          163 :     if (myContainerDevice != nullptr) {
    2618           20 :         myContainerDevice->removeTransportable(t);
    2619              :     }
    2620          163 :     if (myEnergyParams != nullptr) {
    2621           86 :         myEnergyParams->setTransportableMass(myEnergyParams->getTransportableMass() - t->getVehicleType().getMass());
    2622              :     }
    2623          163 : }
    2624              : 
    2625              : 
    2626              : void
    2627         9251 : MSBaseVehicle::removeTransportableMass(MSTransportable* t) {
    2628         9251 :     if (myEnergyParams != nullptr) {
    2629         6004 :         myEnergyParams->setTransportableMass(myEnergyParams->getTransportableMass() - t->getVehicleType().getMass());
    2630              :     }
    2631         9251 : }
    2632              : 
    2633              : 
    2634              : const std::vector<MSTransportable*>&
    2635      8295672 : MSBaseVehicle::getPersons() const {
    2636      8295672 :     if (myPersonDevice == nullptr) {
    2637              :         return myEmptyTransportableVector;
    2638              :     } else {
    2639        13590 :         return myPersonDevice->getTransportables();
    2640              :     }
    2641              : }
    2642              : 
    2643              : 
    2644              : const std::vector<MSTransportable*>&
    2645      7052397 : MSBaseVehicle::getContainers() const {
    2646      7052397 :     if (myContainerDevice == nullptr) {
    2647              :         return myEmptyTransportableVector;
    2648              :     } else {
    2649         3290 :         return myContainerDevice->getTransportables();
    2650              :     }
    2651              : }
    2652              : 
    2653              : 
    2654              : bool
    2655          203 : MSBaseVehicle::isLineStop(double position) const {
    2656          203 :     if (myParameter->line == "") {
    2657              :         // not a public transport line
    2658              :         return false;
    2659              :     }
    2660          349 :     for (const SUMOVehicleParameter::Stop& stop : myParameter->stops) {
    2661          209 :         if (stop.startPos <= position && position <= stop.endPos) {
    2662              :             return true;
    2663              :         }
    2664              :     }
    2665          166 :     for (const SUMOVehicleParameter::Stop& stop : myRoute->getStops()) {
    2666           36 :         if (stop.startPos <= position && position <= stop.endPos) {
    2667              :             return true;
    2668              :         }
    2669              :     }
    2670              :     return false;
    2671              : }
    2672              : 
    2673              : 
    2674              : bool
    2675           45 : MSBaseVehicle::hasDevice(const std::string& deviceName) const {
    2676          144 :     for (MSDevice* const dev : myDevices) {
    2677          126 :         if (dev->deviceName() == deviceName) {
    2678              :             return true;
    2679              :         }
    2680              :     }
    2681              :     return false;
    2682              : }
    2683              : 
    2684              : 
    2685              : void
    2686            9 : MSBaseVehicle::createDevice(const std::string& deviceName) {
    2687            9 :     if (!hasDevice(deviceName)) {
    2688            9 :         if (deviceName == "rerouting") {
    2689           27 :             ((SUMOVehicleParameter*)myParameter)->setParameter("has." + deviceName + ".device", "true");
    2690            9 :             MSDevice_Routing::buildVehicleDevices(*this, myDevices);
    2691            9 :             if (hasDeparted()) {
    2692              :                 // vehicle already departed: disable pre-insertion rerouting and enable regular routing behavior
    2693            0 :                 MSDevice_Routing* routingDevice = static_cast<MSDevice_Routing*>(getDevice(typeid(MSDevice_Routing)));
    2694              :                 assert(routingDevice != 0);
    2695            0 :                 routingDevice->notifyEnter(*this, MSMoveReminder::NOTIFICATION_DEPARTED);
    2696              :             }
    2697              :         } else {
    2698            0 :             throw InvalidArgument(TLF("creating device of type '%' is not supported", deviceName));
    2699              :         }
    2700              :     }
    2701            9 : }
    2702              : 
    2703              : 
    2704              : std::string
    2705        50153 : MSBaseVehicle::getDeviceParameter(const std::string& deviceName, const std::string& key) const {
    2706        59688 :     for (MSVehicleDevice* const dev : myDevices) {
    2707        59679 :         if (dev->deviceName() == deviceName) {
    2708        50144 :             return dev->getParameter(key);
    2709              :         }
    2710              :     }
    2711           27 :     throw InvalidArgument(TLF("no device of type '%' exists", deviceName));
    2712              : }
    2713              : 
    2714              : 
    2715              : void
    2716          208 : MSBaseVehicle::setDeviceParameter(const std::string& deviceName, const std::string& key, const std::string& value) {
    2717          355 :     for (MSVehicleDevice* const dev : myDevices) {
    2718          355 :         if (dev->deviceName() == deviceName) {
    2719          208 :             dev->setParameter(key, value);
    2720          208 :             return;
    2721              :         }
    2722              :     }
    2723            0 :     throw InvalidArgument(TLF("no device of type '%' exists", deviceName));
    2724              : }
    2725              : 
    2726              : 
    2727              : void
    2728          842 : MSBaseVehicle::setJunctionModelParameter(const std::string& key, const std::string& value) {
    2729         1669 :     if (key == toString(SUMO_ATTR_JM_IGNORE_IDS) || key == toString(SUMO_ATTR_JM_IGNORE_TYPES)) {
    2730          837 :         getParameter().parametersSet |= VEHPARS_JUNCTIONMODEL_PARAMS_SET;
    2731          837 :         const_cast<SUMOVehicleParameter&>(getParameter()).setParameter(key, value);
    2732              :         // checked in MSLink::ignoreFoe
    2733              :     } else {
    2734           15 :         throw InvalidArgument(TLF("Vehicle '%' does not support junctionModel parameter '%'.", getID(), key));
    2735              :     }
    2736          837 : }
    2737              : 
    2738              : 
    2739              : void
    2740           38 : MSBaseVehicle::setCarFollowModelParameter(const std::string& key, const std::string& value) {
    2741              :     // handle some generic params first and then delegate to the carFollowModel itself
    2742           67 :     if (key == toString(SUMO_ATTR_CF_IGNORE_IDS) || key == toString(SUMO_ATTR_CF_IGNORE_TYPES)) {
    2743           17 :         getParameter().parametersSet |= VEHPARS_CFMODEL_PARAMS_SET;
    2744           17 :         const_cast<SUMOVehicleParameter&>(getParameter()).setParameter(key, value);
    2745              :         // checked in MSVehicle::planMove
    2746              :     } else {
    2747           21 :         MSVehicle* microVeh = dynamic_cast<MSVehicle*>(this);
    2748           21 :         if (microVeh) {
    2749              :             // remove 'carFollowModel.' prefix
    2750           21 :             const std::string attrName = key.substr(15);
    2751           21 :             microVeh->getCarFollowModel().setParameter(microVeh, attrName, value);
    2752              :         }
    2753              :     }
    2754           33 : }
    2755              : 
    2756              : 
    2757              : void
    2758      5371080 : MSBaseVehicle::initTransientModelParams() {
    2759              :     /* Design idea for additional junction model parameters:
    2760              :        We can distinguish between 3 levels of parameters
    2761              :        1. typically shared by multiple vehicles -> vType parameter
    2762              :        2. specific to one vehicle but stays constant throughout the simulation -> vehicle parameter
    2763              :        3. specific to one vehicle and expected to change during simulation -> prefixed generic vehicle parameter
    2764              :        */
    2765      5644966 :     for (auto item : getParameter().getParametersMap()) {
    2766       547772 :         if (StringUtils::startsWith(item.first, "junctionModel.")) {
    2767          832 :             setJunctionModelParameter(item.first, item.second);
    2768       546108 :         } else if (StringUtils::startsWith(item.first, "carFollowModel.")) {
    2769           17 :             setCarFollowModelParameter(item.first, item.second);
    2770              :         }
    2771              :     }
    2772     10742160 :     const std::string routingModeStr = getStringParam("device.rerouting.mode");
    2773              :     try {
    2774      5371080 :         int routingMode = StringUtils::toInt(routingModeStr);
    2775      5371080 :         if (routingMode != libsumo::ROUTING_MODE_DEFAULT) {
    2776              :             setRoutingMode(routingMode);
    2777              :         }
    2778            0 :     } catch (NumberFormatException&) {
    2779              :         // @todo interpret symbolic constants
    2780            0 :         throw ProcessError(TLF("could not interpret routing.mode '%'", routingModeStr));
    2781            0 :     }
    2782      5371080 : }
    2783              : 
    2784              : 
    2785              : SUMOAbstractRouter<MSEdge, SUMOVehicle>&
    2786        22663 : MSBaseVehicle::getRouterTT() const {
    2787        22663 :     if (myRoutingMode == libsumo::ROUTING_MODE_AGGREGATED) {
    2788          204 :         return MSRoutingEngine::getRouterTT(getRNGIndex(), getVClass());
    2789              :     } else {
    2790        45122 :         return MSNet::getInstance()->getRouterTT(getRNGIndex());
    2791              :     }
    2792              : }
    2793              : 
    2794              : 
    2795              : void
    2796        30656 : MSBaseVehicle::replaceVehicleType(const MSVehicleType* type) {
    2797              :     assert(type != nullptr);
    2798              :     // save old parameters before possible type deletion
    2799        30656 :     const double oldMu = myType->getSpeedFactor().getParameter(0);
    2800              :     const double oldDev = myType->getSpeedFactor().getParameter(1);
    2801        30656 :     if (myType->isVehicleSpecific() && type != myType) {
    2802          930 :         MSNet::getInstance()->getVehicleControl().removeVType(myType);
    2803              :     }
    2804              :     // adapt myChosenSpeedFactor to the new type
    2805        30656 :     if (oldDev == 0.) {
    2806              :         // old type had speedDev 0, reroll
    2807        22801 :         myChosenSpeedFactor = type->computeChosenSpeedDeviation(getRNG());
    2808              :     } else {
    2809              :         // map old speedFactor onto new distribution
    2810         7855 :         const double distPoint = (myChosenSpeedFactor - oldMu) / oldDev;
    2811              :         const double newMu = type->getSpeedFactor().getParameter(0);
    2812              :         const double newDev = type->getSpeedFactor().getParameter(1);
    2813         7855 :         myChosenSpeedFactor = newMu + distPoint * newDev;
    2814              :         // respect distribution limits
    2815         7855 :         myChosenSpeedFactor = MIN2(myChosenSpeedFactor, type->getSpeedFactor().getMax());
    2816         7860 :         myChosenSpeedFactor = MAX2(myChosenSpeedFactor, type->getSpeedFactor().getMin());
    2817              :     }
    2818        30656 :     myType = type;
    2819        30656 :     if (myEnergyParams != nullptr) {
    2820              :         myEnergyParams->setSecondary(type->getEmissionParameters());
    2821              :     }
    2822        30656 : }
    2823              : 
    2824              : 
    2825              : MSVehicleType&
    2826       126213 : MSBaseVehicle::getSingularType() {
    2827       126213 :     if (myType->isVehicleSpecific()) {
    2828              :         return *const_cast<MSVehicleType*>(myType);
    2829              :     }
    2830         2524 :     MSVehicleType* type = myType->buildSingularType(myType->getID() + "@" + getID());
    2831         1262 :     replaceVehicleType(type);
    2832         1262 :     return *type;
    2833              : }
    2834              : 
    2835              : 
    2836              : int
    2837       546900 : MSBaseVehicle::getRNGIndex() const {
    2838       546900 :     const MSLane* const lane = getLane();
    2839       546900 :     if (lane == nullptr) {
    2840        17615 :         return getEdge()->getLanes()[0]->getRNGIndex();
    2841              :     } else {
    2842       529285 :         return lane->getRNGIndex();
    2843              :     }
    2844              : }
    2845              : 
    2846              : 
    2847              : SumoRNG*
    2848    722281144 : MSBaseVehicle::getRNG() const {
    2849    722281144 :     const MSLane* lane = getLane();
    2850    722281144 :     if (lane == nullptr) {
    2851         6815 :         return getEdge()->getLanes()[0]->getRNG();
    2852              :     } else {
    2853    722274329 :         return lane->getRNG();
    2854              :     }
    2855              : }
    2856              : 
    2857              : std::string
    2858        68998 : MSBaseVehicle::getPrefixedParameter(const std::string& key, std::string& error) const {
    2859        68998 :     const MSVehicle* microVeh = dynamic_cast<const MSVehicle*>(this);
    2860       137996 :     if (StringUtils::startsWith(key, "device.")) {
    2861       150459 :         StringTokenizer tok(key, ".");
    2862        50153 :         if (tok.size() < 3) {
    2863            0 :             error = TLF("Invalid device parameter '%' for vehicle '%'.", key, getID());
    2864            0 :             return "";
    2865              :         }
    2866              :         try {
    2867       150459 :             return getDeviceParameter(tok.get(1), key.substr(tok.get(0).size() + tok.get(1).size() + 2));
    2868           18 :         } catch (InvalidArgument& e) {
    2869           54 :             error = TLF("Vehicle '%' does not support device parameter '%' (%).", getID(), key, e.what());
    2870           18 :             return "";
    2871           18 :         }
    2872        87843 :     } else if (StringUtils::startsWith(key, "laneChangeModel.")) {
    2873          324 :         if (microVeh == nullptr) {
    2874           36 :             error = TLF("Mesoscopic vehicle '%' does not support laneChangeModel parameters.", getID());
    2875           18 :             return "";
    2876              :         }
    2877          306 :         const std::string attrName = key.substr(16);
    2878              :         try {
    2879          306 :             return microVeh->getLaneChangeModel().getParameter(attrName);
    2880           44 :         } catch (InvalidArgument& e) {
    2881          132 :             error = TLF("Vehicle '%' does not support laneChangeModel parameter '%' (%).", getID(), key, e.what());
    2882           44 :             return "";
    2883           44 :         }
    2884        37042 :     } else if (StringUtils::startsWith(key, "carFollowModel.")) {
    2885           16 :         if (microVeh == nullptr) {
    2886            0 :             error = TLF("Mesoscopic vehicle '%' does not support carFollowModel parameters.", getID());
    2887            0 :             return "";
    2888              :         }
    2889           16 :         const std::string attrName = key.substr(15);
    2890              :         try {
    2891           16 :             return microVeh->getCarFollowModel().getParameter(microVeh, attrName);
    2892            0 :         } catch (InvalidArgument& e) {
    2893            0 :             error = TLF("Vehicle '%' does not support carFollowModel parameter '%' (%).", getID(), key, e.what());
    2894            0 :             return "";
    2895            0 :         }
    2896        18541 :     } else if (StringUtils::startsWith(key, "has.") && StringUtils::endsWith(key, ".device")) {
    2897          108 :         StringTokenizer tok(key, ".");
    2898           36 :         if (tok.size() != 3) {
    2899            0 :             error = TL("Invalid check for device. Expected format is 'has.DEVICENAME.device'.");
    2900            0 :             return "";
    2901              :         }
    2902           81 :         return hasDevice(tok.get(1)) ? "true" : "false";
    2903              :         // parking related parameters start here
    2904        18505 :     } else if (key == "parking.rerouteCount") {
    2905           15 :         return toString(getNumberParkingReroutes());
    2906        36908 :     } else if (StringUtils::startsWith(key, "parking.memory.")) {
    2907              :         std::vector<std::string> values;
    2908           65 :         if (getParkingMemory()) {
    2909           25 :             if (key == "parking.memory.IDList") {
    2910           30 :                 for (const auto& item : *getParkingMemory()) {
    2911           25 :                     values.push_back(item.first->getID());
    2912              :                 }
    2913           20 :             } else if (key == "parking.memory.score") {
    2914           30 :                 for (const auto& item : *getParkingMemory()) {
    2915           25 :                     values.push_back(item.second.score);
    2916              :                 }
    2917           15 :             } else if (key == "parking.memory.blockedAtTime") {
    2918           30 :                 for (const auto& item : *getParkingMemory()) {
    2919           50 :                     values.push_back(toString(STEPS2TIME(item.second.blockedAtTime)));
    2920              :                 }
    2921           10 :             } else if (key == "parking.memory.blockedAtTimeLocal") {
    2922           30 :                 for (const auto& item : *getParkingMemory()) {
    2923           50 :                     values.push_back(toString(STEPS2TIME(item.second.blockedAtTimeLocal)));
    2924              :                 }
    2925              :             } else {
    2926           10 :                 error = TLF("Unsupported parking parameter '%' for vehicle '%'.", key, getID());
    2927              :             }
    2928              :         }
    2929           65 :         return toString(values);
    2930           65 :     } else {
    2931              :         // default: custom user parameter
    2932        36778 :         return getParameter().getParameter(key, "");
    2933              :     }
    2934              : }
    2935              : 
    2936              : 
    2937              : void
    2938        54107 : MSBaseVehicle::rememberBlockedParkingArea(const MSStoppingPlace* pa, bool local) {
    2939        54107 :     if (myParkingMemory == nullptr) {
    2940            0 :         myParkingMemory = new StoppingPlaceMemory();
    2941              :     }
    2942        54107 :     myParkingMemory->rememberBlockedStoppingPlace(pa, local);
    2943        54107 : }
    2944              : 
    2945              : 
    2946              : void
    2947        19040 : MSBaseVehicle::resetParkingAreaScores() {
    2948        19040 :     if (myParkingMemory != nullptr) {
    2949        17321 :         myParkingMemory->resetStoppingPlaceScores();
    2950              :     }
    2951        19040 : }
    2952              : 
    2953              : 
    2954              : void
    2955          280 : MSBaseVehicle::rememberChargingStationScore(const MSStoppingPlace* cs, const std::string& score) {
    2956          280 :     if (myChargingMemory == nullptr) {
    2957           63 :         myChargingMemory = new StoppingPlaceMemory();
    2958              :     }
    2959          280 :     myChargingMemory->rememberStoppingPlaceScore(cs, score);
    2960          280 : }
    2961              : 
    2962              : 
    2963              : void
    2964           98 : MSBaseVehicle::resetChargingStationScores() {
    2965           98 :     if (myChargingMemory != nullptr) {
    2966            8 :         myChargingMemory->resetStoppingPlaceScores();
    2967              :     }
    2968           98 : }
    2969              : 
    2970              : 
    2971              : void
    2972        85932 : MSBaseVehicle::rememberParkingAreaScore(const MSStoppingPlace* pa, const std::string& score) {
    2973        85932 :     if (myParkingMemory == nullptr) {
    2974         2224 :         myParkingMemory = new StoppingPlaceMemory();
    2975              :     }
    2976        85932 :     myParkingMemory->rememberStoppingPlaceScore(pa, score);
    2977        85932 : }
    2978              : 
    2979              : 
    2980              : SUMOTime
    2981        51410 : MSBaseVehicle::sawBlockedParkingArea(const MSStoppingPlace* pa, bool local) const {
    2982        51410 :     if (myParkingMemory == nullptr) {
    2983              :         return -1;
    2984              :     }
    2985        51410 :     return myParkingMemory->sawBlockedStoppingPlace(pa, local);
    2986              : }
    2987              : 
    2988              : 
    2989            0 : void MSBaseVehicle::rememberBlockedChargingStation(const MSStoppingPlace* cs, bool local) {
    2990            0 :     if (myChargingMemory == nullptr) {
    2991            0 :         myChargingMemory = new StoppingPlaceMemory();
    2992              :     }
    2993            0 :     myChargingMemory->rememberBlockedStoppingPlace(cs, local);
    2994            0 : }
    2995              : 
    2996              : 
    2997              : SUMOTime
    2998          417 : MSBaseVehicle::sawBlockedChargingStation(const MSStoppingPlace* cs, bool local) const {
    2999          417 :     if (myChargingMemory == nullptr) {
    3000              :         return -1;
    3001              :     }
    3002           50 :     return myChargingMemory->sawBlockedStoppingPlace(cs, local);
    3003              : }
    3004              : 
    3005              : 
    3006              : #ifdef _DEBUG
    3007              : void
    3008              : MSBaseVehicle::initMoveReminderOutput(const OptionsCont& oc) {
    3009              :     if (oc.isSet("movereminder-output.vehicles")) {
    3010              :         const std::vector<std::string> vehicles = oc.getStringVector("movereminder-output.vehicles");
    3011              :         myShallTraceMoveReminders.insert(vehicles.begin(), vehicles.end());
    3012              :     }
    3013              : }
    3014              : 
    3015              : 
    3016              : void
    3017              : MSBaseVehicle::traceMoveReminder(const std::string& type, MSMoveReminder* rem, double pos, bool keep) const {
    3018              :     OutputDevice& od = OutputDevice::getDeviceByOption("movereminder-output");
    3019              :     od.openTag("movereminder");
    3020              :     od.writeAttr(SUMO_ATTR_TIME, STEPS2TIME(MSNet::getInstance()->getCurrentTimeStep()));
    3021              :     od.writeAttr("veh", getID());
    3022              :     od.writeAttr(SUMO_ATTR_ID, rem->getDescription());
    3023              :     od.writeAttr("type", type);
    3024              :     od.writeAttr("pos", toString(pos));
    3025              :     od.writeAttr("keep", toString(keep));
    3026              :     od.closeTag();
    3027              : }
    3028              : #endif
    3029              : 
    3030              : 
    3031              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1