LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDevice_Vehroutes.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 95.9 % 345 331
Test Date: 2026-03-27 16:39:44 Functions: 95.0 % 20 19

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2009-2026 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    MSDevice_Vehroutes.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Laura Bieker
      17              : /// @author  Michael Behrisch
      18              : /// @author  Jakob Erdmann
      19              : /// @date    Fri, 30.01.2009
      20              : ///
      21              : // A device which collects info on the vehicle trip
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : 
      25              : #include <microsim/MSGlobals.h>
      26              : #include <microsim/MSNet.h>
      27              : #include <microsim/MSLane.h>
      28              : #include <microsim/MSEdge.h>
      29              : #include <microsim/MSRoute.h>
      30              : #include <microsim/MSStop.h>
      31              : #include <microsim/MSVehicle.h>
      32              : #include <microsim/MSVehicleType.h>
      33              : #include <microsim/transportables/MSTransportableControl.h>
      34              : #include <utils/vehicle/SUMOVehicle.h>
      35              : #include <utils/options/OptionsCont.h>
      36              : #include <utils/iodevices/OutputDevice_String.h>
      37              : #include <utils/xml/SUMOSAXAttributes.h>
      38              : #include "MSDevice_Vehroutes.h"
      39              : 
      40              : 
      41              : // ===========================================================================
      42              : // static member variables
      43              : // ===========================================================================
      44              : bool MSDevice_Vehroutes::mySaveExits = false;
      45              : bool MSDevice_Vehroutes::myLastRouteOnly = false;
      46              : bool MSDevice_Vehroutes::myDUAStyle = false;
      47              : bool MSDevice_Vehroutes::myWriteCosts = false;
      48              : bool MSDevice_Vehroutes::mySorted = false;
      49              : bool MSDevice_Vehroutes::myIntendedDepart = false;
      50              : bool MSDevice_Vehroutes::myRouteLength = false;
      51              : bool MSDevice_Vehroutes::mySkipPTLines = false;
      52              : bool MSDevice_Vehroutes::myIncludeIncomplete = false;
      53              : bool MSDevice_Vehroutes::myWriteStopPriorEdges = false;
      54              : bool MSDevice_Vehroutes::myWriteInternal = false;
      55              : MSDevice_Vehroutes::StateListener MSDevice_Vehroutes::myStateListener;
      56              : MSDevice_Vehroutes::SortedRouteInfo MSDevice_Vehroutes::myRouteInfos;
      57              : 
      58              : 
      59              : // ===========================================================================
      60              : // method definitions
      61              : // ===========================================================================
      62              : // ---------------------------------------------------------------------------
      63              : // static initialisation methods
      64              : // ---------------------------------------------------------------------------
      65              : void
      66        41191 : MSDevice_Vehroutes::init() {
      67        41191 :     const OptionsCont& oc = OptionsCont::getOptions();
      68        82382 :     if (oc.isSet("vehroute-output")) {
      69        11978 :         OutputDevice::createDeviceByOption("vehroute-output", "routes", "routes_file.xsd");
      70        11960 :         OutputDevice& od = OutputDevice::getDeviceByOption("vehroute-output");
      71         5980 :         if (!od.isXML()) {
      72            4 :             WRITE_WARNING("Vehroute output does not fully support tabular formats. Sorting and stop output will not work.");
      73              :         }
      74         5980 :         mySaveExits = oc.getBool("vehroute-output.exit-times");
      75         5980 :         myLastRouteOnly = oc.getBool("vehroute-output.last-route");
      76         5980 :         myDUAStyle = oc.getBool("vehroute-output.dua");
      77         5980 :         myWriteCosts = oc.getBool("vehroute-output.cost");
      78         5980 :         mySorted = myDUAStyle || oc.getBool("vehroute-output.sorted");
      79         5980 :         myIntendedDepart = oc.getBool("vehroute-output.intended-depart");
      80         5980 :         myRouteLength = oc.getBool("vehroute-output.route-length");
      81         5980 :         mySkipPTLines = oc.getBool("vehroute-output.skip-ptlines");
      82         5980 :         myIncludeIncomplete = oc.getBool("vehroute-output.incomplete");
      83         5980 :         myWriteStopPriorEdges = oc.getBool("vehroute-output.stop-edges");
      84         5980 :         myWriteInternal = oc.getBool("vehroute-output.internal");
      85         5980 :         MSNet::getInstance()->addVehicleStateListener(&myStateListener);
      86         5980 :         myRouteInfos.routeOut = &od;
      87              :     }
      88        41185 : }
      89              : 
      90              : 
      91              : void
      92        44322 : MSDevice_Vehroutes::insertOptions(OptionsCont& oc) {
      93        44322 :     oc.addOptionSubTopic("Vehroutes Device");
      94        88644 :     insertDefaultAssignmentOptions("vehroute", "Vehroutes Device", oc);
      95        44322 : }
      96              : 
      97              : 
      98              : MSDevice_Vehroutes*
      99      6498579 : MSDevice_Vehroutes::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into, int maxRoutes) {
     100      6498579 :     if (maxRoutes < std::numeric_limits<int>::max()) {
     101      1882986 :         return new MSDevice_Vehroutes(v, "vehroute_" + v.getID(), maxRoutes);
     102              :     }
     103      5557086 :     if (mySkipPTLines && v.getParameter().line != "") {
     104              :         return nullptr;
     105              :     }
     106      5555960 :     OptionsCont& oc = OptionsCont::getOptions();
     107     11111920 :     if (equippedByDefaultAssignmentOptions(oc, "vehroute", v, oc.isSet("vehroute-output"))) {
     108       138280 :         if (myLastRouteOnly) {
     109              :             maxRoutes = 0;
     110              :         }
     111       138280 :         myStateListener.myDevices[&v] = new MSDevice_Vehroutes(v, "vehroute_" + v.getID(), maxRoutes);
     112       138280 :         into.push_back(myStateListener.myDevices[&v]);
     113       138280 :         return myStateListener.myDevices[&v];
     114              :     }
     115              :     return nullptr;
     116              : }
     117              : 
     118              : 
     119              : // ---------------------------------------------------------------------------
     120              : // MSDevice_Vehroutes::StateListener-methods
     121              : // ---------------------------------------------------------------------------
     122              : void
     123       528727 : MSDevice_Vehroutes::StateListener::vehicleStateChanged(const SUMOVehicle* const vehicle, MSNet::VehicleState to, const std::string& info) {
     124       528727 :     if (to == MSNet::VehicleState::NEWROUTE) {
     125              :         const auto& deviceEntry = myDevices.find(vehicle);
     126       100779 :         if (deviceEntry != myDevices.end()) {
     127        99361 :             deviceEntry->second->addRoute(info);
     128              :         }
     129              :     }
     130       528727 : }
     131              : 
     132              : 
     133              : // ---------------------------------------------------------------------------
     134              : // MSDevice_Vehroutes-methods
     135              : // ---------------------------------------------------------------------------
     136      1079773 : MSDevice_Vehroutes::MSDevice_Vehroutes(SUMOVehicle& holder, const std::string& id, int maxRoutes) :
     137              :     MSVehicleDevice(holder, id),
     138      1079773 :     myCurrentRoute(holder.getRoutePtr()),
     139      1079773 :     myMaxRoutes(maxRoutes),
     140      1079773 :     myLastSavedAt(nullptr),
     141      1079773 :     myLastRouteIndex(-1),
     142      1079773 :     myDepartLane(-1),
     143      1079773 :     myDepartPos(-1),
     144      1079773 :     myDepartSpeed(-1),
     145      1079773 :     myDepartPosLat(0),
     146      1079773 :     myStopOut(2) {
     147      1079773 : }
     148              : 
     149              : 
     150      2159426 : MSDevice_Vehroutes::~MSDevice_Vehroutes() {
     151      1079713 :     myStateListener.myDevices.erase(&myHolder);
     152      2159426 : }
     153              : 
     154              : 
     155              : bool
     156     13448888 : MSDevice_Vehroutes::notifyEnter(SUMOTrafficObject& veh, MSMoveReminder::Notification reason, const MSLane* enteredLane) {
     157     13448888 :     if (reason == MSMoveReminder::NOTIFICATION_DEPARTED) {
     158       891298 :         if (mySorted && myStateListener.myDevices[static_cast<SUMOVehicle*>(&veh)] == this) {
     159         4586 :             const SUMOTime departure = myIntendedDepart ? myHolder.getParameter().depart : MSNet::getInstance()->getCurrentTimeStep();
     160         4586 :             myRouteInfos.departureCounts[departure]++;
     161              :         }
     162       891298 :         if (!MSGlobals::gUseMesoSim) {
     163              :             const MSVehicle& vehicle = static_cast<MSVehicle&>(veh);
     164       573759 :             myDepartLane = vehicle.getLane()->getIndex();
     165       573759 :             myDepartPosLat = vehicle.getLateralPositionOnLane();
     166              :         }
     167       891298 :         myDepartSpeed = veh.getSpeed();
     168       891298 :         myDepartPos = veh.getPositionOnLane();
     169              :     }
     170     13448888 :     if (myWriteStopPriorEdges) {
     171           68 :         if (MSGlobals::gUseMesoSim) {
     172           48 :             const MSEdge* e = veh.getEdge();
     173           48 :             if (myPriorEdges.empty() || myPriorEdges.back() != e) {
     174           12 :                 myPriorEdges.push_back(e);
     175              :             }
     176              :         } else {
     177           20 :             myPriorEdges.push_back(&enteredLane->getEdge());
     178              :         }
     179              :     }
     180     13448888 :     myLastRouteIndex = myHolder.getRoutePosition();
     181     13448888 :     return true;
     182              : }
     183              : 
     184              : 
     185              : bool
     186     13412969 : MSDevice_Vehroutes::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
     187     13412969 :     if (mySaveExits && reason != NOTIFICATION_LANE_CHANGE && reason != NOTIFICATION_PARKING && reason != NOTIFICATION_SEGMENT) {
     188       275649 :         const MSEdge* edge = myWriteInternal ? dynamic_cast<MSBaseVehicle&>(veh).getCurrentEdge() : veh.getEdge();
     189       275649 :         if (myLastSavedAt != edge) {
     190       155512 :             myExits.push_back(MSNet::getInstance()->getCurrentTimeStep());
     191       155512 :             myLastSavedAt = edge;
     192              :         }
     193              :     }
     194     13412969 :     return true;
     195              : }
     196              : 
     197              : 
     198              : void
     199        22862 : MSDevice_Vehroutes::notifyStopEnded() {
     200        22862 :     SUMOVehicleParameter::Stop stop = myHolder.getStops().front().pars;
     201        22862 :     const bool closeLater = myWriteStopPriorEdges || mySaveExits;
     202        22862 :     if (mySaveExits) {
     203              :         // prevent duplicate output
     204         1567 :         stop.parametersSet &=  ~(STOP_STARTED_SET | STOP_ENDED_SET);
     205              :     }
     206        22862 :     stop.write(myStopOut, !closeLater);
     207        22862 :     if (myWriteStopPriorEdges) {
     208              :         // calculate length
     209           16 :         double priorEdgesLength = 0;
     210           40 :         for (int i = 0; i < (int)myPriorEdges.size(); i++) {
     211           24 :             if (i == 0) {
     212           16 :                 priorEdgesLength += myPriorEdges.at(i)->getLength();
     213            8 :             } else if (myPriorEdges.at(i)->getID() != myPriorEdges.at(i - 1)->getID()) {
     214            8 :                 priorEdgesLength += myPriorEdges.at(i)->getLength();
     215              :             }
     216              :         }
     217           16 :         myStopOut.writeAttr("priorEdges", myPriorEdges);
     218              :         myPriorEdges.clear();
     219           16 :         myStopOut.writeAttr("priorEdgesLength", priorEdgesLength);
     220              :     }
     221        22862 :     if (mySaveExits) {
     222         1567 :         myStopOut.writeAttr(SUMO_ATTR_STARTED, time2string(stop.started));
     223         1567 :         myStopOut.writeAttr(SUMO_ATTR_ENDED, stop.ended < 0 ? "-1" : time2string(stop.ended));
     224              :     }
     225        22862 :     if (closeLater) {
     226         3166 :         myStopOut.closeTag();
     227              :     }
     228        22862 : }
     229              : 
     230              : 
     231              : void
     232       166616 : MSDevice_Vehroutes::writeXMLRoute(OutputDevice& os, int index) const {
     233         9872 :     if (index == 0 && !myIncludeIncomplete && myReplacedRoutes[index].route->size() == 2 &&
     234       170368 :             myReplacedRoutes[index].route->getEdges().front()->isTazConnector() &&
     235            0 :             myReplacedRoutes[index].route->getEdges().back()->isTazConnector()) {
     236              :         return;
     237              :     }
     238              :     // check if a previous route shall be written
     239              :     //std::cout << " writeXMLRoute index=" << index << " numReroutes=" << myHolder.getNumberReroutes() << "\n";
     240       166616 :     const int routesToSkip = myHolder.getParameter().wasSet(VEHPARS_FORCE_REROUTE) ? 1 : 0;
     241       166616 :     os.openTag(SUMO_TAG_ROUTE);
     242       166616 :     if (index >= 0) {
     243              :         assert((int)myReplacedRoutes.size() > index);
     244        39188 :         if (myDUAStyle || myWriteCosts) {
     245        14677 :             os.writeAttr(SUMO_ATTR_COST, myReplacedRoutes[index].route->getCosts());
     246              :         }
     247        39188 :         if (myWriteCosts) {
     248        14677 :             os.writeAttr(SUMO_ATTR_SAVINGS, myReplacedRoutes[index].route->getSavings());
     249              :         }
     250              :         // write edge on which the vehicle was when the route was valid
     251       116183 :         os.writeAttr("replacedOnEdge", (myReplacedRoutes[index].edge ?
     252              :                                         myReplacedRoutes[index].edge->getID() : ""));
     253        39188 :         if (myReplacedRoutes[index].lastRouteIndex > 0) {
     254              :             // do not write the default
     255        27295 :             os.writeAttr(SUMO_ATTR_REPLACED_ON_INDEX, myReplacedRoutes[index].lastRouteIndex);
     256              :         }
     257              :         // write the reason for replacement
     258        39188 :         os.writeAttr("reason", myReplacedRoutes[index].info);
     259              : 
     260              :         // write the time at which the route was replaced
     261        39188 :         os.writeAttr(SUMO_ATTR_REPLACED_AT_TIME, time2string(myReplacedRoutes[index].time));
     262        39188 :         os.writeAttr(SUMO_ATTR_PROB, "0");
     263        39188 :         OutputDevice_String edgesD;
     264              :         // always write the part that was actually driven and the rest of the current route that wasn't yet driven
     265              :         int start = 0;
     266       188561 :         for (int i = routesToSkip; i < index; i++) {
     267       149373 :             if (myReplacedRoutes[i].edge != nullptr) {
     268       148546 :                 int end = myReplacedRoutes[i].lastRouteIndex;
     269       148546 :                 myReplacedRoutes[i].route->writeEdgeIDs(edgesD, start, end, myWriteInternal, myHolder.getVClass());
     270              :             }
     271       149373 :             start = myReplacedRoutes[i].newRouteIndex;
     272              :         }
     273        39188 :         myReplacedRoutes[index].route->writeEdgeIDs(edgesD, start, -1, myWriteInternal, myHolder.getVClass());
     274        39188 :         std::string edgesS = edgesD.getString();
     275              :         edgesS.pop_back(); // remove last ' '
     276        39188 :         os.writeAttr(SUMO_ATTR_EDGES, edgesS);
     277        39188 :         if (myRouteLength) {
     278         7154 :             const bool includeInternalLengths = MSGlobals::gUsingInternalLanes && MSNet::getInstance()->hasInternalLinks();
     279              :             ConstMSRoutePtr route = myReplacedRoutes[index].route;
     280         7154 :             const double routeLength = route->getDistanceBetween(myHolder.getDepartPos(), route->getEdges().back()->getLength(),
     281         7154 :                                        route->begin(), route->end(), includeInternalLengths);
     282         7154 :             os.writeAttr("routeLength", routeLength);
     283              :         }
     284        39188 :     } else {
     285       127428 :         if (myDUAStyle || myWriteCosts) {
     286        32936 :             os.writeAttr(SUMO_ATTR_COST, myHolder.getRoute().getCosts());
     287              :         }
     288       127428 :         if (myWriteCosts) {
     289        32930 :             os.writeAttr(SUMO_ATTR_SAVINGS, myHolder.getRoute().getSavings());
     290              :         }
     291       127428 :         OutputDevice_String edgesD;
     292              :         int numWritten = 0;
     293              :         int start = 0;
     294       127428 :         if (myHolder.getNumberReroutes() > 0) {
     295              :             assert((int)myReplacedRoutes.size() <= myHolder.getNumberReroutes());
     296        96074 :             for (int i = routesToSkip; i < (int)myReplacedRoutes.size(); i++) {
     297        38370 :                 if (myReplacedRoutes[i].edge != nullptr) {
     298        37227 :                     int end = myReplacedRoutes[i].lastRouteIndex;
     299        37227 :                     numWritten += myReplacedRoutes[i].route->writeEdgeIDs(edgesD, start, end, myWriteInternal, myHolder.getVClass());
     300              :                 }
     301        38370 :                 start = myReplacedRoutes[i].newRouteIndex;
     302              :             }
     303              :         }
     304       127428 :         numWritten += myCurrentRoute->writeEdgeIDs(edgesD, start, -1, myWriteInternal, myHolder.getVClass());
     305       127428 :         std::string edgesS = edgesD.getString();
     306              :         edgesS.pop_back(); // remove last ' '
     307       127428 :         os.writeAttr(SUMO_ATTR_EDGES, edgesS);
     308              : 
     309       127428 :         if (mySaveExits) {
     310              :             std::vector<std::string> exits;
     311       134014 :             for (SUMOTime t : myExits) {
     312       247720 :                 exits.push_back(time2string(t));
     313              :             }
     314              :             assert(numWritten >= (int)myExits.size());
     315        20308 :             std::vector<std::string> missing(numWritten - (int)myExits.size(), "-1");
     316        10154 :             exits.insert(exits.end(), missing.begin(), missing.end());
     317        10154 :             os.writeAttr(SUMO_ATTR_EXITTIMES, exits);
     318        10154 :         }
     319       127428 :     }
     320       333232 :     os.closeTag();
     321              : }
     322              : 
     323              : 
     324              : void
     325       126120 : MSDevice_Vehroutes::generateOutput(OutputDevice* /*tripinfoOut*/) const {
     326       126120 :     writeOutput(true);
     327       126120 : }
     328              : 
     329              : 
     330              : void
     331       127434 : MSDevice_Vehroutes::writeOutput(const bool hasArrived) const {
     332       127434 :     const OptionsCont& oc = OptionsCont::getOptions();
     333       127434 :     OutputDevice& routeOut = OutputDevice::getDeviceByOption("vehroute-output");
     334       127434 :     OutputDevice_String stringOut(1);
     335       127434 :     OutputDevice& od = mySorted && routeOut.isXML() ? stringOut : routeOut;
     336       127434 :     SUMOVehicleParameter tmp = myHolder.getParameter();
     337       127434 :     tmp.depart = myIntendedDepart ? myHolder.getParameter().depart : myHolder.getDeparture();
     338       127434 :     if (!MSGlobals::gUseMesoSim) {
     339        91187 :         if (tmp.wasSet(VEHPARS_DEPARTLANE_SET)) {
     340        14291 :             tmp.departLaneProcedure = DepartLaneDefinition::GIVEN;
     341        14291 :             tmp.departLane = myDepartLane;
     342              :         }
     343        91187 :         if (tmp.wasSet(VEHPARS_DEPARTPOSLAT_SET)) {
     344            8 :             tmp.departPosLatProcedure = (tmp.departPosLatProcedure == DepartPosLatDefinition::RANDOM
     345            4 :                                          ? DepartPosLatDefinition::GIVEN_VEHROUTE
     346              :                                          : DepartPosLatDefinition::GIVEN);
     347            4 :             tmp.departPosLat = myDepartPosLat;
     348              :         }
     349              :     }
     350       127434 :     if (tmp.wasSet(VEHPARS_DEPARTPOS_SET)) {
     351        73106 :         tmp.departPosProcedure = ((tmp.departPosProcedure != DepartPosDefinition::GIVEN
     352        36553 :                                    && tmp.departPosProcedure != DepartPosDefinition::STOP)
     353        36553 :                                   ? DepartPosDefinition::GIVEN_VEHROUTE
     354              :                                   : DepartPosDefinition::GIVEN);
     355        36553 :         tmp.departPos = myDepartPos;
     356              :     }
     357       127434 :     if (tmp.wasSet(VEHPARS_DEPARTSPEED_SET)) {
     358        94132 :         tmp.departSpeedProcedure = ((tmp.departSpeedProcedure != DepartSpeedDefinition::GIVEN
     359        47066 :                                      && tmp.departSpeedProcedure != DepartSpeedDefinition::LIMIT)
     360        47066 :                                     ? DepartSpeedDefinition::GIVEN_VEHROUTE
     361              :                                     : DepartSpeedDefinition::GIVEN);
     362        47066 :         tmp.departSpeed = myDepartSpeed;
     363              :     }
     364       380296 :     if (oc.getBool("vehroute-output.speedfactor") ||
     365       456652 :             (oc.isDefault("vehroute-output.speedfactor") && tmp.wasSet(VEHPARS_DEPARTSPEED_SET))) {
     366        49066 :         tmp.parametersSet |= VEHPARS_SPEEDFACTOR_SET;
     367        49066 :         tmp.speedFactor = myHolder.getChosenSpeedFactor();
     368              :     }
     369              : 
     370       127434 :     const std::string typeID = myHolder.getVehicleType().getID() != DEFAULT_VTYPE_ID ? myHolder.getVehicleType().getID() : "";
     371       127434 :     tmp.write(od, oc, SUMO_TAG_VEHICLE, typeID);
     372       127434 :     if (hasArrived) {
     373       252240 :         od.writeAttr("arrival", time2string(MSNet::getInstance()->getCurrentTimeStep()));
     374              :     }
     375       127434 :     if (myRouteLength) {
     376          951 :         const bool includeInternalLengths = MSGlobals::gUsingInternalLanes && MSNet::getInstance()->hasInternalLinks();
     377          951 :         const double finalPos = hasArrived ? myHolder.getArrivalPos() : myHolder.getPositionOnLane();
     378         1902 :         const double routeLength = myHolder.getRoute().getDistanceBetween(myHolder.getDepartPos(), finalPos,
     379          951 :                                    myHolder.getRoute().begin(), myHolder.getCurrentRouteEdge(), includeInternalLengths);
     380          951 :         od.writeAttr("routeLength", routeLength);
     381              :     }
     382       127434 :     if (myDUAStyle) {
     383           12 :         const RandomDistributor<ConstMSRoutePtr>* const routeDist = MSRoute::distDictionary("!" + myHolder.getID());
     384           12 :         if (routeDist != nullptr) {
     385              :             const std::vector<ConstMSRoutePtr>& routes = routeDist->getVals();
     386            6 :             unsigned index = 0;
     387            6 :             while (index < routes.size() && routes[index] != myCurrentRoute) {
     388            0 :                 ++index;
     389              :             }
     390            6 :             od.openTag(SUMO_TAG_ROUTE_DISTRIBUTION).writeAttr(SUMO_ATTR_LAST, index);
     391              :             const std::vector<double>& probs = routeDist->getProbs();
     392           18 :             for (int i = 0; i < (int)routes.size(); ++i) {
     393           12 :                 od.setPrecision();
     394           12 :                 od.openTag(SUMO_TAG_ROUTE);
     395           12 :                 od.writeAttr(SUMO_ATTR_COST, routes[i]->getCosts());
     396           12 :                 if (myWriteCosts) {
     397            0 :                     od.writeAttr(SUMO_ATTR_SAVINGS, routes[i]->getSavings());
     398              :                 }
     399           12 :                 od.setPrecision(8);
     400           12 :                 od.writeAttr(SUMO_ATTR_PROB, probs[i]);
     401           12 :                 od.setPrecision();
     402           12 :                 OutputDevice_String edgesD;
     403           12 :                 routes[i]->writeEdgeIDs(edgesD, 0, -1, myWriteInternal, myHolder.getVClass());
     404           12 :                 std::string edgesS = edgesD.getString();
     405              :                 edgesS.pop_back(); // remove last ' '
     406           12 :                 od.writeAttr(SUMO_ATTR_EDGES, edgesS);
     407           24 :                 od.closeTag();
     408           12 :             }
     409           12 :             od.closeTag();
     410              :         } else {
     411            6 :             writeXMLRoute(od);
     412              :         }
     413              :     } else {
     414              :         std::string dummyMsg;
     415       127422 :         const int routesToSkip = (myHolder.getParameter().wasSet(VEHPARS_FORCE_REROUTE)
     416        49617 :                                   && !myIncludeIncomplete
     417        49497 :                                   && myReplacedRoutes.size() > 0
     418       224885 :                                   && !myHolder.hasValidRoute(dummyMsg, myReplacedRoutes[0].route) ? 1 : 0);
     419       127422 :         if ((int)myReplacedRoutes.size() > routesToSkip) {
     420        22259 :             od.openTag(SUMO_TAG_ROUTE_DISTRIBUTION);
     421        61447 :             for (int i = routesToSkip; i < (int)myReplacedRoutes.size(); ++i) {
     422        39188 :                 writeXMLRoute(od, i);
     423              :             }
     424        22259 :             writeXMLRoute(od);
     425        44518 :             od.closeTag();
     426              :         } else {
     427       105163 :             writeXMLRoute(od);
     428              :         }
     429              :     }
     430       127434 :     if (routeOut.isXML()) {
     431       254168 :         od << myStopOut.getString();
     432              :     }
     433       127434 :     myHolder.getParameter().writeParams(od);
     434       127434 :     od.closeTag();
     435       127434 :     od.lf();
     436       127434 :     if (mySorted && routeOut.isXML()) {
     437              :         // numerical id reflects loading order
     438         8844 :         writeSortedOutput(&myRouteInfos, tmp.depart, myHolder.getNumericalID(), stringOut.getString());
     439              :     }
     440       127434 : }
     441              : 
     442              : 
     443              : ConstMSRoutePtr
     444            0 : MSDevice_Vehroutes::getRoute(int index) const {
     445            0 :     if (index < (int)myReplacedRoutes.size()) {
     446            0 :         return myReplacedRoutes[index].route;
     447              :     } else {
     448              :         return nullptr;
     449              :     }
     450              : }
     451              : 
     452              : 
     453              : void
     454        99361 : MSDevice_Vehroutes::addRoute(const std::string& info) {
     455        99361 :     if (myMaxRoutes > 0) {
     456              :         //std::cout << SIMTIME << " " << getID() << " departed=" << myHolder.hasDeparted() << " lastIndex=" << myLastRouteIndex << " start=" << myHolder.getRoutePosition() << "\n";
     457       437034 :         myReplacedRoutes.push_back(RouteReplaceInfo(
     458       129562 :                                        myHolder.hasDeparted() ?  myHolder.getEdge() : nullptr,
     459              :                                        MSNet::getInstance()->getCurrentTimeStep(), myCurrentRoute, info,
     460              :                                        myLastRouteIndex,
     461       129562 :                                        myHolder.hasDeparted() ? myHolder.getRoutePosition() : 0));
     462        88955 :         if ((int)myReplacedRoutes.size() > myMaxRoutes) {
     463              :             myReplacedRoutes.erase(myReplacedRoutes.begin());
     464              :         }
     465              :     }
     466        99361 :     myCurrentRoute = myHolder.getRoutePtr();
     467        99361 : }
     468              : 
     469              : 
     470              : void
     471        39421 : MSDevice_Vehroutes::writePendingOutput(const bool includeUnfinished) {
     472        39421 :     MSNet* const net = MSNet::getInstance();
     473              : 
     474        39421 :     if (!includeUnfinished) {
     475        39044 :         if (mySorted) {
     476          117 :             for (const auto& routeInfo : myRouteInfos.routeXML) {
     477           36 :                 for (const auto& rouXML : routeInfo.second) {
     478           20 :                     (*myRouteInfos.routeOut) << rouXML.second;
     479              :                 }
     480              :             }
     481          101 :             if (net->hasPersons()) {
     482           22 :                 const SortedRouteInfo& personRouteInfos = net->getPersonControl().getRouteInfo();
     483           22 :                 if (personRouteInfos.routeOut != myRouteInfos.routeOut) {
     484            0 :                     for (const auto& routeInfo : personRouteInfos.routeXML) {
     485            0 :                         for (const auto& rouXML : routeInfo.second) {
     486            0 :                             (*personRouteInfos.routeOut) << rouXML.second;
     487              :                         }
     488              :                     }
     489              :                 }
     490              :             }
     491              :         }
     492        39044 :         return;
     493              :     }
     494         1691 :     for (const auto& it : myStateListener.myDevices) {
     495         1314 :         if (it.first->hasDeparted()) {
     496         1314 :             if (it.first->isStopped()) {
     497          682 :                 it.second->notifyStopEnded();
     498              :             }
     499         1314 :             it.second->writeOutput(false);
     500              :         }
     501              :     }
     502              :     // unfinished persons
     503          377 :     if (net->hasPersons()) {
     504          198 :         MSTransportableControl& pc = net->getPersonControl();
     505          270 :         while (pc.loadedBegin() != pc.loadedEnd()) {
     506           72 :             pc.erase(pc.loadedBegin()->second);
     507              :         }
     508              :     }
     509              :     // Flush any remaining sorted outputs that may still be buffered
     510          377 :     if (mySorted) {
     511            4 :         for (const auto& routeInfo : myRouteInfos.routeXML) {
     512            4 :             for (const auto& rouXML : routeInfo.second) {
     513            2 :                 (*myRouteInfos.routeOut) << rouXML.second;
     514              :             }
     515              :         }
     516            2 :         if (net->hasPersons()) {
     517            0 :             const SortedRouteInfo& personRouteInfos = net->getPersonControl().getRouteInfo();
     518            0 :             if (personRouteInfos.routeOut != myRouteInfos.routeOut) {
     519            0 :                 for (const auto& routeInfo : personRouteInfos.routeXML) {
     520            0 :                     for (const auto& rouXML : routeInfo.second) {
     521            0 :                         (*personRouteInfos.routeOut) << rouXML.second;
     522              :                     }
     523              :                 }
     524              :             }
     525              :         }
     526              :     }
     527              : }
     528              : 
     529              : 
     530              : void
     531          108 : MSDevice_Vehroutes::registerTransportableDepart(SUMOTime depart) {
     532          108 :     myRouteInfos.departureCounts[depart]++;
     533          108 : }
     534              : 
     535              : 
     536              : void
     537         4544 : MSDevice_Vehroutes::writeSortedOutput(MSDevice_Vehroutes::SortedRouteInfo* routeInfo, SUMOTime depart, const SUMOTrafficObject::NumericalID id, const std::string& xmlOutput) {
     538         4544 :     if (routeInfo->routeOut == myRouteInfos.routeOut) {
     539              :         routeInfo = &myRouteInfos;
     540              :     }
     541         4544 :     routeInfo->routeXML[depart][id] = xmlOutput;
     542         4544 :     routeInfo->departureCounts[depart]--;
     543              :     std::map<const SUMOTime, int>::iterator it = routeInfo->departureCounts.begin();
     544         7771 :     while (it != routeInfo->departureCounts.end() && it->second == 0) {
     545         7749 :         for (const auto& rouXML : routeInfo->routeXML[it->first]) {
     546         4522 :             (*routeInfo->routeOut) << rouXML.second;
     547              :         }
     548              :         routeInfo->routeXML.erase(it->first);
     549              :         it = routeInfo->departureCounts.erase(it);
     550              :     }
     551         4544 : }
     552              : 
     553              : 
     554              : void
     555         3114 : MSDevice_Vehroutes::saveState(OutputDevice& out) const {
     556         3114 :     out.openTag(SUMO_TAG_DEVICE);
     557         3114 :     out.writeAttr(SUMO_ATTR_ID, getID());
     558              :     std::vector<std::string> internals;
     559         3114 :     if (!MSGlobals::gUseMesoSim) {
     560         1825 :         internals.push_back(toString(myDepartLane));
     561         3650 :         internals.push_back(toString(myDepartPosLat));
     562              :     }
     563         3114 :     internals.push_back(toString(myDepartSpeed));
     564         3114 :     internals.push_back(toString(myDepartPos));
     565         3114 :     internals.push_back(toString(myReplacedRoutes.size()));
     566         5899 :     for (int i = 0; i < (int)myReplacedRoutes.size(); ++i) {
     567         2785 :         const std::string replacedOnEdge = myReplacedRoutes[i].edge == nullptr ? "!NULL" : myReplacedRoutes[i].edge->getID();
     568         2785 :         internals.push_back(replacedOnEdge);
     569         5570 :         internals.push_back(toString(myReplacedRoutes[i].time));
     570         2785 :         internals.push_back(myReplacedRoutes[i].route->getID());
     571         2785 :         internals.push_back(myReplacedRoutes[i].info);
     572         2785 :         internals.push_back(toString(myReplacedRoutes[i].lastRouteIndex));
     573         5570 :         internals.push_back(toString(myReplacedRoutes[i].newRouteIndex));
     574              :     }
     575         3114 :     out.writeAttr(SUMO_ATTR_STATE, toString(internals));
     576         3114 :     if (mySaveExits && myExits.size() > 0) {
     577            9 :         out.writeAttr(SUMO_ATTR_EXITTIMES, myExits);
     578            9 :         out.writeAttr(SUMO_ATTR_EDGE, myLastSavedAt->getID());
     579              :     }
     580         3114 :     out.closeTag();
     581         3114 : }
     582              : 
     583              : 
     584              : void
     585         2204 : MSDevice_Vehroutes::loadState(const SUMOSAXAttributes& attrs) {
     586         2204 :     std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
     587         2204 :     if (!MSGlobals::gUseMesoSim) {
     588         1291 :         bis >> myDepartLane;
     589         1291 :         bis >> myDepartPosLat;
     590              :     }
     591         2204 :     bis >> myDepartSpeed;
     592         2204 :     bis >> myDepartPos;
     593              :     int size;
     594         2204 :     bis >> size;
     595         3965 :     for (int i = 0; i < size; ++i) {
     596              :         std::string edgeID;
     597              :         SUMOTime time;
     598              :         std::string routeID;
     599              :         std::string info;
     600              :         int lastIndex;
     601              :         int newIndex;
     602         1761 :         bis >> edgeID;
     603              :         bis >> time;
     604         1761 :         bis >> routeID;
     605         1761 :         bis >> info;
     606         1761 :         bis >> lastIndex;
     607         1761 :         bis >> newIndex;
     608              : 
     609         1761 :         ConstMSRoutePtr route = MSRoute::dictionary(routeID);
     610         1761 :         if (route != nullptr) {
     611         2766 :             myReplacedRoutes.push_back(RouteReplaceInfo(MSEdge::dictionary(edgeID), time, route, info, lastIndex, newIndex));
     612              :         }
     613              :     }
     614         2204 :     if (mySaveExits && attrs.hasAttribute(SUMO_ATTR_EXITTIMES)) {
     615            9 :         bool ok = true;
     616           20 :         for (const std::string& t : attrs.get<std::vector<std::string> >(SUMO_ATTR_EXITTIMES, nullptr, ok)) {
     617           11 :             myExits.push_back(StringUtils::toLong(t));
     618            9 :         }
     619            9 :         if (attrs.hasAttribute(SUMO_ATTR_EDGE)) {
     620            9 :             myLastSavedAt = MSEdge::dictionary(attrs.getString(SUMO_ATTR_EDGE));
     621              :         }
     622              :     }
     623         2204 :     if (myHolder.hasDeparted()) {
     624          732 :         myLastRouteIndex = myHolder.getRoutePosition();
     625              :     }
     626         2204 : }
     627              : 
     628              : 
     629              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1