LCOV - code coverage report
Current view: top level - src/mesosim - MESegment.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 99.2 % 384 381
Test Date: 2025-11-13 15:38:19 Functions: 97.5 % 40 39

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-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    MESegment.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @date    Tue, May 2005
      17              : ///
      18              : // A single mesoscopic segment (cell)
      19              : /****************************************************************************/
      20              : #include <config.h>
      21              : 
      22              : #include <algorithm>
      23              : #include <limits>
      24              : #include <utils/common/StdDefs.h>
      25              : #include <microsim/MSGlobals.h>
      26              : #include <microsim/MSEdge.h>
      27              : #include <microsim/MSJunction.h>
      28              : #include <microsim/MSNet.h>
      29              : #include <microsim/MSLane.h>
      30              : #include <microsim/MSLink.h>
      31              : #include <microsim/MSMoveReminder.h>
      32              : #include <microsim/traffic_lights/MSTrafficLightLogic.h>
      33              : #include <microsim/output/MSXMLRawOut.h>
      34              : #include <microsim/output/MSDetectorFileOutput.h>
      35              : #include <microsim/MSVehicleControl.h>
      36              : #include <microsim/devices/MSDevice.h>
      37              : #include <utils/common/FileHelpers.h>
      38              : #include <utils/common/MsgHandler.h>
      39              : #include <utils/iodevices/OutputDevice.h>
      40              : #include <utils/common/RandHelper.h>
      41              : #include "MEVehicle.h"
      42              : #include "MELoop.h"
      43              : #include "MESegment.h"
      44              : 
      45              : #define DEFAULT_VEH_LENGTH_WITH_GAP (SUMOVTypeParameter::getDefault().length + SUMOVTypeParameter::getDefault().minGap)
      46              : // avoid division by zero when driving very slowly
      47              : #define MESO_MIN_SPEED (0.05)
      48              : 
      49              : //#define DEBUG_OPENED
      50              : //#define DEBUG_JAMTHRESHOLD
      51              : //#define DEBUG_COND (getID() == "blocker")
      52              : //#define DEBUG_COND (true)
      53              : #define DEBUG_COND (myEdge.isSelected())
      54              : #define DEBUG_COND2(obj) ((obj != 0 && (obj)->isSelected()))
      55              : 
      56              : 
      57              : // ===========================================================================
      58              : // static member definition
      59              : // ===========================================================================
      60              : MSEdge MESegment::myDummyParent("MESegmentDummyParent", -1, SumoXMLEdgeFunc::UNKNOWN, "", "", "", -1, 0);
      61              : MESegment MESegment::myVaporizationTarget("vaporizationTarget");
      62              : const double MESegment::DO_NOT_PATCH_JAM_THRESHOLD(std::numeric_limits<double>::max());
      63              : const std::string MESegment::OVERRIDE_TLS_PENALTIES("meso.tls.control");
      64              : 
      65              : 
      66              : // ===========================================================================
      67              : // MESegment::Queue method definitions
      68              : // ===========================================================================
      69              : MEVehicle*
      70     24014581 : MESegment::Queue::remove(MEVehicle* v) {
      71     24014581 :     myOccupancy -= v->getVehicleType().getLengthWithGap();
      72              :     assert(std::find(myVehicles.begin(), myVehicles.end(), v) != myVehicles.end());
      73     24014581 :     if (v == myVehicles.back()) {
      74              :         myVehicles.pop_back();
      75     24008302 :         if (myVehicles.empty()) {
      76      6419746 :             myOccupancy = 0.;
      77              :         } else {
      78     17588556 :             return myVehicles.back();
      79              :         }
      80              :     } else {
      81         6279 :         myVehicles.erase(std::find(myVehicles.begin(), myVehicles.end(), v));
      82              :     }
      83              :     return nullptr;
      84              : }
      85              : 
      86              : void
      87       124150 : MESegment::Queue::addDetector(MSMoveReminder* data) {
      88       124150 :     myDetectorData.push_back(data);
      89       129870 :     for (MEVehicle* const v : myVehicles) {
      90         5720 :         v->addReminder(data);
      91              :     }
      92       124150 : }
      93              : 
      94              : void
      95     24051749 : MESegment::Queue::addReminders(MEVehicle* veh) const {
      96     33470861 :     for (MSMoveReminder* rem : myDetectorData) {
      97      9419112 :         veh->addReminder(rem);
      98              :     }
      99     24051749 : }
     100              : 
     101              : // ===========================================================================
     102              : // MESegment method definitions
     103              : // ===========================================================================
     104       629431 : MESegment::MESegment(const std::string& id,
     105              :                      const MSEdge& parent, MESegment* next,
     106              :                      const double length, const double speed,
     107              :                      const int idx,
     108              :                      const bool multiQueue,
     109       629431 :                      const MesoEdgeType& edgeType):
     110       629431 :     Named(id), myEdge(parent), myNextSegment(next),
     111       629431 :     myLength(length), myIndex(idx),
     112       629431 :     myTau_length(TIME2STEPS(1) / MAX2(MESO_MIN_SPEED, speed)),
     113       629431 :     myNumVehicles(0),
     114       629431 :     myLastHeadway(TIME2STEPS(-1)),
     115       629431 :     myMeanSpeed(speed),
     116      1258535 :     myLastMeanSpeedUpdate(SUMOTime_MIN) {
     117              : 
     118              :     const std::vector<MSLane*>& lanes = parent.getLanes();
     119              :     int usableLanes = 0;
     120      1362429 :     for (MSLane* const l : lanes) {
     121       732998 :         const SVCPermissions allow = MSEdge::getMesoPermissions(l->getPermissions());
     122       732998 :         if (multiQueue) {
     123        73162 :             myQueues.push_back(Queue(allow));
     124              :         }
     125       732998 :         if (allow != 0) {
     126       701460 :             usableLanes++;
     127              :         }
     128              :     }
     129       629431 :     if (usableLanes == 0) {
     130              :         // cars won't drive here. Give sensible tau values capacity for the ignored classes
     131              :         usableLanes = 1;
     132              :     }
     133       629431 :     if (multiQueue) {
     134        16439 :         if (next == nullptr) {
     135        69151 :             for (const MSEdge* const edge : parent.getSuccessors()) {
     136        53057 :                 const std::vector<MSLane*>* const allowed = parent.allowedLanes(*edge);
     137              :                 assert(allowed != nullptr);
     138              :                 assert(allowed->size() > 0);
     139       113162 :                 for (MSLane* const l : *allowed) {
     140        60105 :                     std::vector<MSLane*>::const_iterator it = std::find(lanes.begin(), lanes.end(), l);
     141        60105 :                     myFollowerMap[edge] |= (1 << distance(lanes.begin(), it));
     142              :                 }
     143              :             }
     144              :         }
     145        16439 :         myQueueCapacity = length;
     146              :     } else {
     147      1225984 :         myQueues.push_back(Queue(parent.getPermissions()));
     148              :     }
     149              : 
     150       629431 :     initSegment(edgeType, parent, length * usableLanes);
     151       629431 : }
     152              : 
     153              : void
     154       630509 : MESegment::initSegment(const MesoEdgeType& edgeType, const MSEdge& parent, const double capacity) {
     155              : 
     156       630509 :     myCapacity = capacity;
     157       630509 :     if (myQueues.size() == 1) {
     158       614421 :         const double laneScale = capacity / myLength;
     159       614421 :         myQueueCapacity = capacity;
     160       614421 :         myTau_length = TIME2STEPS(1) / MAX2(MESO_MIN_SPEED, myMeanSpeed) / laneScale;
     161              :         // Eissfeldt p. 90 and 151 ff.
     162       614421 :         myTau_ff = (SUMOTime)((double)edgeType.tauff / laneScale);
     163       614421 :         myTau_fj = (SUMOTime)((double)edgeType.taufj / laneScale);
     164       614421 :         myTau_jf = (SUMOTime)((double)edgeType.taujf / laneScale);
     165       614421 :         myTau_jj = (SUMOTime)((double)edgeType.taujj / laneScale);
     166              :     } else {
     167        16088 :         myTau_ff = edgeType.tauff;
     168        16088 :         myTau_fj = edgeType.taufj;
     169        16088 :         myTau_jf = edgeType.taujf;
     170        16088 :         myTau_jj = edgeType.taujj;
     171              :     }
     172              : 
     173       630509 :     myJunctionControl = myNextSegment == nullptr && (edgeType.junctionControl || MELoop::isEnteringRoundabout(parent));
     174       629705 :     myTLSPenalty = ((edgeType.tlsPenalty > 0 || edgeType.tlsFlowPenalty > 0) &&
     175              :                     // only apply to the last segment of a tls-controlled edge
     176         1164 :                     myNextSegment == nullptr && (
     177          310 :                         parent.getToJunction()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT ||
     178          310 :                         parent.getToJunction()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION ||
     179              :                         parent.getToJunction()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED)
     180       630887 :                     && !tlsPenaltyOverride());
     181              : 
     182              :     // only apply to the last segment of an uncontrolled edge that has at least 1 minor link
     183      1261338 :     myCheckMinorPenalty = (edgeType.minorPenalty > 0 &&
     184          320 :                            myNextSegment == nullptr &&
     185              :                            parent.getToJunction()->getType() != SumoXMLNodeType::TRAFFIC_LIGHT &&
     186              :                            parent.getToJunction()->getType() != SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION &&
     187       630687 :                            parent.getToJunction()->getType() != SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED &&
     188          178 :                            parent.hasMinorLink());
     189       630509 :     myMinorPenalty = edgeType.minorPenalty;
     190       630509 :     myOvertaking = edgeType.overtaking && myCapacity > myLength;
     191              : 
     192              :     //std::cout << getID() << " myMinorPenalty=" << myMinorPenalty << " myTLSPenalty=" << myTLSPenalty << " myJunctionControl=" << myJunctionControl << " myOvertaking=" << myOvertaking << "\n";
     193              : 
     194       630509 :     recomputeJamThreshold(edgeType.jamThreshold);
     195       630509 : }
     196              : 
     197        39746 : MESegment::MESegment(const std::string& id):
     198              :     Named(id),
     199        39746 :     myEdge(myDummyParent), // arbitrary edge needed to supply the needed reference
     200        39746 :     myNextSegment(nullptr), myLength(0), myIndex(0),
     201        39746 :     myTau_ff(0), myTau_fj(0), myTau_jf(0), myTau_jj(0),
     202        39746 :     myTLSPenalty(false),
     203        39746 :     myCheckMinorPenalty(false),
     204        39746 :     myMinorPenalty(0),
     205        39746 :     myJunctionControl(false),
     206        39746 :     myOvertaking(false),
     207        39746 :     myTau_length(1) {
     208        39746 : }
     209              : 
     210              : 
     211              : void
     212         1807 : MESegment::updatePermissions() {
     213         1807 :     if (myQueues.size() > 1) {
     214           64 :         for (MSLane* lane : myEdge.getLanes()) {
     215           48 :             myQueues[lane->getIndex()].setPermissions(lane->getPermissions());
     216              :         }
     217              :     } else {
     218         1791 :         myQueues.back().setPermissions(myEdge.getPermissions());
     219              :     }
     220         1807 : }
     221              : 
     222              : 
     223              : void
     224       631500 : MESegment::recomputeJamThreshold(double jamThresh) {
     225       631500 :     if (jamThresh == DO_NOT_PATCH_JAM_THRESHOLD) {
     226              :         return;
     227              :     }
     228       631500 :     if (jamThresh < 0) {
     229              :         // compute based on speed
     230       631496 :         myJamThreshold = jamThresholdForSpeed(myEdge.getSpeedLimit(), jamThresh);
     231              :     } else {
     232              :         // compute based on specified percentage
     233            4 :         myJamThreshold = jamThresh * myCapacity;
     234              :     }
     235              : }
     236              : 
     237              : 
     238              : double
     239      4083664 : MESegment::jamThresholdForSpeed(double speed, double jamThresh) const {
     240              :     // vehicles driving freely at maximum speed should not jam
     241              :     // we compute how many vehicles could possible enter the segment until the first vehicle leaves
     242              :     // and multiply by the space these vehicles would occupy
     243              :     // the jamThresh parameter is scale the resulting value
     244      4083664 :     if (speed == 0) {
     245              :         return std::numeric_limits<double>::max();  // never jam. Irrelevant at speed 0 anyway
     246              :     }
     247              : #ifdef DEBUG_JAMTHRESHOLD
     248              :     if (true || DEBUG_COND) {
     249              :         std::cout << "jamThresholdForSpeed seg=" << getID() << " speed=" << speed << " jamThresh=" << jamThresh << " ffVehs=" << std::ceil(myLength / (-jamThresh * speed * STEPS2TIME(tauWithVehLength(myTau_ff, DEFAULT_VEH_LENGTH_WITH_GAP)))) << " thresh=" << std::ceil(myLength / (-jamThresh * speed * STEPS2TIME(tauWithVehLength(myTau_ff, DEFAULT_VEH_LENGTH_WITH_GAP)))) * DEFAULT_VEH_LENGTH_WITH_GAP
     250              :                   << "\n";
     251              :     }
     252              : #endif
     253      4083443 :     return std::ceil(myLength / (-jamThresh * speed * STEPS2TIME(tauWithVehLength(myTau_ff, DEFAULT_VEH_LENGTH_WITH_GAP, 1.)))) * DEFAULT_VEH_LENGTH_WITH_GAP;
     254              : }
     255              : 
     256              : 
     257              : void
     258       122524 : MESegment::addDetector(MSMoveReminder* data, int queueIndex) {
     259       122524 :     if (queueIndex == -1) {
     260       241934 :         for (Queue& q : myQueues) {
     261       121780 :             q.addDetector(data);
     262              :         }
     263              :     } else {
     264              :         assert(queueIndex < (int)myQueues.size());
     265         2370 :         myQueues[queueIndex].addDetector(data);
     266              :     }
     267       122524 : }
     268              : 
     269              : 
     270              : /*
     271              : void
     272              : MESegment::removeDetector(MSMoveReminder* data) {
     273              :     std::vector<MSMoveReminder*>::iterator it = std::find(myDetectorData.begin(), myDetectorData.end(), data);
     274              :     if (it != myDetectorData.end()) {
     275              :         myDetectorData.erase(it);
     276              :     }
     277              :     for (const Queue& q : myQueues) {
     278              :         for (MEVehicle* const v : q.getVehicles()) {
     279              :             v->removeReminder(data);
     280              :         }
     281              :     }
     282              : }
     283              : */
     284              : 
     285              : 
     286              : void
     287      6405984 : MESegment::prepareDetectorForWriting(MSMoveReminder& data, int queueIndex) {
     288      6405984 :     const SUMOTime currentTime = MSNet::getInstance()->getCurrentTimeStep();
     289      6405984 :     if (queueIndex == -1) {
     290     12835116 :         for (const Queue& q : myQueues) {
     291              :             SUMOTime earliestExitTime = currentTime;
     292      7729602 :             for (std::vector<MEVehicle*>::const_reverse_iterator i = q.getVehicles().rbegin(); i != q.getVehicles().rend(); ++i) {
     293      1299870 :                 const SUMOTime exitTime = MAX2(earliestExitTime, (*i)->getEventTime());
     294      1299870 :                 (*i)->updateDetectorForWriting(&data, currentTime, exitTime);
     295      1299870 :                 earliestExitTime = exitTime + tauWithVehLength(myTau_ff, (*i)->getVehicleType().getLengthWithGap(), (*i)->getVehicleType().getCarFollowModel().getHeadwayTime());
     296              :             }
     297              :         }
     298              :     } else {
     299              :         SUMOTime earliestExitTime = currentTime;
     300          612 :         for (std::vector<MEVehicle*>::const_reverse_iterator i = myQueues[queueIndex].getVehicles().rbegin(); i != myQueues[queueIndex].getVehicles().rend(); ++i) {
     301           12 :             const SUMOTime exitTime = MAX2(earliestExitTime, (*i)->getEventTime());
     302           12 :             (*i)->updateDetectorForWriting(&data, currentTime, exitTime);
     303           12 :             earliestExitTime = exitTime + tauWithVehLength(myTau_ff, (*i)->getVehicleType().getLengthWithGap(), (*i)->getVehicleType().getCarFollowModel().getHeadwayTime());
     304              :         }
     305              :     }
     306      6405984 : }
     307              : 
     308              : 
     309              : SUMOTime
     310     57375795 : MESegment::hasSpaceFor(const MEVehicle* const veh, const SUMOTime entryTime, int& qIdx, const bool init) const {
     311              :     SUMOTime earliestEntry = SUMOTime_MAX;
     312     57375795 :     qIdx = 0;
     313     57375795 :     if (myNumVehicles == 0 && myQueues.size() == 1) {
     314              :         // we have always space for at least one vehicle
     315     10578875 :         if (myQueues.front().allows(veh->getVClass())) {
     316              :             return entryTime;
     317              :         }  else {
     318              :             return earliestEntry;
     319              :         }
     320              :     }
     321     46796920 :     const SUMOVehicleClass svc = veh->getVClass();
     322              :     int minSize = std::numeric_limits<int>::max();
     323     47311403 :     const MSEdge* const succ = myNextSegment == nullptr ? veh->succEdge(veh->getEdge() == &myEdge ? 1 : 2) : nullptr;
     324     95395960 :     for (int i = 0; i < (int)myQueues.size(); i++) {
     325     48599040 :         const Queue& q = myQueues[i];
     326     48599040 :         const double newOccupancy = q.size() == 0 ? 0. : q.getOccupancy() + veh->getVehicleType().getLengthWithGap();
     327     48599040 :         if (newOccupancy <= myQueueCapacity) { // we must ensure that occupancy remains below capacity
     328     28342829 :             if (succ == nullptr || myFollowerMap.count(succ) == 0 || ((myFollowerMap.find(succ)->second & (1 << i)) != 0)) {
     329     25807589 :                 if (q.allows(svc) && q.size() < minSize) {
     330     25262207 :                     if (init) {
     331              :                         // regular insertions and initial insertions must respect different constraints:
     332              :                         // - regular insertions must respect entryBlockTime
     333              :                         // - initial insertions should not cause additional jamming
     334              :                         // - inserted vehicle should be able to continue at the current speed
     335      7337464 :                         if (veh->getInsertionChecks() == (int)InsertionCheck::NONE) {
     336           44 :                             qIdx = i;
     337              :                             minSize = q.size();
     338      7337420 :                         } else if (q.getOccupancy() <= myJamThreshold && !hasBlockedLeader() && !myTLSPenalty) {
     339      3885252 :                             if (newOccupancy <= myJamThreshold) {
     340      1397420 :                                 qIdx = i;
     341              :                                 minSize = q.size();
     342              :                             }
     343              :                         } else {
     344      3452168 :                             if (newOccupancy <= jamThresholdForSpeed(getMeanSpeed(false), -1)) {
     345      2693033 :                                 qIdx = i;
     346              :                                 minSize = q.size();
     347              :                             }
     348              :                         }
     349     17924743 :                     } else if (entryTime >= q.getEntryBlockTime()) {
     350     17620065 :                         qIdx = i;
     351              :                         minSize = q.size();
     352              :                     } else {
     353              :                         earliestEntry = MIN2(earliestEntry, q.getEntryBlockTime());
     354              :                     }
     355              :                 }
     356              :             }
     357              :         }
     358              :     }
     359     46796920 :     if (minSize == std::numeric_limits<int>::max()) {
     360              :         return earliestEntry;
     361              :     }
     362              :     return entryTime;
     363              : }
     364              : 
     365              : 
     366              : bool
     367      1984632 : MESegment::initialise(MEVehicle* veh, SUMOTime time) {
     368      1984632 :     int qIdx = 0;
     369      1984632 :     if (hasSpaceFor(veh, time, qIdx, true) == time) {
     370       692544 :         receive(veh, qIdx, time, true);
     371              :         // we can check only after insertion because insertion may change the route via devices
     372              :         std::string msg;
     373      1384376 :         if (MSGlobals::gCheckRoutes && !veh->hasValidRoute(msg)) {
     374            6 :             throw ProcessError(TLF("Vehicle '%' has no valid route. %", veh->getID(), msg));
     375              :         }
     376              :         return true;
     377              :     }
     378              :     return false;
     379              : }
     380              : 
     381              : 
     382              : double
     383     27437681 : MESegment::getMeanSpeed(bool useCached) const {
     384     27437681 :     const SUMOTime currentTime = MSNet::getInstance()->getCurrentTimeStep();
     385     27437681 :     if (currentTime != myLastMeanSpeedUpdate || !useCached) {
     386     27342743 :         myLastMeanSpeedUpdate = currentTime;
     387              :         double v = 0;
     388              :         int count = 0;
     389     55220327 :         for (const Queue& q : myQueues) {
     390     27877584 :             const SUMOTime tau = q.getOccupancy() < myJamThreshold ? myTau_ff : myTau_jf;
     391     27877584 :             SUMOTime earliestExitTime = currentTime;
     392     27877584 :             count += q.size();
     393    134097034 :             for (std::vector<MEVehicle*>::const_reverse_iterator veh = q.getVehicles().rbegin(); veh != q.getVehicles().rend(); ++veh) {
     394    106219450 :                 v += (*veh)->getConservativeSpeed(earliestExitTime); // earliestExitTime is updated!
     395    106219450 :                 earliestExitTime += tauWithVehLength(tau, (*veh)->getVehicleType().getLengthWithGap(), (*veh)->getVehicleType().getCarFollowModel().getHeadwayTime());
     396              :             }
     397              :         }
     398     27342743 :         if (count == 0) {
     399           30 :             myMeanSpeed = myEdge.getSpeedLimit();
     400              :         } else {
     401     27342713 :             myMeanSpeed = v / (double) count;
     402              :         }
     403              :     }
     404     27437681 :     return myMeanSpeed;
     405              : }
     406              : 
     407              : 
     408              : void
     409         2542 : MESegment::resetCachedSpeeds() {
     410         2542 :     myLastMeanSpeedUpdate = SUMOTime_MIN;
     411         2542 : }
     412              : 
     413              : void
     414       206716 : MESegment::writeVehicles(OutputDevice& of) const {
     415       413850 :     for (const Queue& q : myQueues) {
     416       221396 :         for (const MEVehicle* const veh : q.getVehicles()) {
     417        14262 :             MSXMLRawOut::writeVehicle(of, *veh);
     418              :         }
     419              :     }
     420       206716 : }
     421              : 
     422              : 
     423              : MEVehicle*
     424     24014581 : MESegment::removeCar(MEVehicle* v, SUMOTime leaveTime, const MSMoveReminder::Notification reason) {
     425     24014581 :     Queue& q = myQueues[v->getQueIndex()];
     426              :     // One could be tempted to do  v->setSegment(next); here but position on lane will be invalid if next == 0
     427     24014581 :     v->updateDetectors(leaveTime, v->getEventTime(), true, reason);
     428     24014581 :     myNumVehicles--;
     429     24014581 :     myEdge.lock();
     430     24014581 :     MEVehicle* nextLeader = q.remove(v);
     431     24014581 :     myEdge.unlock();
     432     24014581 :     return nextLeader;
     433              : }
     434              : 
     435              : 
     436              : SUMOTime
     437        13548 : MESegment::getNextInsertionTime(SUMOTime earliestEntry) const {
     438              :     // since we do not know which queue will be used we give a conservative estimate
     439              :     SUMOTime earliestLeave = earliestEntry;
     440              :     SUMOTime latestEntry = -1;
     441        27354 :     for (const Queue& q : myQueues) {
     442              :         earliestLeave = MAX2(earliestLeave, q.getBlockTime());
     443              :         latestEntry = MAX2(latestEntry, q.getEntryBlockTime());
     444              :     }
     445        13548 :     if (myEdge.getSpeedLimit() == 0) {
     446           12 :         return MAX2(earliestEntry, latestEntry);    // FIXME: This line is just an adhoc-fix to avoid division by zero (Leo)
     447              :     } else {
     448        13536 :         return MAX3(earliestEntry, earliestLeave - TIME2STEPS(myLength / myEdge.getSpeedLimit()), latestEntry);
     449              :     }
     450              : }
     451              : 
     452              : 
     453              : MSLink*
     454    105207658 : MESegment::getLink(const MEVehicle* veh, bool penalty) const {
     455    105207658 :     if (myJunctionControl || penalty) {
     456     14041389 :         const MSEdge* const nextEdge = veh->succEdge(1);
     457     14041389 :         if (nextEdge == nullptr || veh->getQueIndex() == PARKING_QUEUE) {
     458              :             return nullptr;
     459              :         }
     460              :         // try to find any link leading to our next edge, start with the lane pointed to by the que index
     461     13094409 :         const MSLane* const bestLane = myEdge.getLanes()[veh->getQueIndex()];
     462     15978574 :         for (MSLink* const link : bestLane->getLinkCont()) {
     463     15605559 :             if (&link->getLane()->getEdge() == nextEdge) {
     464              :                 return link;
     465              :             }
     466              :         }
     467              :         // this is for the non-multique case, maybe we should use caching here !!!
     468       852331 :         for (const MSLane* const lane : myEdge.getLanes()) {
     469       835925 :             if (lane != bestLane) {
     470       580180 :                 for (MSLink* const link : lane->getLinkCont()) {
     471       473101 :                     if (&link->getLane()->getEdge() == nextEdge) {
     472              :                         return link;
     473              :                     }
     474              :                 }
     475              :             }
     476              :         }
     477              :     }
     478              :     return nullptr;
     479              : }
     480              : 
     481              : 
     482              : bool
     483     27222020 : MESegment::isOpen(const MEVehicle* veh) const {
     484              : #ifdef DEBUG_OPENED
     485              :     if (DEBUG_COND || DEBUG_COND2(veh)) {
     486              :         gDebugFlag1 = true;
     487              :         std::cout << SIMTIME << " opened seg=" << getID() << " veh=" << Named::getIDSecure(veh)
     488              :                   << " tlsPenalty=" << myTLSPenalty;
     489              :         const MSLink* link = getLink(veh);
     490              :         if (link == 0) {
     491              :             std::cout << " link=0";
     492              :         } else {
     493              :             std::cout << " prio=" << link->havePriority()
     494              :                       << " override=" << limitedControlOverride(link)
     495              :                       << " isOpen=" << link->opened(veh->getEventTime(), veh->getSpeed(), veh->estimateLeaveSpeed(link),
     496              :                                                     veh->getVehicleType().getLengthWithGap(), veh->getImpatience(),
     497              :                                                     veh->getVehicleType().getCarFollowModel().getMaxDecel(), veh->getWaitingTime(),
     498              :                                                     0, nullptr, false, veh)
     499              :                       << " et=" << veh->getEventTime()
     500              :                       << " v=" << veh->getSpeed()
     501              :                       << " vLeave=" << veh->estimateLeaveSpeed(link)
     502              :                       << " impatience=" << veh->getImpatience()
     503              :                       << " tWait=" << veh->getWaitingTime();
     504              :         }
     505              :         std::cout << "\n";
     506              :         gDebugFlag1 = false;
     507              :     }
     508              : #endif
     509     27222020 :     if (myTLSPenalty) {
     510              :         // XXX should limited control take precedence over tls penalty?
     511              :         return true;
     512              :     }
     513     27172480 :     const MSLink* link = getLink(veh);
     514              :     return (link == nullptr
     515      4946433 :             || link->havePriority()
     516      4119154 :             || limitedControlOverride(link)
     517     31290870 :             || link->opened(veh->getEventTime(), veh->getSpeed(), veh->estimateLeaveSpeed(link),
     518      4118390 :                             veh->getVehicleType().getLengthWithGap(), veh->getImpatience(),
     519      4118390 :                             veh->getVehicleType().getCarFollowModel().getMaxDecel(), veh->getWaitingTime(),
     520              :                             0, nullptr, false, veh));
     521              : }
     522              : 
     523              : 
     524              : bool
     525      4122344 : MESegment::limitedControlOverride(const MSLink* link) const {
     526              :     assert(link != nullptr);
     527      4122344 :     if (!MSGlobals::gMesoLimitedJunctionControl) {
     528              :         return false;
     529              :     }
     530              :     // if the target segment of this link is not saturated junction control is disabled
     531              :     const MSEdge& targetEdge = link->getLane()->getEdge();
     532         8246 :     const MESegment* target = MSGlobals::gMesoNet->getSegmentForEdge(targetEdge);
     533         8246 :     return (target->getBruttoOccupancy() * 2 < target->myJamThreshold) && !targetEdge.isRoundabout();
     534              : }
     535              : 
     536              : 
     537              : void
     538     24014581 : MESegment::send(MEVehicle* veh, MESegment* const next, const int nextQIdx, SUMOTime time, const MSMoveReminder::Notification reason) {
     539     24014581 :     Queue& q = myQueues[veh->getQueIndex()];
     540              :     assert(isInvalid(next) || time >= q.getBlockTime());
     541     24014581 :     MSLink* const link = getLink(veh);
     542     24014581 :     if (link != nullptr) {
     543      1088531 :         link->removeApproaching(veh);
     544              :     }
     545     24014581 :     if (veh->isStopped()) {
     546         7784 :         veh->processStop();
     547              :     }
     548     24014581 :     MEVehicle* lc = removeCar(veh, time, reason); // new leaderCar
     549              :     q.setBlockTime(time);
     550              :     if (!isInvalid(next)) {
     551     23356190 :         const bool nextFree = next->myQueues[nextQIdx].getOccupancy() <= next->myJamThreshold;
     552     23356190 :         const SUMOTime tau = (q.getOccupancy() <= myJamThreshold
     553     23356190 :                               ? (nextFree ? myTau_ff : myTau_fj)
     554       407917 :                               : (nextFree ? myTau_jf : getTauJJ((double)next->myQueues[nextQIdx].size(), next->myQueueCapacity, next->myJamThreshold)));
     555              :         assert(tau >= 0);
     556     23356190 :         myLastHeadway = tauWithVehLength(tau, veh->getVehicleType().getLengthWithGap(), veh->getVehicleType().getCarFollowModel().getHeadwayTime());
     557     23356190 :         if (myTLSPenalty) {
     558        49540 :             const MSLink* const tllink = getLink(veh, true);
     559        49540 :             if (tllink != nullptr && tllink->isTLSControlled()) {
     560              :                 assert(tllink->getGreenFraction() > 0);
     561        49540 :                 myLastHeadway = (SUMOTime)((double)myLastHeadway / tllink->getGreenFraction());
     562              :             }
     563              :         }
     564     23356190 :         q.setBlockTime(q.getBlockTime() + myLastHeadway);
     565              :     }
     566     24014581 :     if (lc != nullptr) {
     567              :         lc->setEventTime(MAX2(lc->getEventTime(), q.getBlockTime()));
     568     17588556 :         MSGlobals::gMesoNet->addLeaderCar(lc, getLink(lc));
     569              :     }
     570     24014581 : }
     571              : 
     572              : SUMOTime
     573       135964 : MESegment::getTauJJ(double nextQueueSize, double nextQueueCapacity, double nextJamThreshold) const {
     574              :     // compute coefficients for the jam-jam headway function
     575              :     // this function models the effect that "empty space" needs to move
     576              :     // backwards through the downstream segment before the upstream segment may
     577              :     // send annother vehicle.
     578              :     // this allows jams to clear and move upstream.
     579              :     // the headway function f(x) depends on the number of vehicles in the
     580              :     // downstream segment x
     581              :     // f is a linear function that passes through the following fixed points:
     582              :     // f(n_jam_threshold) = tau_jf_withLength (for continuity)
     583              :     // f(headwayCapacity) = myTau_jj * headwayCapacity
     584              : 
     585       135964 :     const SUMOTime tau_jf_withLength = tauWithVehLength(myTau_jf, DEFAULT_VEH_LENGTH_WITH_GAP, 1.);
     586              :     // number of vehicles that fit into the NEXT queue (could be larger than expected with DEFAULT_VEH_LENGTH_WITH_GAP!)
     587       135964 :     const double headwayCapacity = MAX2(nextQueueSize, nextQueueCapacity / DEFAULT_VEH_LENGTH_WITH_GAP);
     588              :     // number of vehicles above which the NEXT queue is jammed
     589       135964 :     const double n_jam_threshold = headwayCapacity * nextJamThreshold / nextQueueCapacity;
     590              : 
     591              :     // slope a and axis offset b for the jam-jam headway function
     592              :     // solving f(x) = a * x + b
     593       135964 :     const double a = (STEPS2TIME(myTau_jj) * headwayCapacity - STEPS2TIME(tau_jf_withLength)) / (headwayCapacity - n_jam_threshold);
     594       135964 :     const double b = headwayCapacity * (STEPS2TIME(myTau_jj) - a);
     595              : 
     596              :     // it is only well defined for nextQueueSize >= n_jam_threshold (which may not be the case for longer vehicles), so we take the MAX
     597       135964 :     return TIME2STEPS(a * MAX2(nextQueueSize, n_jam_threshold) + b);
     598              : }
     599              : 
     600              : 
     601              : bool
     602      1160042 : MESegment::overtake() {
     603      1160066 :     return myOvertaking && RandHelper::rand() > (getBruttoOccupancy() / myCapacity);
     604              : }
     605              : 
     606              : 
     607              : void
     608     24057222 : MESegment::addReminders(MEVehicle* veh) const {
     609     24057222 :     if (veh->getQueIndex() != PARKING_QUEUE) {
     610     24051749 :         myQueues[veh->getQueIndex()].addReminders(veh);
     611              :     }
     612     24057222 : }
     613              : 
     614              : 
     615              : void
     616     24054762 : MESegment::receive(MEVehicle* veh, const int qIdx, SUMOTime time, const bool isDepart, const bool isTeleport, const bool newEdge) {
     617     24054762 :     const double speed = isDepart ? -1 : MAX2(veh->getSpeed(), MESO_MIN_SPEED); // on the previous segment
     618     24054762 :     veh->setSegment(this); // for arrival checking
     619              :     veh->setLastEntryTime(time);
     620              :     veh->setBlockTime(SUMOTime_MAX);
     621     24054762 :     if (!isDepart && (
     622              :                 // arrival on entering a new edge
     623      2845139 :                 (newEdge && veh->moveRoutePointer())
     624              :                 // arrival on entering a new segment
     625     23362059 :                 || veh->hasArrived())) {
     626              :         // route has ended
     627        15241 :         veh->setEventTime(time + TIME2STEPS(myLength / speed)); // for correct arrival speed
     628        15241 :         addReminders(veh);
     629        15241 :         veh->activateReminders(MSMoveReminder::NOTIFICATION_JUNCTION);
     630        30378 :         veh->updateDetectors(time, veh->getEventTime(), true,
     631        15241 :                              veh->getEdge()->isVaporizing() ? MSMoveReminder::NOTIFICATION_VAPORIZED_VAPORIZER : MSMoveReminder::NOTIFICATION_ARRIVED);
     632        15241 :         MSNet::getInstance()->getVehicleControl().scheduleVehicleRemoval(veh);
     633        15241 :         return;
     634              :     }
     635              :     assert(veh->getEdge() == &getEdge());
     636              :     // route continues
     637     24039521 :     Queue& q = myQueues[qIdx];
     638     24039521 :     const double maxSpeedOnEdge = veh->getEdge()->getLanes()[qIdx]->getVehicleMaxSpeed(veh);
     639              :     const double uspeed = MAX2(maxSpeedOnEdge, MESO_MIN_SPEED);
     640              :     std::vector<MEVehicle*>& cars = q.getModifiableVehicles();
     641              :     MEVehicle* newLeader = nullptr; // first vehicle in the current queue
     642     24039521 :     const SUMOTime stopTime = veh->checkStop(time);
     643     24039521 :     SUMOTime tleave = MAX2(stopTime + TIME2STEPS(myLength / uspeed) + getLinkPenalty(veh), q.getBlockTime());
     644     24039521 :     if (veh->isStopped()) {
     645        13576 :         myEdge.addWaiting(veh);
     646              :     }
     647     24039521 :     if (veh->isParking()) {
     648              :         // parking stops should take at least 1ms
     649         5473 :         veh->setEventTime(MAX2(stopTime, veh->getEventTime() + 1));
     650         5473 :         veh->setSegment(this, PARKING_QUEUE);
     651         5473 :         myEdge.getLanes()[0]->addParking(veh);  // TODO for GUI only
     652              :     } else {
     653     24034048 :         myEdge.lock();
     654     24034048 :         if (cars.empty()) {
     655      6424583 :             cars.push_back(veh);
     656              :             newLeader = veh;
     657              :         } else {
     658     17609465 :             SUMOTime leaderOut = cars[0]->getEventTime();
     659     17609465 :             if (!isDepart && leaderOut > tleave && overtake()) {
     660           20 :                 if (cars.size() == 1) {
     661            4 :                     MSGlobals::gMesoNet->removeLeaderCar(cars[0]);
     662              :                     newLeader = veh;
     663              :                 }
     664           20 :                 cars.insert(cars.begin() + 1, veh);
     665              :             } else {
     666     17609445 :                 tleave = MAX2(leaderOut + tauWithVehLength(myTau_ff, cars[0]->getVehicleType().getLengthWithGap(), cars[0]->getVehicleType().getCarFollowModel().getHeadwayTime()), tleave);
     667     17609445 :                 cars.insert(cars.begin(), veh);
     668              :             }
     669              :         }
     670     24034048 :         myEdge.unlock();
     671     24034048 :         myNumVehicles++;
     672     24034048 :         if (!isDepart && !isTeleport) {
     673              :             // departs and teleports could take place anywhere on the edge so they should not block regular flow
     674              :             // the -1 facilitates interleaving of multiple streams
     675     23340860 :             q.setEntryBlockTime(time + tauWithVehLength(myTau_ff, veh->getVehicleType().getLengthWithGap(), veh->getVehicleType().getCarFollowModel().getHeadwayTime()) - 1);
     676              :         }
     677     24034048 :         q.setOccupancy(MIN2(myQueueCapacity, q.getOccupancy() + veh->getVehicleType().getLengthWithGap()));
     678              :         veh->setEventTime(tleave);
     679     24034048 :         veh->setSegment(this, qIdx);
     680              :     }
     681     24039521 :     addReminders(veh);
     682     24039521 :     if (isDepart) {
     683       692544 :         veh->onDepart();
     684       692544 :         veh->activateReminders(MSMoveReminder::NOTIFICATION_DEPARTED);
     685     23346977 :     } else if (newEdge) {
     686      2844980 :         veh->activateReminders(MSMoveReminder::NOTIFICATION_JUNCTION);
     687              :     } else {
     688     20501997 :         veh->activateReminders(MSMoveReminder::NOTIFICATION_SEGMENT);
     689              :     }
     690     24039520 :     if (veh->isParking()) {
     691         5473 :         MSGlobals::gMesoNet->addLeaderCar(veh, nullptr);
     692              :     } else {
     693     24034047 :         if (newLeader != nullptr) {
     694      6424586 :             MSGlobals::gMesoNet->addLeaderCar(newLeader, getLink(newLeader));
     695              :         }
     696              :     }
     697              : }
     698              : 
     699              : 
     700              : bool
     701         8674 : MESegment::vaporizeAnyCar(SUMOTime currentTime, const MSDetectorFileOutput* filter) {
     702         9232 :     for (const Queue& q : myQueues) {
     703         8706 :         if (q.size() > 0) {
     704         8148 :             for (MEVehicle* const veh : q.getVehicles()) {
     705         8148 :                 if (filter->vehicleApplies(*veh)) {
     706         8148 :                     MSGlobals::gMesoNet->removeLeaderCar(veh);
     707         8148 :                     MSGlobals::gMesoNet->changeSegment(veh, currentTime + 1, &myVaporizationTarget, MSMoveReminder::NOTIFICATION_VAPORIZED_CALIBRATOR);
     708              :                     return true;
     709              :                 }
     710              :             }
     711              :         }
     712              :     }
     713              :     return false;
     714              : }
     715              : 
     716              : 
     717              : void
     718          348 : MESegment::setSpeedForQueue(double newSpeed, SUMOTime currentTime, SUMOTime blockTime, const std::vector<MEVehicle*>& vehs) {
     719          348 :     MEVehicle* v = vehs.back();
     720              :     SUMOTime oldEarliestExitTime = currentTime;
     721              :     const SUMOTime oldExit = MAX2(oldEarliestExitTime, v->getEventTime());
     722          348 :     v->updateDetectors(currentTime, oldExit, false);
     723          348 :     oldEarliestExitTime = oldExit + tauWithVehLength(myTau_ff, v->getVehicleType().getLengthWithGap(), v->getVehicleType().getCarFollowModel().getHeadwayTime());
     724          348 :     SUMOTime newEvent = MAX2(newArrival(v, newSpeed, currentTime), blockTime);
     725          348 :     if (v->getEventTime() != newEvent) {
     726          326 :         MSGlobals::gMesoNet->removeLeaderCar(v);
     727              :         v->setEventTime(newEvent);
     728          326 :         MSGlobals::gMesoNet->addLeaderCar(v, getLink(v));
     729              :     }
     730         1071 :     for (std::vector<MEVehicle*>::const_reverse_iterator i = vehs.rbegin() + 1; i != vehs.rend(); ++i) {
     731          723 :         const SUMOTime oldExitTime = MAX2(oldEarliestExitTime, (*i)->getEventTime());
     732          723 :         (*i)->updateDetectors(currentTime, oldExitTime, false);
     733          723 :         const SUMOTime minTau = tauWithVehLength(myTau_ff, (*i)->getVehicleType().getLengthWithGap(), (*i)->getVehicleType().getCarFollowModel().getHeadwayTime());
     734          723 :         oldEarliestExitTime = oldExitTime + minTau;
     735          723 :         newEvent = MAX2(newArrival(*i, newSpeed, currentTime), newEvent + minTau);
     736          723 :         (*i)->setEventTime(newEvent);
     737              :     }
     738          348 : }
     739              : 
     740              : 
     741              : SUMOTime
     742         1071 : MESegment::newArrival(const MEVehicle* const v, double newSpeed, SUMOTime currentTime) {
     743              :     // since speed is only an upper bound, pos may be too optimistic
     744         1071 :     const double pos = MIN2(myLength, STEPS2TIME(currentTime - v->getLastEntryTime()) * v->getSpeed());
     745              :     // traveltime may not be 0
     746         1071 :     double tt = (myLength - pos) / MAX2(newSpeed, MESO_MIN_SPEED);
     747         1071 :     return currentTime + MAX2(TIME2STEPS(tt), SUMOTime(1));
     748              : }
     749              : 
     750              : 
     751              : void
     752          991 : MESegment::setSpeed(double newSpeed, SUMOTime currentTime, double jamThresh, int qIdx) {
     753          991 :     recomputeJamThreshold(jamThresh);
     754              :     //myTau_length = MAX2(MESO_MIN_SPEED, newSpeed) * myEdge.getLanes().size() / TIME2STEPS(1);
     755              :     int i = 0;
     756         2064 :     for (const Queue& q : myQueues) {
     757         1073 :         if (q.size() != 0) {
     758          381 :             if (qIdx == -1 || qIdx == i) {
     759          348 :                 setSpeedForQueue(newSpeed, currentTime, q.getBlockTime(), q.getVehicles());
     760              :             }
     761              :         }
     762         1073 :         i++;
     763              :     }
     764          991 : }
     765              : 
     766              : 
     767              : SUMOTime
     768      1312982 : MESegment::getEventTime() const {
     769              :     SUMOTime result = SUMOTime_MAX;
     770      2849489 :     for (const Queue& q : myQueues) {
     771      1536507 :         if (q.size() != 0 && q.getVehicles().back()->getEventTime() < result) {
     772              :             result = q.getVehicles().back()->getEventTime();
     773              :         }
     774              :     }
     775      1312982 :     if (result < SUMOTime_MAX) {
     776       829643 :         return result;
     777              :     }
     778              :     return -1;
     779              : }
     780              : 
     781              : 
     782              : void
     783         7688 : MESegment::saveState(OutputDevice& out) const {
     784              :     bool write = false;
     785        14687 :     for (const Queue& q : myQueues) {
     786         7735 :         if (q.getBlockTime() != -1 || !q.getVehicles().empty()) {
     787              :             write = true;
     788              :             break;
     789              :         }
     790              :     }
     791         7688 :     if (write) {
     792          736 :         out.openTag(SUMO_TAG_SEGMENT).writeAttr(SUMO_ATTR_ID, getID());
     793         1486 :         for (const Queue& q : myQueues) {
     794          750 :             out.openTag(SUMO_TAG_VIEWSETTINGS_VEHICLES);
     795          750 :             out.writeAttr(SUMO_ATTR_TIME, toString<SUMOTime>(q.getBlockTime()));
     796         1500 :             out.writeAttr(SUMO_ATTR_BLOCKTIME, toString<SUMOTime>(q.getEntryBlockTime()));
     797          750 :             out.writeAttr(SUMO_ATTR_VALUE, q.getVehicles());
     798         1500 :             out.closeTag();
     799              :         }
     800         1472 :         out.closeTag();
     801              :     }
     802         7688 : }
     803              : 
     804              : 
     805              : void
     806          277 : MESegment::clearState() {
     807          554 :     for (Queue& q : myQueues) {
     808              :         q.getModifiableVehicles().clear();
     809              :     }
     810          277 : }
     811              : 
     812              : void
     813          585 : MESegment::loadState(const std::vector<SUMOVehicle*>& vehs, const SUMOTime blockTime, const SUMOTime entryBlockTime, const int queIdx) {
     814          585 :     Queue& q = myQueues[queIdx];
     815         1030 :     for (SUMOVehicle* veh : vehs) {
     816          445 :         MEVehicle* v = static_cast<MEVehicle*>(veh);
     817              :         assert(v->getSegment() == this);
     818          445 :         q.getModifiableVehicles().push_back(v);
     819          445 :         myNumVehicles++;
     820          445 :         q.setOccupancy(q.getOccupancy() + v->getVehicleType().getLengthWithGap());
     821          445 :         addReminders(v);
     822              :     }
     823          585 :     if (q.size() != 0) {
     824              :         // add the last vehicle of this queue
     825              :         // !!! one question - what about the previously added vehicle? Is it stored twice?
     826          230 :         MEVehicle* veh = q.getVehicles().back();
     827          230 :         MSGlobals::gMesoNet->addLeaderCar(veh, getLink(veh));
     828              :     }
     829              :     q.setBlockTime(blockTime);
     830              :     q.setEntryBlockTime(entryBlockTime);
     831          585 :     q.setOccupancy(MIN2(q.getOccupancy(), myQueueCapacity));
     832          585 : }
     833              : 
     834              : 
     835              : std::vector<const MEVehicle*>
     836           88 : MESegment::getVehicles() const {
     837              :     std::vector<const MEVehicle*> result;
     838          228 :     for (const Queue& q : myQueues) {
     839          140 :         result.insert(result.end(), q.getVehicles().begin(), q.getVehicles().end());
     840              :     }
     841           88 :     return result;
     842            0 : }
     843              : 
     844              : 
     845              : bool
     846      3905417 : MESegment::hasBlockedLeader() const {
     847      7863878 :     for (const Queue& q : myQueues) {
     848      3978352 :         if (q.size() > 0 && q.getVehicles().back()->getWaitingTime() > 0) {
     849              :             return true;
     850              :         }
     851              :     }
     852              :     return false;
     853              : }
     854              : 
     855              : 
     856              : double
     857            0 : MESegment::getFlow() const {
     858            0 :     return 3600 * getCarNumber() * getMeanSpeed() / myLength;
     859              : }
     860              : 
     861              : 
     862              : SUMOTime
     863     24039521 : MESegment::getLinkPenalty(const MEVehicle* veh) const {
     864     24039521 :     const MSLink* link = getLink(veh, myTLSPenalty || myCheckMinorPenalty);
     865     24039521 :     if (link != nullptr) {
     866              :         SUMOTime result = 0;
     867      1139014 :         if (link->isTLSControlled() && myTLSPenalty) {
     868              :             result += link->getMesoTLSPenalty();
     869              :         }
     870              :         // minor tls links may get an additional penalty
     871       315462 :         if (!link->havePriority() &&
     872              :                 // do not apply penalty on top of tLSPenalty
     873      1139014 :                 !myTLSPenalty &&
     874              :                 // do not apply penalty if limited control is active
     875       279786 :                 (!MSGlobals::gMesoLimitedJunctionControl || limitedControlOverride(link))) {
     876       277316 :             result += myMinorPenalty;
     877              :         }
     878      1139014 :         return result;
     879              :     } else {
     880              :         return 0;
     881              :     }
     882              : }
     883              : 
     884              : 
     885              : bool
     886          378 : MESegment::tlsPenaltyOverride() const {
     887         1052 :     for (const MSLane* lane : myEdge.getLanes()) {
     888         1832 :         for (const MSLink* link : lane->getLinkCont()) {
     889         3474 :             if (link->isTLSControlled() && StringUtils::toBool(link->getTLLogic()->getParameter(OVERRIDE_TLS_PENALTIES, "0"))) {
     890              :                 return true;
     891              :             }
     892              :         }
     893              :     }
     894              :     return false;
     895              : }
     896              : 
     897              : 
     898              : double
     899            3 : MESegment::getWaitingSeconds() const {
     900              :     double result = 0;
     901            6 :     for (const Queue& q : myQueues) {
     902              :         // @note: only the leader currently accumulates waitingTime but this might change in the future
     903            4 :         for (const MEVehicle* veh : q.getVehicles()) {
     904            1 :             result += veh->getWaitingSeconds();
     905              :         }
     906              :     }
     907            3 :     return result;
     908              : }
     909              : 
     910              : 
     911              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1