LCOV - code coverage report
Current view: top level - src/router - ROVehicle.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 87.9 % 207 182
Test Date: 2026-03-26 16:31:35 Functions: 100.0 % 9 9

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2002-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    ROVehicle.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Axel Wegener
      17              : /// @author  Michael Behrisch
      18              : /// @author  Jakob Erdmann
      19              : /// @date    Sept 2002
      20              : ///
      21              : // A vehicle as used by router
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : 
      25              : #include <string>
      26              : #include <iostream>
      27              : #include <utils/common/StringUtils.h>
      28              : #include <utils/common/ToString.h>
      29              : #include <utils/common/MsgHandler.h>
      30              : #include <utils/geom/GeoConvHelper.h>
      31              : #include <utils/vehicle/SUMOVTypeParameter.h>
      32              : #include <utils/options/OptionsCont.h>
      33              : #include <utils/iodevices/OutputDevice.h>
      34              : #include "RORouteDef.h"
      35              : #include "RORoute.h"
      36              : #include "ROHelper.h"
      37              : #include "RONet.h"
      38              : #include "ROLane.h"
      39              : #include "ROVehicle.h"
      40              : 
      41              : #define INVALID_STOP_POS -1
      42              : 
      43              : // ===========================================================================
      44              : // static members
      45              : // ===========================================================================
      46              : std::map<ConstROEdgeVector, std::string> ROVehicle::mySavedRoutes;
      47              : 
      48              : // ===========================================================================
      49              : // method definitions
      50              : // ===========================================================================
      51       297964 : ROVehicle::ROVehicle(const SUMOVehicleParameter& pars,
      52              :                      RORouteDef* route, const SUMOVTypeParameter* type,
      53       297964 :                      const RONet* net, MsgHandler* errorHandler):
      54              :     RORoutable(pars, type),
      55       297964 :     myRoute(route),
      56       297964 :     myJumpTime(-1) {
      57              :     getParameter().stops.clear();
      58       595003 :     if (route != nullptr && route->getFirstRoute() != nullptr) {
      59       594410 :         for (StopParVector::const_iterator s = route->getFirstRoute()->getStops().begin(); s != route->getFirstRoute()->getStops().end(); ++s) {
      60          332 :             addStop(*s, net, errorHandler);
      61              :         }
      62              :     }
      63       298488 :     for (StopParVector::const_iterator s = pars.stops.begin(); s != pars.stops.end(); ++s) {
      64          524 :         addStop(*s, net, errorHandler);
      65              :     }
      66       297964 : }
      67              : 
      68              : 
      69              : void
      70          856 : ROVehicle::addStop(const SUMOVehicleParameter::Stop& stopPar, const RONet* net, MsgHandler* errorHandler) {
      71          856 :     const ROEdge* stopEdge = net->getEdge(stopPar.edge);
      72              :     assert(stopEdge != 0); // was checked when parsing the stop
      73          856 :     if (stopEdge->prohibits(this)) {
      74            0 :         if (errorHandler != nullptr) {
      75            0 :             errorHandler->inform("Stop edge '" + stopEdge->getID() + "' does not allow vehicle '" + getID() + "'.");
      76              :         }
      77              :         return;
      78              :     }
      79              :     // where to insert the stop
      80              :     StopParVector::iterator iter = getParameter().stops.begin();
      81          856 :     if (stopPar.index == STOP_INDEX_END || stopPar.index >= static_cast<int>(getParameter().stops.size())) {
      82              :         if (getParameter().stops.size() > 0) {
      83              :             iter = getParameter().stops.end();
      84              :         }
      85              :     } else {
      86            0 :         if (stopPar.index == STOP_INDEX_FIT) {
      87              :             iter = getParameter().stops.end();
      88              :         } else {
      89              :             iter += stopPar.index;
      90              :         }
      91              :     }
      92          856 :     getParameter().stops.insert(iter, stopPar);
      93          856 :     if (stopPar.jump >= 0) {
      94           24 :         if (stopEdge->isInternal()) {
      95            0 :             if (errorHandler != nullptr) {
      96            0 :                 errorHandler->inform("Jumps are not supported from internal stop edge '" + stopEdge->getID() + "'.");
      97              :             }
      98              :         } else {
      99           24 :             if (myJumpTime < 0) {
     100           20 :                 myJumpTime = 0;
     101              :             }
     102           24 :             myJumpTime += stopPar.jump;
     103              :         }
     104              :     }
     105              : }
     106              : 
     107              : 
     108       595835 : ROVehicle::~ROVehicle() {}
     109              : 
     110              : 
     111              : const ROEdge*
     112          511 : ROVehicle:: getDepartEdge() const {
     113          511 :     return myRoute->getFirstRoute()->getFirst();
     114              : }
     115              : 
     116              : 
     117              : void
     118       296811 : ROVehicle::computeRoute(const RORouterProvider& provider,
     119              :                         const bool removeLoops, MsgHandler* errorHandler) {
     120              :     SUMOAbstractRouter<ROEdge, ROVehicle>& router = provider.getVehicleRouter(getVClass());
     121       593622 :     std::string noRouteMsg = "The vehicle '" + getID() + "' has no valid route.";
     122              :     RORouteDef* const routeDef = getRouteDefinition();
     123              :     // check if the route definition is valid
     124       296811 :     if (routeDef == nullptr) {
     125            0 :         errorHandler->inform(noRouteMsg);
     126            0 :         myRoutingSuccess = false;
     127            0 :         return;
     128              :     }
     129       296811 :     routeDef->validateAlternatives(this, errorHandler);
     130       296808 :     if (routeDef->getFirstRoute() == nullptr) {
     131              :         // everything is invalid and no new routes will be computed
     132            3 :         myRoutingSuccess = false;
     133            3 :         return;
     134              :     }
     135       296808 :     RORoute* current = routeDef->buildCurrentRoute(router, getDepartureTime(), *this);
     136       296808 :     if (current == nullptr || current->size() == 0) {
     137        19134 :         delete current;
     138         1925 :         if (current == nullptr || !routeDef->discardSilent()) {
     139        51627 :             errorHandler->inform(noRouteMsg);
     140              :         }
     141        19134 :         myRoutingSuccess = false;
     142        19134 :         return;
     143              :     }
     144              :     // check whether we have to evaluate the route for not containing loops
     145       277674 :     if (removeLoops) {
     146         5215 :         const ROEdge* requiredStart = (getParameter().departPosProcedure == DepartPosDefinition::GIVEN
     147         5215 :                                        || getParameter().departLaneProcedure == DepartLaneDefinition::GIVEN ? current->getEdgeVector().front() : 0);
     148         5215 :         const ROEdge* requiredEnd = (getParameter().arrivalPosProcedure == ArrivalPosDefinition::GIVEN
     149         5215 :                                      || getParameter().arrivalLaneProcedure == ArrivalLaneDefinition::GIVEN ? current->getEdgeVector().back() : 0);
     150              :         ConstROEdgeVector mandatory;
     151         5243 :         for (auto m : getMandatoryEdges(requiredStart, requiredEnd)) {
     152           28 :             mandatory.push_back(m.edge);
     153         5215 :         }
     154         5215 :         current->recheckForLoops(mandatory);
     155              :         // check whether the route is still valid
     156         5215 :         if (current->size() == 0) {
     157            0 :             delete current;
     158            0 :             errorHandler->inform(noRouteMsg + " (after removing loops)");
     159            0 :             myRoutingSuccess = false;
     160              :             return;
     161              :         }
     162         5215 :     }
     163       277674 :     if (RONet::getInstance()->getMaxTraveltime() > 0) {
     164           12 :         double costs = router.recomputeCosts(current->getEdgeVector(), this, getDepartureTime());
     165           12 :         if (costs > RONet::getInstance()->getMaxTraveltime()) {
     166           40 :             errorHandler->inform(noRouteMsg + " (traveltime " + time2string(TIME2STEPS(costs)) + " exceeds max-traveltime)");
     167            8 :             delete current;
     168            8 :             myRoutingSuccess = false;
     169            8 :             return;
     170              :         }
     171              :     }
     172              :     // add built route
     173       277666 :     RORoute* replaced = routeDef->addAlternative(router, this, current, getDepartureTime(), errorHandler);
     174       277650 :     if (replaced != nullptr) {
     175       154870 :         if (getParameter().departEdge > 0) {
     176            6 :             updateIndex(replaced, current, getParameterMutable().departEdge);
     177              :         }
     178       154870 :         if (getParameter().arrivalEdge >= 0) {
     179            6 :             updateIndex(replaced, current, getParameterMutable().arrivalEdge);
     180              :         }
     181       154870 :         delete replaced;
     182              :     }
     183       277650 :     myRoutingSuccess = true;
     184              : }
     185              : 
     186              : 
     187              : void
     188           12 : ROVehicle::updateIndex(const RORoute* replaced, const RORoute* current, int& attr) {
     189              :     const ConstROEdgeVector& oldRoute = replaced->getEdgeVector();
     190           12 :     if ((int)oldRoute.size() > attr) {
     191           12 :         const ROEdge* old = oldRoute[attr];
     192              :         int skips = 0;
     193           63 :         for (int i = 0; i < attr; i++) {
     194           51 :             if (oldRoute[i] == old) {
     195            0 :                 skips++;
     196              :             }
     197              :         }
     198              :         int i = 0;
     199          120 :         for (const ROEdge* cand : current->getEdgeVector()) {
     200          108 :             if (cand == old) {
     201           12 :                 if (skips > 0) {
     202            0 :                     skips--;
     203              :                 } else {
     204           12 :                     attr = i;
     205              :                 }
     206              :             }
     207          108 :             i++;
     208              :         }
     209              :     }
     210           12 : }
     211              : 
     212              : 
     213              : std::vector<ROVehicle::Mandatory>
     214       294597 : ROVehicle::getMandatoryEdges(const ROEdge* requiredStart, const ROEdge* requiredEnd) const {
     215       294597 :     const RONet* net = RONet::getInstance();
     216              :     std::vector<Mandatory> mandatory;
     217       294597 :     if (requiredStart) {
     218              :         double departPos = 0;
     219       289390 :         if (getParameter().departPosProcedure == DepartPosDefinition::GIVEN) {
     220          705 :             departPos = SUMOVehicleParameter::interpretEdgePos(getParameter().departPos, requiredStart->getLength(), SUMO_ATTR_DEPARTPOS, "vehicle '" + getID() + "'");
     221              :         }
     222       289390 :         mandatory.push_back(Mandatory(requiredStart, departPos));
     223              :     }
     224       294597 :     if (getParameter().via.size() != 0) {
     225              :         // via takes precedence over stop edges
     226        48297 :         for (const std::string& via : getParameter().via) {
     227        33624 :             mandatory.push_back(Mandatory(net->getEdge(via), INVALID_STOP_POS));
     228              :         }
     229              :     } else {
     230       280636 :         for (const auto& stop : getParameter().stops) {
     231          712 :             const ROEdge* e = net->getEdge(stop.edge);
     232          712 :             if (e->isInternal()) {
     233              :                 // the edges before and after the internal edge are mandatory
     234            8 :                 const ROEdge* before = e->getNormalBefore();
     235            8 :                 const ROEdge* after = e->getNormalAfter();
     236            8 :                 mandatory.push_back(Mandatory(before, INVALID_STOP_POS));
     237            8 :                 mandatory.push_back(Mandatory(after, INVALID_STOP_POS, stop.jump));
     238              :             } else {
     239         1408 :                 double endPos = SUMOVehicleParameter::interpretEdgePos(stop.endPos, e->getLength(), SUMO_ATTR_ENDPOS, "stop of vehicle '" + getID() + "' on edge '" + e->getID() + "'");
     240          704 :                 mandatory.push_back(Mandatory(e, endPos, stop.jump));
     241              :             }
     242              :         }
     243              :     }
     244       294597 :     if (requiredEnd) {
     245              :         double arrivalPos = INVALID_STOP_POS;
     246       289390 :         if (getParameter().arrivalPosProcedure == ArrivalPosDefinition::GIVEN) {
     247          414 :             arrivalPos = SUMOVehicleParameter::interpretEdgePos(getParameter().arrivalPos, requiredEnd->getLength(), SUMO_ATTR_ARRIVALPOS, "vehicle '" + getID() + "'");
     248              :         }
     249       289390 :         mandatory.push_back(Mandatory(requiredEnd, arrivalPos));
     250              :     }
     251       294597 :     return mandatory;
     252            0 : }
     253              : 
     254              : 
     255              : void
     256       390797 : ROVehicle::saveAsXML(OutputDevice& os, OutputDevice* const typeos, bool asAlternatives, OptionsCont& options, int cloneIndex) const {
     257       390797 :     if (typeos != nullptr && getType() != nullptr && !getType()->saved) {
     258          393 :         getType()->write(*typeos);
     259          393 :         getType()->saved = true;
     260              :     }
     261       390797 :     if (getType() != nullptr && !getType()->saved) {
     262         4100 :         getType()->write(os);
     263         4100 :         getType()->saved = asAlternatives;
     264              :     }
     265              : 
     266       777328 :     const bool writeTrip = options.exists("write-trips") && options.getBool("write-trips");
     267       390797 :     const bool writeGeoTrip = writeTrip && options.getBool("write-trips.geo");
     268       390797 :     const bool writeJunctions = writeTrip && options.getBool("write-trips.junctions");
     269       390797 :     const bool writeNamedRoute = !asAlternatives && options.getBool("named-routes");
     270       777328 :     const bool writeCosts = options.exists("write-costs") && options.getBool("write-costs");
     271       781594 :     const bool writeExit = options.exists("exit-times") && options.getBool("exit-times");
     272       777328 :     const bool writeLength = options.exists("route-length") && options.getBool("route-length");
     273       777328 :     const bool writeFlow = options.exists("keep-flows") && options.getBool("keep-flows") && isPartOfFlow();
     274              : 
     275              :     std::string routeID;
     276       390797 :     if (writeNamedRoute) {
     277           29 :         ConstROEdgeVector edges = myRoute->getUsedRoute()->getNormalEdges();
     278              :         auto it = mySavedRoutes.find(edges);
     279           29 :         if (it == mySavedRoutes.end()) {
     280           22 :             routeID = "r" + toString(mySavedRoutes.size());
     281           11 :             myRoute->getUsedRoute()->writeXMLDefinition(os, this, writeCosts, false, writeExit,
     282              :                     writeLength, routeID);
     283           11 :             mySavedRoutes[edges] = routeID;
     284              :         } else {
     285           18 :             routeID = it->second;
     286              :         }
     287           29 :     }
     288       390797 :     const SumoXMLTag tag = writeFlow ? SUMO_TAG_FLOW : (writeTrip ? SUMO_TAG_TRIP : SUMO_TAG_VEHICLE);
     289              :     // write the vehicle (new style, with included routes)
     290       390797 :     if (cloneIndex == 0) {
     291       781354 :         getParameter().write(os, options, tag);
     292              :     } else {
     293          120 :         SUMOVehicleParameter p = getParameter();
     294              :         // @note id collisions may occur if scale-suffic occurs in other vehicle ids
     295          240 :         p.id += options.getString("scale-suffix") + toString(cloneIndex);
     296          120 :         p.write(os, options, tag);
     297          120 :     }
     298              :     // save the route
     299       390797 :     if (writeTrip) {
     300       158652 :         const ConstROEdgeVector edges = myRoute->getFirstRoute()->getEdgeVector();
     301              :         const ROEdge* from = nullptr;
     302              :         const ROEdge* to = nullptr;
     303        79326 :         if (edges.size() > 0) {
     304        79326 :             if (edges.front()->isTazConnector()) {
     305         7284 :                 if (edges.size() > 1) {
     306         7284 :                     from = edges[1];
     307         7284 :                     if (from->isTazConnector() && writeJunctions && edges.front()->getSuccessors().size() > 0) {
     308              :                         // routing was skipped
     309            8 :                         from = edges.front()->getSuccessors(getVClass()).front();
     310              :                     }
     311              :                 }
     312              :             } else {
     313              :                 from = edges[0];
     314              :             }
     315        79326 :             if (edges.back()->isTazConnector()) {
     316         7284 :                 if (edges.size() > 1) {
     317         7284 :                     to = edges[edges.size() - 2];
     318         7284 :                     if (to->isTazConnector() && writeJunctions && edges.back()->getPredecessors().size() > 0) {
     319              :                         // routing was skipped
     320            4 :                         to = edges.back()->getPredecessors().front();
     321              :                     }
     322              :                 }
     323              :             } else {
     324        72042 :                 to = edges[edges.size() - 1];
     325              :             }
     326              :         }
     327        79326 :         if (from != nullptr) {
     328        79326 :             if (writeGeoTrip) {
     329           16 :                 Position fromPos = from->getLanes()[0]->getShape().positionAtOffset2D(0);
     330           16 :                 if (GeoConvHelper::getFinal().usingGeoProjection()) {
     331            0 :                     os.setPrecision(gPrecisionGeo);
     332            0 :                     GeoConvHelper::getFinal().cartesian2geo(fromPos);
     333            0 :                     os.writeAttr(SUMO_ATTR_FROMLONLAT, fromPos);
     334            0 :                     os.setPrecision(gPrecision);
     335              :                 } else {
     336           16 :                     os.writeAttr(SUMO_ATTR_FROMXY, fromPos);
     337              :                 }
     338        79310 :             } else if (writeJunctions) {
     339         7244 :                 os.writeAttr(SUMO_ATTR_FROM_JUNCTION, from->getFromJunction()->getID());
     340              :             } else {
     341        72066 :                 os.writeAttr(SUMO_ATTR_FROM, from->getID());
     342              :             }
     343              :         }
     344        79326 :         if (to != nullptr) {
     345        79326 :             if (writeGeoTrip) {
     346           16 :                 Position toPos = to->getLanes()[0]->getShape().positionAtOffset2D(to->getLanes()[0]->getShape().length2D());
     347           16 :                 if (GeoConvHelper::getFinal().usingGeoProjection()) {
     348            0 :                     os.setPrecision(gPrecisionGeo);
     349            0 :                     GeoConvHelper::getFinal().cartesian2geo(toPos);
     350            0 :                     os.writeAttr(SUMO_ATTR_TOLONLAT, toPos);
     351            0 :                     os.setPrecision(gPrecision);
     352              :                 } else {
     353           16 :                     os.writeAttr(SUMO_ATTR_TOXY, toPos);
     354              :                 }
     355        79310 :             } else if (writeJunctions) {
     356         7244 :                 os.writeAttr(SUMO_ATTR_TO_JUNCTION, to->getToJunction()->getID());
     357              :             } else {
     358        72066 :                 os.writeAttr(SUMO_ATTR_TO, to->getID());
     359              :             }
     360              :         }
     361        79326 :         if (getParameter().via.size() > 0) {
     362              :             std::vector<std::string> viaOut;
     363              :             SumoXMLAttr viaAttr = (writeGeoTrip
     364         7236 :                                    ? (GeoConvHelper::getFinal().usingGeoProjection() ? SUMO_ATTR_VIALONLAT : SUMO_ATTR_VIAXY)
     365        14456 :                                    : (writeJunctions ? SUMO_ATTR_VIAJUNCTIONS : SUMO_ATTR_VIA));
     366        21672 :             for (const std::string& viaID : getParameter().via) {
     367        14436 :                 const ROEdge* viaEdge = RONet::getInstance()->getEdge(viaID);
     368        14436 :                 if (viaEdge->isTazConnector()) {
     369           20 :                     if (viaEdge->getPredecessors().size() == 0) {
     370            0 :                         continue;
     371              :                     }
     372              :                     // XXX used edge that was used in route
     373           20 :                     viaEdge = viaEdge->getPredecessors().front();
     374              :                 }
     375              :                 assert(viaEdge != nullptr);
     376        14436 :                 if (writeGeoTrip) {
     377            8 :                     Position viaPos = viaEdge->getLanes()[0]->getShape().positionAtOffset2D(viaEdge->getLanes()[0]->getShape().length2D() / 2);
     378            8 :                     if (GeoConvHelper::getFinal().usingGeoProjection()) {
     379            0 :                         GeoConvHelper::getFinal().cartesian2geo(viaPos);
     380            0 :                         viaOut.push_back(toString(viaPos, gPrecisionGeo));
     381              :                     } else {
     382           16 :                         viaOut.push_back(toString(viaPos, gPrecision));
     383              :                     }
     384        14428 :                 } else if (writeJunctions) {
     385            8 :                     viaOut.push_back(viaEdge->getToJunction()->getID());
     386              :                 } else {
     387        14420 :                     viaOut.push_back(viaEdge->getID());
     388              :                 }
     389              :             }
     390         7236 :             os.writeAttr(viaAttr, viaOut);
     391         7236 :         }
     392       390797 :     } else if (writeNamedRoute) {
     393           29 :         os.writeAttr(SUMO_ATTR_ROUTE, routeID);
     394              :     } else {
     395       311442 :         myRoute->writeXMLDefinition(os, this, asAlternatives, writeExit, writeCosts, writeLength);
     396              :     }
     397       392193 :     for (StopParVector::const_iterator stop = getParameter().stops.begin(); stop != getParameter().stops.end(); ++stop) {
     398         1396 :         stop->write(os);
     399              :     }
     400       390797 :     getParameter().writeParams(os);
     401       781594 :     os.closeTag();
     402       390797 : }
     403              : 
     404              : 
     405              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1