LCOV - code coverage report
Current view: top level - src/mesosim - MELoop.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 97.3 % 147 143
Test Date: 2026-04-16 16:39:47 Functions: 93.3 % 15 14

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-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    MELoop.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @date    Tue, May 2005
      17              : ///
      18              : // The main mesocopic simulation loop
      19              : /****************************************************************************/
      20              : #include <config.h>
      21              : 
      22              : #include <queue>
      23              : #include <vector>
      24              : #include <map>
      25              : #include <cmath>
      26              : 
      27              : #include <microsim/MSNet.h>
      28              : #include <microsim/MSEdge.h>
      29              : #include <microsim/MSGlobals.h>
      30              : #include <microsim/MSLane.h>
      31              : #include <microsim/MSVehicle.h>
      32              : #include <microsim/MSVehicleControl.h>
      33              : #include <utils/options/OptionsCont.h>
      34              : #include <utils/common/ToString.h>
      35              : #include <utils/common/FileHelpers.h>
      36              : #include <utils/common/SUMOTime.h>
      37              : #include <utils/common/RandHelper.h>
      38              : #include "MELoop.h"
      39              : #include "MESegment.h"
      40              : #include "MEVehicle.h"
      41              : 
      42              : 
      43              : // ===========================================================================
      44              : // method definitions
      45              : // ===========================================================================
      46         6451 : MELoop::MELoop(const SUMOTime recheckInterval) : myFullRecheckInterval(recheckInterval), myLinkRecheckInterval(TIME2STEPS(1)) {
      47         6451 : }
      48              : 
      49         6357 : MELoop::~MELoop() {
      50       329454 :     for (std::vector<MESegment*>::const_iterator j = myEdges2FirstSegments.begin(); j != myEdges2FirstSegments.end(); ++j) {
      51      1003332 :         for (MESegment* s = *j; s != nullptr;) {
      52              :             MESegment* n = s->getNextSegment();
      53       680235 :             delete s;
      54              :             s = n;
      55              :         }
      56              :     }
      57         6357 : }
      58              : 
      59              : 
      60              : void
      61     15347565 : MELoop::simulate(SUMOTime tMax) {
      62     51337365 :     while (!myLeaderCars.empty()) {
      63     47721014 :         const SUMOTime time = myLeaderCars.begin()->first;
      64     47721014 :         std::vector<MEVehicle*> vehs = myLeaderCars[time];
      65              :         assert(time > tMax - DELTA_T || vehs.size() == 0);
      66     47721014 :         if (time > tMax) {
      67              :             return;
      68              :         }
      69              :         myLeaderCars.erase(time);
      70     73310577 :         for (std::vector<MEVehicle*>::const_iterator i = vehs.begin(); i != vehs.end(); ++i) {
      71     37320777 :             checkCar(*i);
      72              :             assert(myLeaderCars.empty() || myLeaderCars.begin()->first >= time);
      73              :         }
      74     47721014 :     }
      75              : }
      76              : 
      77              : 
      78              : SUMOTime
      79     37336171 : MELoop::changeSegment(MEVehicle* veh, SUMOTime leaveTime, MESegment* const toSegment, MSMoveReminder::Notification reason, const bool ignoreLink) const {
      80     37336171 :     int qIdx = 0;
      81              :     MESegment* const onSegment = veh->getSegment();
      82              :     if (MESegment::isInvalid(toSegment)) {
      83       687019 :         if (veh->isStoppedTriggered()) {
      84         2160 :             return leaveTime + MAX2(SUMOTime(1), myLinkRecheckInterval);
      85              :         }
      86       684859 :         if (onSegment != nullptr) {
      87       681847 :             onSegment->send(veh, toSegment, qIdx, leaveTime, reason);
      88              :         } else {
      89         9036 :             WRITE_WARNINGF(TL("Vehicle '%' teleports beyond arrival edge '%', time=%."),
      90              :                            veh->getID(), veh->getEdge()->getID(), time2string(leaveTime));
      91              :         }
      92       684859 :         veh->setSegment(toSegment); // signal arrival
      93       684859 :         MSNet::getInstance()->getVehicleControl().scheduleVehicleRemoval(veh);
      94       684859 :         return leaveTime;
      95     36719099 :     } else if (!MSGlobals::gCheckRoutes && !ignoreLink && !MESegment::isInvalid(onSegment) && &onSegment->getEdge() != &toSegment->getEdge() &&
      96        30411 :                veh->getEdge()->allowedLanes(*veh->succEdge(1), veh->getVClass()) == nullptr) {
      97        27072 :         if (veh->isStopped()) {
      98            2 :             veh->processStop();
      99              :         }
     100              :         return SUMOTime_MAX;
     101              :     }
     102     36622080 :     const SUMOTime entry = toSegment->hasSpaceFor(veh, leaveTime, qIdx);
     103     36622080 :     if (entry == leaveTime && (ignoreLink || veh->mayProceed())) {
     104     25323814 :         if (onSegment != nullptr) {
     105     25323264 :             if (veh->getQueIndex() == MESegment::PARKING_QUEUE) { // parking or just aborted parking
     106         5645 :                 if (veh->isParking()) {
     107         5643 :                     veh->processStop();
     108              :                 }
     109         5645 :                 veh->getEdge()->getLanes()[0]->removeParking(veh);  // TODO for GUI only
     110              :             } else {
     111     45895544 :                 onSegment->send(veh, toSegment, qIdx, leaveTime, onSegment->getNextSegment() == nullptr ? MSMoveReminder::NOTIFICATION_JUNCTION : MSMoveReminder::NOTIFICATION_SEGMENT);
     112              :             }
     113     25323264 :             toSegment->receive(veh, qIdx, leaveTime, false, ignoreLink, &onSegment->getEdge() != &toSegment->getEdge());
     114              :         } else {
     115         1650 :             WRITE_WARNINGF(TL("Vehicle '%' ends teleporting on edge '%':%, time=%."),
     116              :                            veh->getID(), toSegment->getEdge().getID(), toSegment->getIndex(), time2string(leaveTime));
     117              :             // this is not quite correct but suffices for interrogation by
     118              :             // subsequent methods (veh->getSpeed() needs segment != 0)
     119          550 :             veh->setSegment(myEdges2FirstSegments[veh->getEdge()->getNumericalID()]);
     120              :             // clean up detectors (do not add traffic data)
     121              :             // note: updateDatector is not called if leaveTime == getLastEntryTime()
     122          550 :             veh->updateDetectors(veh->getLastEntryTime(), veh->getEventTime(), true, MSMoveReminder::NOTIFICATION_TELEPORT);
     123          550 :             toSegment->receive(veh, qIdx, leaveTime, false, true, true);
     124              :         }
     125     25323814 :         return entry;
     126              :     }
     127     11298266 :     if (entry == leaveTime && !ignoreLink) { // this is a long way of saying !veh->mayProceed() (which is a costly call)
     128      9059042 :         return entry + MAX2(SUMOTime(1), myLinkRecheckInterval);
     129              :     }
     130              :     return entry;
     131              : }
     132              : 
     133              : 
     134              : void
     135     37320777 : MELoop::checkCar(MEVehicle* veh) {
     136              :     const SUMOTime leaveTime = veh->getEventTime();
     137              :     MESegment* const onSegment = veh->getSegment();
     138     37320777 :     MESegment* const toSegment = veh->getQueIndex() == MESegment::PARKING_QUEUE ? onSegment : nextSegment(onSegment, veh);
     139     37320777 :     const bool teleporting = (onSegment == nullptr); // is the vehicle currently teleporting?
     140              :     // @note reason is only evaluated if toSegment == nullptr
     141     37320777 :     const SUMOTime nextEntry = changeSegment(veh, leaveTime, toSegment, MSMoveReminder::NOTIFICATION_ARRIVED, teleporting);
     142     37320777 :     if (nextEntry == leaveTime) {
     143              :         return;
     144              :     }
     145     11323752 :     const bool r1 = MSGlobals::gTimeToGridlock > 0 && veh->getWaitingTime() > MSGlobals::gTimeToGridlock;
     146     11323752 :     const bool r3 = MSGlobals::gTimeToTeleportDisconnected >= 0 && veh->getWaitingTime() > MSGlobals::gTimeToTeleportDisconnected;
     147     11323752 :     if (!veh->isStopped() && (r1 || r3)) {
     148         7004 :         const bool disconnected = (MSGlobals::gTimeToTeleportDisconnected >= 0
     149         1818 :                                    && veh->succEdge(1) != nullptr
     150         8822 :                                    && veh->getEdge()->allowedLanes(*veh->succEdge(1), veh->getVClass()) == nullptr);
     151         7004 :         if ((r1 && !disconnected) || (r3 && disconnected)) {
     152         5202 :             teleportVehicle(veh, toSegment, disconnected);
     153         5202 :             return;
     154              :         }
     155              :     }
     156     11318550 :     if (veh->getBlockTime() == SUMOTime_MAX && (!veh->isStopped()
     157      8421581 :                 || (!veh->isStoppedTriggered() && veh->isStoppedParking()))) {
     158              :         veh->setBlockTime(leaveTime);
     159              :     }
     160     11318550 :     if (nextEntry == SUMOTime_MAX) {
     161              :         // all usable queues on the next segment are full
     162      2020025 :         SUMOTime newEventTime = MAX3(toSegment->getEventTime() + 1, leaveTime + 1, leaveTime + myFullRecheckInterval);
     163      2020025 :         if (MSGlobals::gTimeToGridlock > 0) {
     164              :             // if teleporting is enabled, make sure we look at the vehicle when the gridlock-time is up
     165      1533730 :             const SUMOTime recheck = MSGlobals::gTimeToTeleportDisconnected >= 0 ? MIN2(MSGlobals::gTimeToGridlock, MSGlobals::gTimeToTeleportDisconnected) : MSGlobals::gTimeToGridlock;
     166      1533730 :             newEventTime = MAX2(MIN2(newEventTime, veh->getBlockTime() + recheck + 1), leaveTime + DELTA_T);
     167              :         }
     168              :         veh->setEventTime(newEventTime);
     169              :     } else {
     170              :         // receiving segment has recently received another vehicle or the junction is blocked
     171              :         veh->setEventTime(nextEntry);
     172              :     }
     173     11318550 :     addLeaderCar(veh, teleporting ? nullptr : onSegment->getLink(veh));
     174              : }
     175              : 
     176              : 
     177              : void
     178         5202 : MELoop::teleportVehicle(MEVehicle* veh, MESegment* const toSegment, bool disconnected) {
     179              :     const SUMOTime leaveTime = veh->getEventTime();
     180              :     MESegment* const onSegment = veh->getSegment();
     181         5202 :     if (MSGlobals::gRemoveGridlocked) {
     182           48 :         WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long, from edge '%':%, time=%."),
     183              :                        veh->getID(), onSegment->getEdge().getID(), onSegment->getIndex(),
     184              :                        time2string(leaveTime));
     185           16 :         MSNet::getInstance()->getVehicleControl().registerTeleportJam();
     186              :         int qIdx = 0;
     187           16 :         onSegment->send(veh, nullptr, qIdx, leaveTime, MSMoveReminder::NOTIFICATION_TELEPORT_ARRIVED);
     188           16 :         veh->setSegment(nullptr);
     189           16 :         MSNet::getInstance()->getVehicleControl().scheduleVehicleRemoval(veh);
     190           16 :         return;
     191              :     }
     192              :     const bool teleporting = (onSegment == nullptr); // is the vehicle already teleporting?
     193              :     // try to find a place on the current edge
     194         5186 :     MESegment* teleSegment = disconnected ? toSegment : toSegment->getNextSegment();
     195         8932 :     while (teleSegment != nullptr && changeSegment(veh, leaveTime, teleSegment, MSMoveReminder::NOTIFICATION_TELEPORT, true) != leaveTime) {
     196              :         // @caution the time to get to the next segment here is ignored XXX
     197              :         teleSegment = teleSegment->getNextSegment();
     198              :     }
     199         5186 :     if (teleSegment != nullptr) {
     200          408 :         if (!teleporting) {
     201              :             // we managed to teleport in a single jump
     202          798 :             const std::string reason = disconnected ? " (disconnected)" : "";
     203         1212 :             WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long%, from edge '%':% to edge '%':%, time=%."),
     204              :                            veh->getID(), reason, onSegment->getEdge().getID(), onSegment->getIndex(),
     205              :                            teleSegment->getEdge().getID(), teleSegment->getIndex(), time2string(leaveTime));
     206          404 :             MSNet::getInstance()->getVehicleControl().registerTeleportJam();
     207              :         }
     208              :     } else {
     209              :         // teleport across the current edge and try insertion later
     210         4778 :         if (!teleporting) {
     211              :             int qIdx = 0;
     212              :             // announce start of multi-step teleport, arrival will be announced in changeSegment()
     213        10782 :             WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long, from edge '%':%, time=%."),
     214              :                            veh->getID(), onSegment->getEdge().getID(), onSegment->getIndex(), time2string(leaveTime));
     215         3594 :             MSNet::getInstance()->getVehicleControl().registerTeleportJam();
     216              :             // remove from current segment
     217         3594 :             onSegment->send(veh, nullptr, qIdx, leaveTime, MSMoveReminder::NOTIFICATION_TELEPORT);
     218              :             // mark veh as teleporting
     219         3594 :             veh->setSegment(nullptr);
     220              :         } else {
     221         1184 :             veh->updateDetectors(veh->getLastEntryTime(), leaveTime, true, MSMoveReminder::NOTIFICATION_TELEPORT);
     222              :         }
     223              :         // @caution microsim uses current travel time teleport duration
     224        14334 :         const SUMOTime teleArrival = leaveTime + TIME2STEPS(veh->getEdge()->getLength() / MAX2(veh->getEdge()->getSpeedLimit(), NUMERICAL_EPS));
     225         4778 :         const bool atDest = veh->moveRoutePointer();
     226         4778 :         if (atDest) {
     227              :             // teleporting to end of route
     228         3012 :             changeSegment(veh, teleArrival, nullptr, MSMoveReminder::NOTIFICATION_TELEPORT_ARRIVED, true);
     229              :         } else {
     230              :             veh->setEventTime(teleArrival);
     231         1766 :             addLeaderCar(veh, nullptr);
     232              :             // teleporting vehicles must react to rerouters
     233         1766 :             getSegmentForEdge(*veh->getEdge())->addReminders(veh);
     234         1766 :             veh->activateReminders(MSMoveReminder::NOTIFICATION_JUNCTION);
     235              :         }
     236              :     }
     237              : }
     238              : 
     239              : 
     240              : void
     241     37329433 : MELoop::addLeaderCar(MEVehicle* veh, MSLink* link) {
     242     37329433 :     myLeaderCars[veh->getEventTime()].push_back(veh);
     243     37329433 :     veh->setApproaching(link);
     244     37329433 : }
     245              : 
     246              : 
     247              : void
     248           16 : MELoop::clearState() {
     249              :     myLeaderCars.clear();
     250           16 : }
     251              : 
     252              : 
     253              : bool
     254         8578 : MELoop::removeLeaderCar(MEVehicle* v) {
     255         8578 :     const auto candIt = myLeaderCars.find(v->getEventTime());
     256         8578 :     if (candIt != myLeaderCars.end()) {
     257         2425 :         std::vector<MEVehicle*>& cands = candIt->second;
     258         2425 :         auto it = find(cands.begin(), cands.end(), v);
     259         2425 :         if (it != cands.end()) {
     260              :             cands.erase(it);
     261              :             return true;
     262              :         }
     263              :     }
     264              :     return false;
     265              : }
     266              : 
     267              : 
     268              : void
     269            0 : MELoop::vaporizeCar(MEVehicle* v, MSMoveReminder::Notification reason) {
     270              :     int qIdx = 0;
     271            0 :     v->getSegment()->send(v, nullptr, qIdx, MSNet::getInstance()->getCurrentTimeStep(), reason);
     272            0 :     removeLeaderCar(v);
     273            0 : }
     274              : 
     275              : 
     276              : MESegment*
     277     37151519 : MELoop::nextSegment(MESegment* s, MEVehicle* v) {
     278     37151519 :     if (s != nullptr) { // vehicle is not teleporting
     279              :         MESegment* next = s->getNextSegment();
     280     37149783 :         if (next != nullptr) {
     281              :             // ok, the street continues
     282              :             return next;
     283              :         }
     284              :     }
     285              :     // we have to check the next edge in the vehicle's route
     286     16036148 :     const MSEdge* nextEdge = v->succEdge(1);
     287     16036148 :     if (nextEdge == nullptr) {
     288              :         // end of route
     289              :         return nullptr;
     290              :     }
     291     15360369 :     if (MSGlobals::gUsingInternalLanes && s != nullptr && s->getEdge().isNormal()) {
     292      2880962 :         const MSEdge* internal = s->getEdge().getInternalFollowingEdge(nextEdge, v->getVClass());
     293      2880962 :         if (internal) {
     294              :             nextEdge = internal;
     295              :         }
     296              :     }
     297     15360369 :     return myEdges2FirstSegments[nextEdge->getNumericalID()];
     298              : }
     299              : 
     300              : 
     301              : int
     302       473408 : MELoop::numSegmentsFor(const double length, const double sLength) {
     303       473408 :     int no = (int)floor(length / sLength + 0.5);
     304       473408 :     if (no == 0) { // assure there is at least one segment
     305              :         return 1;
     306              :     } else {
     307       187791 :         return no;
     308              :     }
     309              : }
     310              : 
     311              : 
     312              : void
     313       325518 : MELoop::buildSegmentsFor(const MSEdge& e, const OptionsCont& oc) {
     314       325518 :     const MESegment::MesoEdgeType& edgeType = MSNet::getInstance()->getMesoType(e.getEdgeType());
     315              :     const double length = e.getLength();
     316       325518 :     const int numSegments = numSegmentsFor(length, edgeType.edgeLength);
     317       325518 :     const double slength = length / (double)numSegments;
     318              :     MESegment* newSegment = nullptr;
     319              :     MESegment* nextSegment = nullptr;
     320       325518 :     const bool laneQueue = oc.getBool("meso-lane-queue");
     321       630176 :     bool multiQueue = laneQueue || (oc.getBool("meso-multi-queue") && e.getLanes().size() > 1 && e.getNumSuccessors() > 1);
     322      1010352 :     for (int s = numSegments - 1; s >= 0; s--) {
     323      1369668 :         std::string id = e.getID() + ":" + toString(s);
     324       684834 :         newSegment = new MESegment(id, e, nextSegment, slength, e.getLanes()[0]->getSpeedLimit(), s, multiQueue, edgeType);
     325              :         multiQueue = laneQueue;
     326              :         nextSegment = newSegment;
     327              :     }
     328       651036 :     while (e.getNumericalID() >= static_cast<int>(myEdges2FirstSegments.size())) {
     329       325518 :         myEdges2FirstSegments.push_back(0);
     330              :     }
     331       325518 :     myEdges2FirstSegments[e.getNumericalID()] = newSegment;
     332       696474 :     for (MSLane* lane : e.getLanes()) {
     333       370956 :         lane->updateMesoGUISegments();
     334              :     }
     335       325518 : }
     336              : 
     337              : 
     338              : MESegment*
     339    161401550 : MELoop::getSegmentForEdge(const MSEdge& e, double pos) {
     340    161401550 :     if (e.getNumericalID() >= (int)myEdges2FirstSegments.size()) {
     341              :         return nullptr;
     342              :     }
     343    161401526 :     MESegment* s = myEdges2FirstSegments[e.getNumericalID()];
     344    161401526 :     if (pos > 0) {
     345              :         double cpos = 0;
     346       813081 :         while (s->getNextSegment() != nullptr && cpos + s->getLength() < pos) {
     347              :             cpos += s->getLength();
     348              :             s = s->getNextSegment();
     349              :         }
     350              :     }
     351              :     return s;
     352              : }
     353              : 
     354              : 
     355              : bool
     356       234800 : MELoop::isEnteringRoundabout(const MSEdge& e) {
     357       639500 :     for (const MSEdge* succ : e.getSuccessors()) {
     358       404712 :         if (succ->isRoundabout()) {
     359              :             return true;
     360              :         }
     361              :     }
     362              :     return false;
     363              : }
     364              : 
     365              : 
     366              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1