LCOV - code coverage report
Current view: top level - src/mesosim - MELoop.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 97.5 % 157 153
Test Date: 2026-09-20 15:45:03 Functions: 93.8 % 16 15

            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 "MELSegment.h"
      41              : #include "MEVehicle.h"
      42              : 
      43              : long long int MELoop::LeaderEvent::myEventCounter(0);
      44              : 
      45              : // ===========================================================================
      46              : // method definitions
      47              : // ===========================================================================
      48         7038 : MELoop::MELoop(const SUMOTime recheckInterval) : myFullRecheckInterval(recheckInterval), myLinkRecheckInterval(TIME2STEPS(1)) {
      49         7038 : }
      50              : 
      51         6938 : MELoop::~MELoop() {
      52       355463 :     for (std::vector<MESegment*>::const_iterator j = myEdges2FirstSegments.begin(); j != myEdges2FirstSegments.end(); ++j) {
      53      1071158 :         for (MESegment* s = *j; s != nullptr;) {
      54              :             MESegment* n = s->getNextSegment();
      55       722633 :             delete s;
      56              :             s = n;
      57              :         }
      58              :     }
      59         6938 : }
      60              : 
      61              : 
      62              : void
      63     23252575 : MELoop::simulate(SUMOTime tMax) {
      64     63311093 :     while (!myLeaderCars.empty()) {
      65     53657652 :         LeaderEvent e = myLeaderCars.top();
      66              :         assert(e.time > tMax - DELTA_T);
      67     53657652 :         if (e.time > tMax) {
      68              :             return;
      69              :         }
      70     40058518 :         myLeaderCars.pop();
      71     40064797 :         while (!myInvalidatedLeaderCars.empty() && e.time > myInvalidatedLeaderCars.top().time) {
      72         6279 :             myInvalidatedLeaderCars.pop();
      73              :         }
      74     40058518 :         if (!myInvalidatedLeaderCars.empty()) {
      75         2257 :             if (e == myInvalidatedLeaderCars.top()) {
      76         2257 :                 myInvalidatedLeaderCars.pop();
      77              :                 continue;
      78              :             }
      79              :         }
      80              :         //std::cout << SIMTIME << " checkChar " << e.veh->getID() << " on=" << e.veh->getSegment()->getID() << " t=" << e.time << " nid=" << e.veh->getNumericalID() << "\n";
      81     40056261 :         checkCar(e.veh);
      82              :         assert(myLeaderCars.empty() || myLeaderCars.top().time >= e.time);
      83              :     }
      84              : }
      85              : 
      86              : 
      87              : SUMOTime
      88     40070863 : MELoop::changeSegment(MEVehicle* veh, SUMOTime leaveTime, MESegment* const toSegment, MSMoveReminder::Notification reason, const bool ignoreLink) const {
      89     40070863 :     int qIdx = 0;
      90              :     MESegment* const onSegment = veh->getSegment();
      91              :     if (MESegment::isInvalid(toSegment)) {
      92       807893 :         if (veh->isStoppedTriggered()) {
      93         5398 :             return leaveTime + MAX2(SUMOTime(1), myLinkRecheckInterval);
      94              :         }
      95       802495 :         if (onSegment != nullptr) {
      96       798725 :             onSegment->send(veh, toSegment, qIdx, leaveTime, reason);
      97              :         } else {
      98        11310 :             WRITE_WARNINGF(TL("Vehicle '%' teleports beyond arrival edge '%', time=%."),
      99              :                            veh->getID(), veh->getEdge()->getID(), time2string(leaveTime));
     100              :         }
     101       802495 :         veh->setSegment(toSegment); // signal arrival
     102       802495 :         MSNet::getInstance()->getVehicleControl().scheduleVehicleRemoval(veh);
     103       802495 :         return leaveTime;
     104     39333125 :     } else if (!MSGlobals::gCheckRoutes && !ignoreLink && !MESegment::isInvalid(onSegment) && &onSegment->getEdge() != &toSegment->getEdge() &&
     105        30433 :                veh->getEdge()->allowedLanes(*veh->succEdge(1), veh->getVClass()) == nullptr) {
     106        27072 :         if (veh->isStopped()) {
     107            2 :             veh->processStop();
     108              :         }
     109              :         return SUMOTime_MAX;
     110              :     }
     111     39235898 :     toSegment->updateEntryBlockTime(leaveTime);
     112     39235898 :     const SUMOTime entry = toSegment->hasSpaceFor(veh, leaveTime, qIdx);
     113     39235898 :     if (entry == leaveTime && (ignoreLink || veh->mayProceed())) {
     114     25938737 :         if (onSegment != nullptr) {
     115     25938087 :             if (veh->getQueIndex() == MESegment::PARKING_QUEUE) { // parking or just aborted parking
     116         5651 :                 if (veh->isParking()) {
     117         5650 :                     veh->processStop();
     118              :                 }
     119         5651 :                 veh->getEdge()->getLanes()[0]->removeParking(veh);  // TODO for GUI only
     120              :             } else {
     121     46800166 :                 onSegment->send(veh, toSegment, qIdx, leaveTime, onSegment->getNextSegment() == nullptr ? MSMoveReminder::NOTIFICATION_JUNCTION : MSMoveReminder::NOTIFICATION_SEGMENT);
     122              :             }
     123     25938087 :             toSegment->receive(veh, qIdx, leaveTime, false, ignoreLink, &onSegment->getEdge() != &toSegment->getEdge());
     124              :         } else {
     125         1950 :             WRITE_WARNINGF(TL("Vehicle '%' ends teleporting on edge '%':%, time=%."),
     126              :                            veh->getID(), toSegment->getEdge().getID(), toSegment->getIndex(), time2string(leaveTime));
     127              :             // this is not quite correct but suffices for interrogation by
     128              :             // subsequent methods (veh->getSpeed() needs segment != 0)
     129          650 :             veh->setSegment(myEdges2FirstSegments[veh->getEdge()->getNumericalID()]);
     130              :             // clean up detectors (do not add traffic data)
     131              :             // note: updateDatector is not called if leaveTime == getLastEntryTime()
     132          650 :             veh->updateDetectors(veh->getLastEntryTime(), veh->getEventTime(), true, MSMoveReminder::NOTIFICATION_TELEPORT);
     133          650 :             toSegment->receive(veh, qIdx, leaveTime, false, true, true);
     134              :         }
     135     25938737 :         return entry;
     136              :     }
     137     13297161 :     if (entry == leaveTime && !ignoreLink) { // this is a long way of saying !veh->mayProceed() (which is a costly call)
     138     10916456 :         return entry + MAX2(SUMOTime(1), myLinkRecheckInterval);
     139              :     }
     140              :     return entry;
     141              : }
     142              : 
     143              : 
     144              : void
     145     40056261 : MELoop::checkCar(MEVehicle* veh) {
     146              :     const SUMOTime leaveTime = veh->getEventTime();
     147              :     MESegment* const onSegment = veh->getSegment();
     148     40056261 :     MESegment* const toSegment = veh->getQueIndex() == MESegment::PARKING_QUEUE ? onSegment : nextSegment(onSegment, veh);
     149     40056261 :     const bool teleporting = (onSegment == nullptr); // is the vehicle currently teleporting?
     150              :     // @note reason is only evaluated if toSegment == nullptr
     151     40056261 :     const SUMOTime nextEntry = changeSegment(veh, leaveTime, toSegment, MSMoveReminder::NOTIFICATION_ARRIVED, teleporting);
     152     40056261 :     if (nextEntry == leaveTime) {
     153              :         return;
     154              :     }
     155     13327409 :     const bool r1 = MSGlobals::gTimeToGridlock > 0 && veh->getWaitingTime() > MSGlobals::gTimeToGridlock;
     156     13327409 :     const bool r3 = MSGlobals::gTimeToTeleportDisconnected >= 0 && veh->getWaitingTime() > MSGlobals::gTimeToTeleportDisconnected;
     157     13327409 :     if (!veh->isStopped() && (r1 || r3)) {
     158         8596 :         const bool disconnected = (MSGlobals::gTimeToTeleportDisconnected >= 0
     159         1818 :                                    && veh->succEdge(1) != nullptr
     160        10414 :                                    && veh->getEdge()->allowedLanes(*veh->succEdge(1), veh->getVClass()) == nullptr);
     161         8596 :         if ((r1 && !disconnected) || (r3 && disconnected)) {
     162         6794 :             teleportVehicle(veh, toSegment, disconnected);
     163         6794 :             return;
     164              :         }
     165              :     }
     166     13320615 :     if (veh->getBlockTime() == SUMOTime_MAX && (!veh->isStopped()
     167      9919878 :                 || (!veh->isStoppedTriggered() && veh->isStoppedParking()))) {
     168              :         veh->setBlockTime(leaveTime);
     169              :     }
     170     13320615 :     if (nextEntry == SUMOTime_MAX) {
     171              :         // all usable queues on the next segment are full
     172      2153000 :         SUMOTime newEventTime = MAX3(toSegment->getEventTime() + 1, leaveTime + 1, leaveTime + myFullRecheckInterval);
     173      2153000 :         if (MSGlobals::gTimeToGridlock > 0) {
     174              :             // if teleporting is enabled, make sure we look at the vehicle when the gridlock-time is up
     175      1655835 :             const SUMOTime recheck = MSGlobals::gTimeToTeleportDisconnected >= 0 ? MIN2(MSGlobals::gTimeToGridlock, MSGlobals::gTimeToTeleportDisconnected) : MSGlobals::gTimeToGridlock;
     176      1655835 :             newEventTime = MAX2(MIN2(newEventTime, veh->getBlockTime() + recheck + 1), leaveTime + DELTA_T);
     177              :         }
     178              :         veh->setEventTime(newEventTime);
     179              :     } else {
     180              :         // receiving segment has recently received another vehicle or the junction is blocked
     181              :         veh->setEventTime(nextEntry);
     182              :     }
     183     13320615 :     addLeaderCar(veh, teleporting ? nullptr : onSegment->getLink(veh));
     184              : }
     185              : 
     186              : 
     187              : void
     188         6794 : MELoop::teleportVehicle(MEVehicle* veh, MESegment* const toSegment, bool disconnected) {
     189              :     const SUMOTime leaveTime = veh->getEventTime();
     190              :     MESegment* const onSegment = veh->getSegment();
     191         6794 :     if (MSGlobals::gRemoveGridlocked) {
     192           48 :         WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long, from edge '%':%, time=%."),
     193              :                        veh->getID(), onSegment->getEdge().getID(), onSegment->getIndex(),
     194              :                        time2string(leaveTime));
     195           16 :         MSNet::getInstance()->getVehicleControl().registerTeleportJam();
     196              :         int qIdx = 0;
     197           16 :         onSegment->send(veh, nullptr, qIdx, leaveTime, MSMoveReminder::NOTIFICATION_TELEPORT_ARRIVED);
     198           16 :         veh->setSegment(nullptr);
     199           16 :         MSNet::getInstance()->getVehicleControl().scheduleVehicleRemoval(veh);
     200           16 :         return;
     201              :     }
     202              :     const bool teleporting = (onSegment == nullptr); // is the vehicle already teleporting?
     203              :     // try to find a place on the current edge
     204         6778 :     MESegment* teleSegment = disconnected ? toSegment : toSegment->getNextSegment();
     205         9000 :     while (teleSegment != nullptr && changeSegment(veh, leaveTime, teleSegment, MSMoveReminder::NOTIFICATION_TELEPORT, true) != leaveTime) {
     206              :         // @caution the time to get to the next segment here is ignored XXX
     207              :         teleSegment = teleSegment->getNextSegment();
     208              :     }
     209         6778 :     if (teleSegment != nullptr) {
     210          382 :         if (!teleporting) {
     211              :             // we managed to teleport in a single jump
     212          746 :             const std::string reason = disconnected ? " (disconnected)" : "";
     213         1134 :             WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long%, from edge '%':% to edge '%':%, time=%."),
     214              :                            veh->getID(), reason, onSegment->getEdge().getID(), onSegment->getIndex(),
     215              :                            teleSegment->getEdge().getID(), teleSegment->getIndex(), time2string(leaveTime));
     216          378 :             MSNet::getInstance()->getVehicleControl().registerTeleportJam();
     217              :         }
     218              :     } else {
     219              :         // teleport across the current edge and try insertion later
     220         6396 :         if (!teleporting) {
     221              :             int qIdx = 0;
     222              :             // announce start of multi-step teleport, arrival will be announced in changeSegment()
     223        13350 :             WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long, from edge '%':%, time=%."),
     224              :                            veh->getID(), onSegment->getEdge().getID(), onSegment->getIndex(), time2string(leaveTime));
     225         4450 :             MSNet::getInstance()->getVehicleControl().registerTeleportJam();
     226              :             // remove from current segment
     227         4450 :             onSegment->send(veh, nullptr, qIdx, leaveTime, MSMoveReminder::NOTIFICATION_TELEPORT);
     228              :             // mark veh as teleporting
     229         4450 :             veh->setSegment(nullptr);
     230              :         } else {
     231         1946 :             veh->updateDetectors(veh->getLastEntryTime(), leaveTime, true, MSMoveReminder::NOTIFICATION_TELEPORT);
     232              :         }
     233              :         // @caution microsim uses current travel time teleport duration
     234        19188 :         const SUMOTime teleArrival = leaveTime + TIME2STEPS(veh->getEdge()->getLength() / MAX2(veh->getEdge()->getSpeedLimit(), NUMERICAL_EPS));
     235         6396 :         const bool atDest = veh->moveRoutePointer();
     236         6396 :         if (atDest) {
     237              :             // teleporting to end of route
     238         3770 :             changeSegment(veh, teleArrival, nullptr, MSMoveReminder::NOTIFICATION_TELEPORT_ARRIVED, true);
     239              :         } else {
     240              :             veh->setEventTime(teleArrival);
     241         2626 :             addLeaderCar(veh, nullptr);
     242              :             // teleporting vehicles must react to rerouters
     243         2626 :             getSegmentForEdge(*veh->getEdge())->addReminders(veh);
     244         2626 :             veh->activateReminders(MSMoveReminder::NOTIFICATION_JUNCTION);
     245              :         }
     246              :     }
     247              : }
     248              : 
     249              : 
     250              : void
     251     40066020 : MELoop::addLeaderCar(MEVehicle* veh, MSLink* link) {
     252     40066020 :     myLeaderCars.push(LeaderEvent(veh));
     253     40066020 :     veh->setApproaching(link);
     254     40066020 : }
     255              : 
     256              : 
     257              : void
     258           12 : MELoop::clearState() {
     259           12 :     myLeaderCars = LeaderEventQeue();
     260           12 :     myInvalidatedLeaderCars = LeaderEventQeue();
     261           12 : }
     262              : 
     263              : 
     264              : void
     265         8568 : MELoop::removeLeaderCar(MEVehicle* v) {
     266         8568 :     myInvalidatedLeaderCars.push(LeaderEvent(v));
     267         8568 : }
     268              : 
     269              : 
     270              : void
     271            0 : MELoop::vaporizeCar(MEVehicle* v, MSMoveReminder::Notification reason) {
     272              :     int qIdx = 0;
     273            0 :     v->getSegment()->send(v, nullptr, qIdx, MSNet::getInstance()->getCurrentTimeStep(), reason);
     274            0 :     removeLeaderCar(v);
     275            0 : }
     276              : 
     277              : 
     278              : MESegment*
     279     39861328 : MELoop::nextSegment(MESegment* s, MEVehicle* v) {
     280     39861328 :     if (s != nullptr) { // vehicle is not teleporting
     281              :         MESegment* next = s->getNextSegment();
     282     39858730 :         if (next != nullptr) {
     283              :             // ok, the street continues
     284              :             return next;
     285              :         }
     286              :     }
     287              :     // we have to check the next edge in the vehicle's route
     288     18391843 :     const MSEdge* nextEdge = v->succEdge(1);
     289     18391843 :     if (nextEdge == nullptr) {
     290              :         // end of route
     291              :         return nullptr;
     292              :     }
     293     17595948 :     if (MSGlobals::gUsingInternalLanes && s != nullptr && s->getEdge().isNormal()) {
     294      3407568 :         const MSEdge* internal = s->getEdge().getInternalFollowingEdge(nextEdge, v->getVClass());
     295      3407568 :         if (internal) {
     296              :             nextEdge = internal;
     297              :         }
     298              :     }
     299     17595948 :     return myEdges2FirstSegments[nextEdge->getNumericalID()];
     300              : }
     301              : 
     302              : 
     303              : int
     304       513538 : MELoop::numSegmentsFor(const double length, const double sLength) {
     305       513538 :     int no = (int)floor(length / sLength + 0.5);
     306       513538 :     if (no == 0) { // assure there is at least one segment
     307              :         return 1;
     308              :     } else {
     309       202495 :         return no;
     310              :     }
     311              : }
     312              : 
     313              : 
     314              : void
     315       351037 : MELoop::buildSegmentsFor(const MSEdge& e, const OptionsCont& oc) {
     316       351037 :     const MESegment::MesoEdgeType& edgeType = MSNet::getInstance()->getMesoType(e.getEdgeType());
     317              :     const double length = e.getLength();
     318       351037 :     const int numSegments = numSegmentsFor(length, edgeType.edgeLength);
     319       351037 :     const double slength = length / (double)numSegments;
     320              :     MESegment* newSegment = nullptr;
     321              :     MESegment* nextSegment = nullptr;
     322       351037 :     const bool laneQueue = oc.getBool("meso-lane-queue");
     323       351037 :     const bool lift = oc.getBool("meso-ltm");
     324       678678 :     bool multiQueue = laneQueue || (oc.getBool("meso-multi-queue") && e.getLanes().size() > 1 && e.getNumSuccessors() > 1);
     325      1078542 :     for (int s = numSegments - 1; s >= 0; s--) {
     326      1455010 :         std::string id = e.getID() + ":" + toString(s);
     327              :         newSegment = lift
     328       727505 :             ? new MELSegment(id, e, nextSegment, slength, e.getLanes()[0]->getSpeedLimit(), s, multiQueue, edgeType)
     329       727505 :             : new MESegment(id, e, nextSegment, slength, e.getLanes()[0]->getSpeedLimit(), s, multiQueue, edgeType);
     330              :         multiQueue = laneQueue;
     331              :         nextSegment = newSegment;
     332              :     }
     333       702074 :     while (e.getNumericalID() >= static_cast<int>(myEdges2FirstSegments.size())) {
     334       351037 :         myEdges2FirstSegments.push_back(0);
     335              :     }
     336       351037 :     myEdges2FirstSegments[e.getNumericalID()] = newSegment;
     337       752249 :     for (MSLane* lane : e.getLanes()) {
     338       401212 :         lane->updateMesoGUISegments();
     339              :     }
     340       351037 : }
     341              : 
     342              : 
     343              : MESegment*
     344    166770434 : MELoop::getSegmentForEdge(const MSEdge& e, double pos) {
     345    166770434 :     if (e.getNumericalID() >= (int)myEdges2FirstSegments.size()) {
     346              :         return nullptr;
     347              :     }
     348    166770410 :     MESegment* s = myEdges2FirstSegments[e.getNumericalID()];
     349    166770410 :     if (pos > 0) {
     350              :         double cpos = 0;
     351       900701 :         while (s->getNextSegment() != nullptr && cpos + s->getLength() < pos) {
     352              :             cpos += s->getLength();
     353              :             s = s->getNextSegment();
     354              :         }
     355              :     }
     356              :     return s;
     357              : }
     358              : 
     359              : 
     360              : bool
     361       241097 : MELoop::isEnteringRoundabout(const MSEdge& e) {
     362       654884 :     for (const MSEdge* succ : e.getSuccessors()) {
     363       413811 :         if (succ->isRoundabout()) {
     364              :             return true;
     365              :         }
     366              :     }
     367              :     return false;
     368              : }
     369              : 
     370              : 
     371     40074588 : MELoop::LeaderEvent::LeaderEvent(MEVehicle* v):
     372     40074588 :     time(v->getEventTime()),
     373     40074588 :     eventIndex(myEventCounter++),
     374     40074588 :     nid(v->getNumericalID()),
     375     40074588 :     veh(v) { }
     376              : 
     377              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1