LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDevice_Routing.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 91.3 % 196 179
Test Date: 2025-12-06 15:35:27 Functions: 94.1 % 17 16

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2007-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    MSDevice_Routing.cpp
      15              : /// @author  Michael Behrisch
      16              : /// @author  Daniel Krajzewicz
      17              : /// @author  Laura Bieker
      18              : /// @author  Christoph Sommer
      19              : /// @author  Jakob Erdmann
      20              : /// @date    Tue, 04 Dec 2007
      21              : ///
      22              : // A device that performs vehicle rerouting based on current edge speeds
      23              : /****************************************************************************/
      24              : #include <config.h>
      25              : 
      26              : #include <microsim/MSNet.h>
      27              : #include <microsim/MSLane.h>
      28              : #include <microsim/MSEdge.h>
      29              : #include <microsim/MSEdgeControl.h>
      30              : #include <microsim/MSEventControl.h>
      31              : #include <microsim/MSGlobals.h>
      32              : #include <microsim/MSVehicleControl.h>
      33              : #include <utils/options/OptionsCont.h>
      34              : #include <utils/common/WrappingCommand.h>
      35              : #include <utils/common/StringUtils.h>
      36              : #include <utils/xml/SUMOSAXAttributes.h>
      37              : #include "MSRoutingEngine.h"
      38              : #include "MSDevice_Routing.h"
      39              : 
      40              : 
      41              : // ===========================================================================
      42              : // method definitions
      43              : // ===========================================================================
      44              : // ---------------------------------------------------------------------------
      45              : // static initialisation methods
      46              : // ---------------------------------------------------------------------------
      47              : void
      48        39900 : MSDevice_Routing::insertOptions(OptionsCont& oc) {
      49        79800 :     insertDefaultAssignmentOptions("rerouting", "Routing", oc);
      50              : 
      51        79800 :     oc.doRegister("device.rerouting.period", new Option_String("0", "TIME"));
      52        79800 :     oc.addSynonyme("device.rerouting.period", "device.routing.period", true);
      53        79800 :     oc.addDescription("device.rerouting.period", "Routing", TL("The period with which the vehicle shall be rerouted"));
      54              : 
      55        79800 :     oc.doRegister("device.rerouting.pre-period", new Option_String("60", "TIME"));
      56        79800 :     oc.addSynonyme("device.rerouting.pre-period", "device.routing.pre-period", true);
      57        79800 :     oc.addDescription("device.rerouting.pre-period", "Routing", TL("The rerouting period before depart"));
      58              : 
      59        39900 :     oc.doRegister("device.rerouting.adaptation-weight", new Option_Float(0));
      60        79800 :     oc.addSynonyme("device.rerouting.adaptation-weight", "device.routing.adaptation-weight", true);
      61        79800 :     oc.addDescription("device.rerouting.adaptation-weight", "Routing", TL("The weight of prior edge weights for exponential moving average"));
      62              : 
      63        39900 :     oc.doRegister("device.rerouting.adaptation-steps", new Option_Integer(180));
      64        79800 :     oc.addSynonyme("device.rerouting.adaptation-steps", "device.routing.adaptation-steps", true);
      65        79800 :     oc.addDescription("device.rerouting.adaptation-steps", "Routing", TL("The number of steps for moving average weight of prior edge weights"));
      66              : 
      67        79800 :     oc.doRegister("device.rerouting.adaptation-interval", new Option_String("1", "TIME"));
      68        79800 :     oc.addSynonyme("device.rerouting.adaptation-interval", "device.routing.adaptation-interval", true);
      69        79800 :     oc.addDescription("device.rerouting.adaptation-interval", "Routing", TL("The interval for updating the edge weights"));
      70              : 
      71        39900 :     oc.doRegister("device.rerouting.threshold.factor", new Option_Float(1));
      72        79800 :     oc.addDescription("device.rerouting.threshold.factor", "Routing", TL("Only reroute if the new route is faster than the current route by the given factor"));
      73              : 
      74        79800 :     oc.doRegister("device.rerouting.threshold.constant", new Option_String("0", "TIME"));
      75        79800 :     oc.addDescription("device.rerouting.threshold.constant", "Routing", TL("Only reroute if the new route is faster than the current route by the given TIME"));
      76              : 
      77        39900 :     oc.doRegister("device.rerouting.with-taz", new Option_Bool(false));
      78        79800 :     oc.addSynonyme("device.rerouting.with-taz", "device.routing.with-taz", true);
      79        79800 :     oc.addSynonyme("device.rerouting.with-taz", "with-taz");
      80        79800 :     oc.addDescription("device.rerouting.with-taz", "Routing", TL("Use zones (districts) as routing start- and endpoints"));
      81              : 
      82        79800 :     oc.doRegister("device.rerouting.mode", new Option_String("0"));
      83        79800 :     oc.addDescription("device.rerouting.mode", "Routing", TL("Set routing flags (8 ignores temporary blockages)"));
      84              : 
      85        39900 :     oc.doRegister("device.rerouting.init-with-loaded-weights", new Option_Bool(false));
      86        79800 :     oc.addDescription("device.rerouting.init-with-loaded-weights", "Routing", TL("Use weight files given with option --weight-files for initializing edge weights"));
      87              : 
      88        39900 :     oc.doRegister("device.rerouting.threads", new Option_Integer(0));
      89        79800 :     oc.addSynonyme("device.rerouting.threads", "routing-threads");
      90        79800 :     oc.addDescription("device.rerouting.threads", "Routing", TL("The number of parallel execution threads used for rerouting"));
      91              : 
      92        39900 :     oc.doRegister("device.rerouting.synchronize", new Option_Bool(false));
      93        79800 :     oc.addDescription("device.rerouting.synchronize", "Routing", TL("Let rerouting happen at the same time for all vehicles"));
      94              : 
      95        39900 :     oc.doRegister("device.rerouting.railsignal", new Option_Bool(false));
      96        79800 :     oc.addDescription("device.rerouting.railsignal", "Routing", TL("Allow rerouting triggered by rail signals."));
      97              : 
      98        39900 :     oc.doRegister("device.rerouting.bike-speeds", new Option_Bool(false));
      99        79800 :     oc.addDescription("device.rerouting.bike-speeds", "Routing", TL("Compute separate average speeds for bicycles"));
     100              : 
     101        39900 :     oc.doRegister("device.rerouting.output", new Option_FileName());
     102        79800 :     oc.addDescription("device.rerouting.output", "Routing", TL("Save adapting weights to FILE"));
     103        39900 : }
     104              : 
     105              : 
     106              : bool
     107        39440 : MSDevice_Routing::checkOptions(OptionsCont& oc) {
     108              :     bool ok = true;
     109        39766 :     if (!oc.isDefault("device.rerouting.adaptation-steps") && !oc.isDefault("device.rerouting.adaptation-weight")) {
     110            0 :         WRITE_ERROR(TL("Only one of the options 'device.rerouting.adaptation-steps' or 'device.rerouting.adaptation-weight' may be given."));
     111              :         ok = false;
     112              :     }
     113        78880 :     if (oc.getFloat("weights.random-factor") < 1) {
     114            0 :         WRITE_ERROR(TL("weights.random-factor cannot be less than 1"));
     115              :         ok = false;
     116              :     }
     117        78880 :     if (string2time(oc.getString("device.rerouting.adaptation-interval")) < 0) {
     118           14 :         WRITE_ERROR(TL("Negative value for device.rerouting.adaptation-interval!"));
     119              :         ok = false;
     120              :     }
     121       118320 :     if (oc.getFloat("device.rerouting.adaptation-weight") < 0.  ||
     122       118320 :             oc.getFloat("device.rerouting.adaptation-weight") > 1.) {
     123            0 :         WRITE_ERROR(TL("The value for device.rerouting.adaptation-weight must be between 0 and 1!"));
     124              :         ok = false;
     125              :     }
     126              : #ifndef HAVE_FOX
     127              :     if (oc.getInt("device.rerouting.threads") > 1) {
     128              :         WRITE_ERROR(TL("Parallel routing is only possible when compiled with Fox."));
     129              :         ok = false;
     130              :     }
     131              : #endif
     132        43823 :     if (oc.getInt("threads") > 1 && oc.getInt("device.rerouting.threads") > 1 && oc.getInt("threads") != oc.getInt("device.rerouting.threads")) {
     133           18 :         WRITE_WARNING(TL("Adapting number of routing threads to number of simulation threads."));
     134              :     }
     135        39440 :     return ok;
     136              : }
     137              : 
     138              : 
     139              : void
     140      5382025 : MSDevice_Routing::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
     141      5382025 :     const OptionsCont& oc = OptionsCont::getOptions();
     142      5382025 :     const bool equip = equippedByDefaultAssignmentOptions(oc, "rerouting", v, false);
     143      5382019 :     if (v.getParameter().wasSet(VEHPARS_FORCE_REROUTE) || equip) {
     144              :         // route computation is enabled
     145              :         // for implicitly equipped vehicles (trips, flows), option probability
     146              :         // can still be used to disable periodic rerouting after insertion for
     147              :         // parts of the fleet
     148      1407395 :         const SUMOTime period = (equip || (
     149      2813822 :                                      oc.isDefault("device.rerouting.probability") &&
     150      4388713 :                                      v.getFloatParam("device.rerouting.probability") == oc.getFloat("device.rerouting.probability"))
     151      4558075 :                                  ? v.getTimeParam("device.rerouting.period") : 0);
     152      1575859 :         const SUMOTime prePeriod = MAX2((SUMOTime)0, v.getTimeParam("device.rerouting.pre-period"));
     153      1575859 :         MSRoutingEngine::initWeightUpdate();
     154              :         // build the device
     155      3151718 :         into.push_back(new MSDevice_Routing(v, "routing_" + v.getID(), period, prePeriod));
     156              :     }
     157      5382019 : }
     158              : 
     159              : 
     160              : // ---------------------------------------------------------------------------
     161              : // MSDevice_Routing-methods
     162              : // ---------------------------------------------------------------------------
     163      1575859 : MSDevice_Routing::MSDevice_Routing(SUMOVehicle& holder, const std::string& id,
     164      1575859 :                                    SUMOTime period, SUMOTime preInsertionPeriod) :
     165              :     MSVehicleDevice(holder, id),
     166      1575859 :     myPeriod(period),
     167      1575859 :     myPreInsertionPeriod(preInsertionPeriod),
     168      1575859 :     myLastRouting(-1),
     169      1575859 :     mySkipRouting(-1),
     170      1575859 :     myRerouteCommand(nullptr),
     171      1575859 :     myRerouteRailSignal(holder.getBoolParam("device.rerouting.railsignal", true)),
     172      1575859 :     myLastLaneEntryTime(-1),
     173      1575859 :     myRerouteAfterStop(false),
     174      1575859 :     myThresholdFactor(holder.getFloatParam("device.rerouting.threshold.factor", true, 1)),
     175      1575859 :     myThresholdTime(STEPS2TIME(holder.getTimeParam("device.rerouting.threshold.constant", true, 0))),
     176      1575859 :     myActive(true) {
     177      1575859 :     if (myPreInsertionPeriod > 0 || holder.getParameter().wasSet(VEHPARS_FORCE_REROUTE)) {
     178              :         // we do always a pre insertion reroute for trips to fill the best lanes of the vehicle with somehow meaningful values (especially for deaprtLane="best")
     179      1575029 :         myRerouteCommand = new WrappingCommand<MSDevice_Routing>(this, &MSDevice_Routing::preInsertionReroute);
     180              :         // if we don't update the edge weights, we might as well reroute now and hopefully use our threads better
     181      1575029 :         const SUMOTime execTime = MSRoutingEngine::hasEdgeUpdates() ? holder.getParameter().depart : -1;
     182      1575029 :         MSNet::getInstance()->getInsertionEvents()->addEvent(myRerouteCommand, execTime);
     183              :     }
     184      1575859 : }
     185              : 
     186              : 
     187      3151704 : MSDevice_Routing::~MSDevice_Routing() {
     188              :     // make the rerouting command invalid if there is one
     189      1575852 :     if (myRerouteCommand != nullptr) {
     190              :         myRerouteCommand->deschedule();
     191              :     }
     192      3151704 : }
     193              : 
     194              : 
     195              : bool
     196      1002987 : MSDevice_Routing::notifyEnter(SUMOTrafficObject& /*veh*/, MSMoveReminder::Notification reason, const MSLane* enteredLane) {
     197      1002987 :     if (reason == MSMoveReminder::NOTIFICATION_DEPARTED) {
     198       969965 :         if (myRerouteCommand == nullptr && myPreInsertionPeriod > 0 && myHolder.getDepartDelay() > myPreInsertionPeriod) {
     199              :             // pre-insertion rerouting was disabled. Reroute once if insertion was delayed
     200              :             // this is happening in the run thread (not inbeginOfTimestepEvents) so we cannot safely use the threadPool
     201      1321074 :             myHolder.reroute(MSNet::getInstance()->getCurrentTimeStep(), "device.rerouting",
     202       880716 :                              MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass()),
     203              :                              false, MSRoutingEngine::withTaz(), false);
     204              :         }
     205              :         // build repetition trigger if routing shall be done more often
     206       969965 :         rebuildRerouteCommand(SIMSTEP + myPeriod);
     207              :     }
     208      1002987 :     if (MSGlobals::gWeightsSeparateTurns > 0) {
     209        36105 :         if (reason == MSMoveReminder::NOTIFICATION_JUNCTION) {
     210        31105 :             const SUMOTime t = SIMSTEP;
     211        31105 :             if (myLastLaneEntryTime >= 0 && enteredLane->isInternal()) {
     212              :                 // record travel time on the previous edge but store on the internal ledge
     213        11090 :                 MSRoutingEngine::addEdgeTravelTime(enteredLane->getEdge(), t - myLastLaneEntryTime);
     214              :             }
     215        31105 :             myLastLaneEntryTime = t;
     216              :         }
     217        36105 :         return true;
     218              :     } else {
     219              :         return false;
     220              :     }
     221              : }
     222              : 
     223              : 
     224              : void
     225            0 : MSDevice_Routing::notifyStopEnded() {
     226            0 :     if (myRerouteAfterStop) {
     227            0 :         reroute(SIMSTEP);
     228            0 :         myRerouteAfterStop = false;
     229              :     }
     230            0 : }
     231              : 
     232              : 
     233              : void
     234       971926 : MSDevice_Routing::rebuildRerouteCommand(SUMOTime start) {
     235       971926 :     if (myRerouteCommand != nullptr) {
     236              :         myRerouteCommand->deschedule();
     237       220316 :         myRerouteCommand = nullptr;
     238              :     }
     239       971926 :     if (myPeriod > 0) {
     240        56715 :         myRerouteCommand = new WrappingCommand<MSDevice_Routing>(this, &MSDevice_Routing::wrappedRerouteCommandExecute);
     241       113430 :         if (OptionsCont::getOptions().getBool("device.rerouting.synchronize")) {
     242         1584 :             start -= start % myPeriod;
     243              :         }
     244              :         // ensure stable sorting of events (for repeatable routing with randomness)
     245        56715 :         myRerouteCommand->priority = (int)myHolder.getNumericalID();
     246        56715 :         MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(myRerouteCommand, start);
     247              :     }
     248       971926 : }
     249              : 
     250              : 
     251              : SUMOTime
     252     19646434 : MSDevice_Routing::preInsertionReroute(const SUMOTime currentTime) {
     253     19646434 :     if (mySkipRouting == currentTime) {
     254     17474427 :         return DELTA_T;
     255              :     }
     256      2172007 :     if (myPreInsertionPeriod == 0) {
     257              :         // the event will deschedule and destroy itself so it does not need to be stored
     258          496 :         myRerouteCommand = nullptr;
     259              :     }
     260      2172007 :     const MSEdge* source = *myHolder.getRoute().begin();
     261      2172007 :     const MSEdge* dest = myHolder.getRoute().getLastEdge();
     262      2172007 :     if (source->isTazConnector() && dest->isTazConnector()) {
     263       583669 :         ConstMSRoutePtr cached = MSRoutingEngine::getCachedRoute(std::make_pair(source, dest));
     264       583669 :         if (cached != nullptr && cached->size() > 2) {
     265            0 :             myHolder.replaceRoute(cached, "device.rerouting", true);
     266            0 :             return myPreInsertionPeriod;
     267              :         }
     268              :     }
     269              :     try {
     270              :         std::string msg;
     271      2172007 :         if (myHolder.hasValidRouteStart(msg)) {
     272      2171986 :             reroute(currentTime, true);
     273              :         }
     274          116 :     } catch (ProcessError&) {
     275          116 :         myRerouteCommand = nullptr;
     276          116 :         throw;
     277          116 :     }
     278              :     // avoid repeated pre-insertion rerouting when the departure edge is fix and
     279              :     // the departure lane does not depend on the route
     280      2171891 :     if (myPreInsertionPeriod > 0 && !source->isTazConnector() && myHolder.getParameter().departLaneProcedure != DepartLaneDefinition::BEST_FREE) {
     281      1088384 :         myRerouteCommand = nullptr;
     282      1088384 :         return 0;
     283              :     }
     284      1083507 :     return myPreInsertionPeriod;
     285              : }
     286              : 
     287              : 
     288              : SUMOTime
     289       174829 : MSDevice_Routing::wrappedRerouteCommandExecute(SUMOTime currentTime) {
     290       174829 :     if (myHolder.isStopped()) {
     291        19485 :         myRerouteAfterStop = true;
     292              :     } else {
     293       155344 :         reroute(currentTime);
     294              :     }
     295       174829 :     return myPeriod;
     296              : }
     297              : 
     298              : 
     299              : void
     300      2327330 : MSDevice_Routing::reroute(const SUMOTime currentTime, const bool onInit) {
     301      2327330 :     MSRoutingEngine::initEdgeWeights(myHolder.getVClass());
     302              :     //check whether the weights did change since the last reroute
     303      2327330 :     if (myLastRouting >= MSRoutingEngine::getLastAdaptation() || !myActive) {
     304              :         return;
     305              :     }
     306      2325697 :     myLastRouting = currentTime;
     307      4651394 :     MSRoutingEngine::reroute(myHolder, currentTime, "device.rerouting", onInit);
     308              : }
     309              : 
     310              : 
     311              : bool
     312       593330 : MSDevice_Routing::sufficientSaving(double oldCost, double newCost) {
     313       593330 :     if (newCost == 0) {
     314              :         return true;
     315              :     }
     316       593116 :     return (oldCost / newCost > myThresholdFactor) && (oldCost - newCost > myThresholdTime);
     317              : }
     318              : 
     319              : 
     320              : std::string
     321           36 : MSDevice_Routing::getParameter(const std::string& key) const {
     322           72 :     if (StringUtils::startsWith(key, "edge:")) {
     323           18 :         const std::string edgeID = key.substr(5);
     324           18 :         const MSEdge* edge = MSEdge::dictionary(edgeID);
     325           18 :         if (edge == nullptr) {
     326            0 :             throw InvalidArgument("Edge '" + edgeID + "' is invalid for parameter retrieval of '" + deviceName() + "'");
     327              :         }
     328           18 :         return toString(MSRoutingEngine::getEffort(edge, &myHolder, 0));
     329           18 :     } else if (key == "period") {
     330           18 :         return time2string(myPeriod);
     331              :     }
     332            0 :     throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
     333              : }
     334              : 
     335              : 
     336              : void
     337           33 : MSDevice_Routing::setParameter(const std::string& key, const std::string& value) {
     338              :     double doubleValue;
     339              :     try {
     340           33 :         doubleValue = StringUtils::toDouble(value);
     341            0 :     } catch (NumberFormatException&) {
     342            0 :         throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'");
     343            0 :     }
     344           66 :     if (StringUtils::startsWith(key, "edge:")) {
     345            9 :         const std::string edgeID = key.substr(5);
     346            9 :         const MSEdge* edge = MSEdge::dictionary(edgeID);
     347            9 :         if (edge == nullptr) {
     348            0 :             throw InvalidArgument("Edge '" + edgeID + "' is invalid for parameter setting of '" + deviceName() + "'");
     349              :         }
     350            9 :         MSRoutingEngine::setEdgeTravelTime(edge, doubleValue);
     351           24 :     } else if (key == "period") {
     352           24 :         myPeriod = TIME2STEPS(doubleValue);
     353              :         // re-schedule routing command
     354           24 :         rebuildRerouteCommand(SIMSTEP + myPeriod);
     355              :     } else {
     356            0 :         throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
     357              :     }
     358           33 : }
     359              : 
     360              : 
     361              : void
     362         3236 : MSDevice_Routing::saveState(OutputDevice& out) const {
     363         3236 :     out.openTag(SUMO_TAG_DEVICE);
     364         3236 :     out.writeAttr(SUMO_ATTR_ID, getID());
     365              :     std::vector<std::string> internals;
     366         3236 :     internals.push_back(toString(myPeriod));
     367         3236 :     internals.push_back(toString(myLastRouting));
     368         3236 :     out.writeAttr(SUMO_ATTR_STATE, toString(internals));
     369         3236 :     out.closeTag();
     370         3236 : }
     371              : 
     372              : 
     373              : void
     374         4814 : MSDevice_Routing::loadState(const SUMOSAXAttributes& attrs) {
     375         4814 :     std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
     376         4814 :     bis >> myPeriod;
     377         4814 :     bis >> myLastRouting;
     378         4814 :     if (myHolder.hasDeparted()) {
     379         1937 :         SUMOTime offset = myPeriod;
     380         1937 :         if (myPeriod > 0) {
     381          805 :             offset = ((SIMSTEP - myHolder.getDeparture()) % myPeriod);
     382          805 :             if (offset != 0) {
     383          783 :                 offset = myPeriod - offset;
     384              :             }
     385              :         }
     386         1937 :         rebuildRerouteCommand(SIMSTEP + offset);
     387              :     }
     388         4814 : }
     389              : 
     390              : 
     391              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1