LCOV - code coverage report
Current view: top level - src/microsim - MSInsertionControl.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.5 % 222 201
Test Date: 2026-09-20 15:45:03 Functions: 96.0 % 25 24

            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    MSInsertionControl.cpp
      15              : /// @author  Christian Roessel
      16              : /// @author  Daniel Krajzewicz
      17              : /// @author  Axel Wegener
      18              : /// @author  Michael Behrisch
      19              : /// @author  Jakob Erdmann
      20              : /// @author  Mirko Barthauer
      21              : /// @date    Mon, 12 Mar 2001
      22              : ///
      23              : // Inserts vehicles into the network when their departure time is reached
      24              : /****************************************************************************/
      25              : #include <config.h>
      26              : 
      27              : #include <iostream>
      28              : #include <algorithm>
      29              : #include <cassert>
      30              : #include <iterator>
      31              : #include <utils/router/IntermodalRouter.h>
      32              : #include <microsim/devices/MSDevice_Routing.h>
      33              : #include <microsim/devices/MSRoutingEngine.h>
      34              : #include "MSGlobals.h"
      35              : #include "MSVehicle.h"
      36              : #include "MSVehicleControl.h"
      37              : #include "MSLane.h"
      38              : #include "MSEdge.h"
      39              : #include "MSNet.h"
      40              : #include "MSRouteHandler.h"
      41              : #include "MSInsertionControl.h"
      42              : 
      43              : 
      44              : // ===========================================================================
      45              : // member method definitions
      46              : // ===========================================================================
      47        42946 : MSInsertionControl::MSInsertionControl(MSVehicleControl& vc,
      48              :                                        SUMOTime maxDepartDelay,
      49              :                                        bool eagerInsertionCheck,
      50              :                                        int maxVehicleNumber,
      51        42946 :                                        SUMOTime randomDepartOffset) :
      52        42946 :     myVehicleControl(vc),
      53        42946 :     myMaxDepartDelay(maxDepartDelay),
      54        42946 :     myEagerInsertionCheck(eagerInsertionCheck),
      55        42946 :     myMaxVehicleNumber(maxVehicleNumber),
      56        42946 :     myPendingEmitsUpdateTime(SUMOTime_MIN),
      57        85892 :     myFlowRNG("flow") {
      58        42946 :     myMaxRandomDepartOffset = randomDepartOffset;
      59        42946 :     RandHelper::initRandGlobal(&myFlowRNG);
      60        42946 : }
      61              : 
      62              : 
      63        42328 : MSInsertionControl::~MSInsertionControl() {
      64        45157 :     for (const Flow& f : myFlows) {
      65         2829 :         delete (f.pars);
      66              :     }
      67        84656 : }
      68              : 
      69              : 
      70              : void
      71      5316852 : MSInsertionControl::add(SUMOVehicle* veh) {
      72      5316852 :     myAllVeh.add(veh);
      73      5316852 : }
      74              : 
      75              : 
      76              : bool
      77        19791 : MSInsertionControl::addFlow(SUMOVehicleParameter* const pars, int index) {
      78        19791 :     if (myFlowIDs.count(pars->id) > 0) {
      79           39 :         return false;
      80              :     }
      81              :     const bool loadingFromState = index >= 0;
      82        19752 :     Flow flow{pars, loadingFromState ? index : 0, initScale(pars->vtypeid)};
      83        19752 :     if (!loadingFromState && pars->repetitionProbability < 0 && pars->repetitionOffset < 0) {
      84              :         // init poisson flow (but only the timing)
      85          781 :         flow.pars->incrementFlow(flow.scale, &myFlowRNG);
      86          781 :         flow.pars->repetitionsDone--;
      87              :     }
      88        19752 :     myFlows.emplace_back(flow);
      89        19752 :     myFlowIDs.insert(std::make_pair(pars->id, flow.index));
      90        19752 :     return true;
      91              : }
      92              : 
      93              : 
      94              : double
      95        19752 : MSInsertionControl::initScale(const std::string vtypeid) {
      96        19752 :     MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
      97        19752 :     if (vc.hasVTypeDistribution(vtypeid)) {
      98              :         double result = -1;
      99          846 :         const RandomDistributor<MSVehicleType*>* dist = vc.getVTypeDistribution(vtypeid);
     100         2672 :         for (const MSVehicleType* t : dist->getVals()) {
     101         1826 :             if (result == -1) {
     102          846 :                 result = t->getParameter().scale;
     103          980 :             } else if (result != t->getParameter().scale) {
     104              :                 // unequal scales in distribution
     105              :                 return -1;
     106              :             }
     107              :         }
     108          846 :         return result;
     109              :     } else {
     110              :         // rng is not used since vtypeid is not a distribution
     111        18906 :         return vc.getVType(vtypeid, nullptr, true)->getParameter().scale;
     112              :     }
     113              : }
     114              : 
     115              : 
     116              : void
     117            5 : MSInsertionControl::updateScale(const std::string vtypeid) {
     118            5 :     for (Flow& f : myFlows) {
     119            0 :         if (f.pars->vtypeid == vtypeid) {
     120            0 :             f.scale = initScale(vtypeid);
     121              :         }
     122              :     }
     123            5 : }
     124              : 
     125              : 
     126              : int
     127    138409372 : MSInsertionControl::emitVehicles(SUMOTime time) {
     128              :     // check whether any vehicles shall be emitted within this time step
     129              :     const bool havePreChecked = MSRoutingEngine::isEnabled();
     130    138409372 :     if (myPendingEmits.empty() || (havePreChecked && myEmitCandidates.empty())) {
     131              :         return 0;
     132              :     }
     133              :     int numEmitted = 0;
     134              :     // we use buffering for the refused emits to save time
     135              :     //  for this, we have two lists; one contains previously refused emits, the second
     136              :     //  will be used to append those vehicles that will not be able to depart in this
     137              :     //  time step
     138              :     MSVehicleContainer::VehicleVector refusedEmits;
     139              : 
     140              :     // go through the list of previously refused vehicles, first
     141     20604492 :     MSNet::getInstance()->lockPendingEmits();
     142              :     MSVehicleContainer::VehicleVector::const_iterator veh;
     143   2158213928 :     for (veh = myPendingEmits.begin(); veh != myPendingEmits.end(); veh++) {
     144   2137609582 :         if (havePreChecked && (myEmitCandidates.count(*veh) == 0)) {
     145      4129741 :             refusedEmits.push_back(*veh);
     146              :         } else {
     147   2133479841 :             numEmitted += tryInsert(time, *veh, refusedEmits);
     148              :         }
     149              :     }
     150              :     myEmitCandidates.clear();
     151     20604346 :     myPendingEmits = refusedEmits;
     152     20604346 :     MSNet::getInstance()->unlockPendingEmits();
     153              :     return numEmitted;
     154     20604492 : }
     155              : 
     156              : 
     157              : int
     158   2133479841 : MSInsertionControl::tryInsert(SUMOTime time, SUMOVehicle* veh,
     159              :                               MSVehicleContainer::VehicleVector& refusedEmits) {
     160              :     assert(veh->getParameter().depart <= time);
     161   2133479841 :     const MSEdge& edge = *veh->getEdge();
     162   2133479841 :     if (veh->isOnRoad()) {
     163              :         return 1;
     164              :     }
     165       475184 :     if ((myMaxVehicleNumber < 0 || (int)MSNet::getInstance()->getVehicleControl().getRunningVehicleNo() < myMaxVehicleNumber)
     166   2133490964 :             && edge.insertVehicle(*veh, time, false, myEagerInsertionCheck || veh->getParameter().departProcedure == DepartDefinition::SPLIT)) {
     167              :         // Successful insertion
     168              :         return 1;
     169              :     }
     170   2129185529 :     if (myMaxDepartDelay >= 0 && time - veh->getParameter().depart > myMaxDepartDelay) {
     171              :         // remove vehicles waiting too long for departure
     172         2036 :         myVehicleControl.deleteVehicle(veh, true);
     173   2129183493 :     } else if (edge.isVaporizing()) {
     174              :         // remove vehicles if the edge shall be empty
     175        12818 :         myVehicleControl.deleteVehicle(veh, true);
     176   2129170675 :     } else if (myAbortedEmits.count(veh) > 0) {
     177              :         // remove vehicles which shall not be inserted for some reason
     178          458 :         myAbortedEmits.erase(veh);
     179          458 :         myVehicleControl.deleteVehicle(veh, true);
     180   2129170217 :     } else if ((veh->getRouteValidity(false) & (
     181              :                     MSBaseVehicle::ROUTE_START_INVALID_LANE
     182              :                     | MSBaseVehicle::ROUTE_START_INVALID_PERMISSIONS)) != 0) {
     183           46 :         myVehicleControl.deleteVehicle(veh, true);
     184              :     } else {
     185              :         // let the vehicle wait one step, we'll retry then
     186   2129170171 :         refusedEmits.push_back(veh);
     187              :     }
     188              :     edge.setLastFailedInsertionTime(time);
     189   2129185529 :     return 0;
     190              : }
     191              : 
     192              : 
     193              : void
     194    138409529 : MSInsertionControl::checkCandidates(SUMOTime time, const bool preCheck) {
     195    138409529 :     if (myMaxDepartDelay >= 0) {
     196     40178060 :         for (auto it = myPendingEmits.begin(); it != myPendingEmits.end();) {
     197     36635400 :             SUMOVehicle* veh = *it;
     198     36635400 :             if (time - veh->getParameter().depart > myMaxDepartDelay) {
     199              :                 // remove vehicles waiting too long for departure
     200       845867 :                 myVehicleControl.deleteVehicle(veh, true);
     201       845867 :                 it = myPendingEmits.erase(it);
     202              :             } else {
     203              :                 it++;
     204              :             }
     205              :         }
     206              :     }
     207    142234217 :     while (myAllVeh.anyWaitingBefore(time)) {
     208      3824688 :         const MSVehicleContainer::VehicleVector& top = myAllVeh.top();
     209      3824688 :         copy(top.begin(), top.end(), back_inserter(myPendingEmits));
     210      3824688 :         myAllVeh.pop();
     211              :     }
     212    138409529 :     if (preCheck) {
     213              :         MSVehicleContainer::VehicleVector::const_iterator veh;
     214    778722670 :         for (veh = myPendingEmits.begin(); veh != myPendingEmits.end(); veh++) {
     215    692592820 :             SUMOVehicle* const v = *veh;
     216    692592820 :             const MSEdge* const edge = v->getEdge();
     217    692592820 :             if (edge->insertVehicle(*v, time, true, myEagerInsertionCheck)) {
     218              :                 myEmitCandidates.insert(v);
     219              :             } else {
     220     86503056 :                 MSDevice_Routing* dev = static_cast<MSDevice_Routing*>(v->getDevice(typeid(MSDevice_Routing)));
     221              :                 if (dev != nullptr) {
     222              :                     dev->skipRouting(time);
     223              :                 }
     224              :             }
     225              :         }
     226              :     }
     227    138409491 : }
     228              : 
     229              : 
     230              : void
     231    138409538 : MSInsertionControl::determineCandidates(SUMOTime time) {
     232    138409538 :     MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
     233              :     // for equidistant vehicles, up-scaling is done via repetitionOffset
     234    172717992 :     for (std::vector<Flow>::iterator i = myFlows.begin(); i != myFlows.end();) {
     235              :         MSVehicleType* vtype = nullptr;
     236     34308463 :         SUMOVehicleParameter* const pars = i->pars;
     237     34308463 :         double typeScale = i->scale;
     238     34308463 :         if (typeScale < 0) {
     239              :             // must sample from distribution to determine scale value
     240            0 :             vtype = vehControl.getVType(pars->vtypeid, MSRouteHandler::getParsingRNG());
     241            0 :             typeScale = vtype->getParameter().scale;
     242              :         }
     243     34308463 :         const double scale = vehControl.getScale() * typeScale;
     244     34308463 :         const long long int scaledRepetitions = pars->repetitionNumber == std::numeric_limits<long long int>::max() ? std::numeric_limits<long long int>::max() :
     245     22697631 :                                                 (long long int)((double)pars->repetitionNumber * scale + 0.5);
     246     34308463 :         bool tryEmitByProb = pars->repetitionProbability > 0;
     247     39255419 :         while (scale > 0 && ((pars->repetitionProbability < 0
     248     29598768 :                               && pars->repetitionsDone < scaledRepetitions
     249     29586232 :                               && pars->depart + pars->repetitionTotalOffset <= time)
     250     34867114 :                              || (tryEmitByProb
     251      9097991 :                                  && pars->depart <= time
     252      9078071 :                                  && pars->repetitionEnd > time
     253              :                                  // only call rand if all other conditions are met
     254      9074354 :                                  && RandHelper::rand(&myFlowRNG) < (pars->repetitionProbability * TS))
     255              :                             )) {
     256              :             tryEmitByProb = false; // only emit one per step
     257      4946965 :             SUMOVehicleParameter* const newPars = new SUMOVehicleParameter(*pars);
     258      9893930 :             newPars->id = pars->id + "." + toString(i->index);
     259      4946965 :             newPars->depart = pars->repetitionProbability > 0 ? time : pars->depart + pars->repetitionTotalOffset + computeRandomDepartOffset();
     260      4946965 :             pars->incrementFlow(scale, &myFlowRNG);
     261      4946965 :             myFlowIDs[pars->id] = i->index;
     262              :             //std::cout << SIMTIME << " flow=" << pars->id << " done=" << pars->repetitionsDone << " totalOffset=" << STEPS2TIME(pars->repetitionTotalOffset) << "\n";
     263              :             // try to build the vehicle
     264      4946965 :             if (vehControl.getVehicle(newPars->id) == nullptr) {
     265      4946965 :                 ConstMSRoutePtr const route = MSRoute::dictionary(pars->routeid);
     266      4946965 :                 if (vtype == nullptr) {
     267      4946965 :                     vtype = vehControl.getVType(pars->vtypeid, MSRouteHandler::getParsingRNG());
     268              :                 }
     269      4946965 :                 SUMOVehicle* const vehicle = vehControl.buildVehicle(newPars, route, vtype, !MSGlobals::gCheckRoutes);
     270              :                 // for equidistant vehicles, all scaling is done via repetitionOffset (to avoid artefacts, #11441)
     271              :                 // for probabilistic vehicles, we use the quota
     272      4946956 :                 int quota = pars->repetitionProbability < 0 ? 1 : vehControl.getQuota(scale);
     273       558660 :                 if (quota > 0) {
     274      4923190 :                     vehControl.addVehicle(newPars->id, vehicle);
     275      4923190 :                     if (pars->departProcedure == DepartDefinition::GIVEN || pars->departProcedure == DepartDefinition::BEGIN) {
     276      4923166 :                         add(vehicle);
     277              :                     }
     278      4923190 :                     i->index++;
     279      4979769 :                     while (--quota > 0) {
     280        56579 :                         SUMOVehicleParameter* const quotaPars = new SUMOVehicleParameter(*pars);
     281       113158 :                         quotaPars->id = pars->id + "." + toString(i->index);
     282        56579 :                         quotaPars->depart = pars->repetitionProbability > 0 ? time :
     283            0 :                                             pars->depart + pars->repetitionsDone * pars->repetitionTotalOffset + computeRandomDepartOffset();
     284        56588 :                         SUMOVehicle* const quotaVehicle = vehControl.buildVehicle(quotaPars, route, vtype, !MSGlobals::gCheckRoutes);
     285        56579 :                         vehControl.addVehicle(quotaPars->id, quotaVehicle);
     286        56579 :                         if (pars->departProcedure == DepartDefinition::GIVEN || pars->departProcedure == DepartDefinition::BEGIN) {
     287        56579 :                             add(quotaVehicle);
     288              :                         }
     289        56579 :                         pars->repetitionsDone++;
     290        56579 :                         i->index++;
     291              :                     }
     292              :                 } else {
     293        23766 :                     vehControl.deleteVehicle(vehicle, true);
     294              :                 }
     295              :             } else {
     296            0 :                 if (MSGlobals::gStateLoaded) {
     297              :                     /// @note probably obsolete since flows save their state
     298              :                     break;
     299              :                 }
     300            0 :                 throw ProcessError(TLF("Another vehicle with the id '%' exists.", newPars->id));
     301              :             }
     302              :             vtype = nullptr;
     303              :         }
     304     34308454 :         if (time >= pars->repetitionEnd || pars->repetitionsDone >= scaledRepetitions) {
     305        16902 :             i = myFlows.erase(i);
     306        16902 :             MSRoute::checkDist(pars->routeid);
     307        16902 :             delete pars;
     308              :         } else {
     309              :             ++i;
     310              :         }
     311              :     }
     312    138409529 :     checkCandidates(time, MSRoutingEngine::isEnabled());
     313    138409491 : }
     314              : 
     315              : 
     316              : int
     317     13686876 : MSInsertionControl::getWaitingVehicleNo() const {
     318     13686876 :     return (int)myPendingEmits.size();
     319              : }
     320              : 
     321              : 
     322              : int
     323     86810179 : MSInsertionControl::getPendingFlowCount() const {
     324     86810179 :     return (int)myFlows.size();
     325              : }
     326              : 
     327              : 
     328              : void
     329          471 : MSInsertionControl::descheduleDeparture(const SUMOVehicle* veh) {
     330          471 :     myAbortedEmits.insert(veh);
     331          471 : }
     332              : 
     333              : void
     334        15073 : MSInsertionControl::retractDescheduleDeparture(const SUMOVehicle* veh) {
     335        15073 :     myAbortedEmits.erase(veh);
     336        15073 : }
     337              : 
     338              : 
     339              : void
     340         2929 : MSInsertionControl::alreadyDeparted(SUMOVehicle* veh) {
     341         2929 :     myPendingEmits.erase(std::remove(myPendingEmits.begin(), myPendingEmits.end(), veh), myPendingEmits.end());
     342         2929 :     myAllVeh.remove(veh);
     343         2929 : }
     344              : 
     345              : 
     346              : void
     347            6 : MSInsertionControl::clearPendingVehicles(const std::string& route) {
     348              :     //clear out the refused vehicle list, deleting the vehicles entirely
     349              :     MSVehicleContainer::VehicleVector::iterator veh;
     350           11 :     for (veh = myPendingEmits.begin(); veh != myPendingEmits.end();) {
     351            5 :         if ((*veh)->getRoute().getID() == route || route == "") {
     352            5 :             myVehicleControl.deleteVehicle(*veh, true);
     353            5 :             veh = myPendingEmits.erase(veh);
     354              :         } else {
     355              :             ++veh;
     356              :         }
     357              :     }
     358            6 : }
     359              : 
     360              : 
     361              : int
     362            0 : MSInsertionControl::getPendingEmits(const MSLane* lane) {
     363            0 :     const MSNet* net = MSNet::getInstance();
     364            0 :     if (net->getCurrentTimeStep() != myPendingEmitsUpdateTime) {
     365            0 :         net->lockPendingEmits();
     366              :         // updated pending emits (only once per time step)
     367              :         myPendingEmitsForLane.clear();
     368            0 :         for (const SUMOVehicle* const veh : myPendingEmits) {
     369            0 :             const MSLane* const vlane = veh->getLane();
     370            0 :             if (vlane != nullptr) {
     371            0 :                 myPendingEmitsForLane[vlane]++;
     372              :             } else {
     373              :                 // no (tentative) departLane was set, increase count for all
     374              :                 // lanes of the depart edge
     375            0 :                 for (const MSLane* const l : veh->getEdge()->getLanes()) {
     376            0 :                     myPendingEmitsForLane[l]++;
     377              :                 }
     378              :             }
     379              :         }
     380            0 :         myPendingEmitsUpdateTime = MSNet::getInstance()->getCurrentTimeStep();
     381            0 :         net->unlockPendingEmits();
     382              :     }
     383            0 :     return myPendingEmitsForLane[lane];
     384              : }
     385              : 
     386              : 
     387              : void
     388         4847 : MSInsertionControl::adaptIntermodalRouter(MSTransportableRouter& router) const {
     389              :     // fill the public transport router with pre-parsed public transport lines
     390         6851 :     for (const Flow& f : myFlows) {
     391         2004 :         if (f.pars->line != "") {
     392          511 :             ConstMSRoutePtr const route = MSRoute::dictionary(f.pars->routeid);
     393          511 :             router.getNetwork()->addSchedule(*f.pars, route == nullptr ? nullptr : &route->getStops());
     394              :         }
     395              :     }
     396         4847 : }
     397              : 
     398              : 
     399              : void
     400          535 : MSInsertionControl::saveState(OutputDevice& out) {
     401              :     // save flow states
     402          629 :     for (const Flow& flow : myFlows) {
     403           94 :         flow.pars->write(out, OptionsCont::getOptions(), SUMO_TAG_FLOWSTATE,
     404           94 :                          flow.pars->vtypeid == DEFAULT_VTYPE_ID ? "" : flow.pars->vtypeid);
     405           94 :         if (flow.pars->repetitionProbability <= 0) {
     406           80 :             out.writeAttr(SUMO_ATTR_NEXT, STEPS2TIME(flow.pars->repetitionTotalOffset));
     407              :         }
     408           94 :         out.writeAttr(SUMO_ATTR_ROUTE, flow.pars->routeid);
     409           94 :         out.writeAttr(SUMO_ATTR_DONE, flow.pars->repetitionsDone);
     410           94 :         out.writeAttr(SUMO_ATTR_INDEX, flow.index);
     411           94 :         if (flow.pars->wasSet(VEHPARS_FORCE_REROUTE)) {
     412           30 :             out.writeAttr(SUMO_ATTR_REROUTE, true);
     413              :         }
     414          108 :         for (const SUMOVehicleParameter::Stop& stop : flow.pars->stops) {
     415           14 :             stop.write(out);
     416              :         }
     417          188 :         out.closeTag();
     418              :     }
     419          535 : }
     420              : 
     421              : 
     422              : void
     423          162 : MSInsertionControl::clearState() {
     424          162 :     for (const Flow& f : myFlows) {
     425            0 :         delete (f.pars);
     426              :     }
     427              :     myFlows.clear();
     428              :     myFlowIDs.clear();
     429          162 :     myAllVeh.clearState();
     430              :     myPendingEmits.clear();
     431              :     myEmitCandidates.clear();
     432          162 :     myAbortedEmits.clear();
     433              :     // myPendingEmitsForLane must not be cleared since it updates itself on the next call
     434          162 : }
     435              : 
     436              : 
     437              : SUMOTime
     438      5230828 : MSInsertionControl::computeRandomDepartOffset() const {
     439      5230828 :     if (myMaxRandomDepartOffset > 0) {
     440              :         // round to the closest usable simulation step
     441           90 :         return DELTA_T * ((RandHelper::rand(myMaxRandomDepartOffset, MSRouteHandler::getParsingRNG()) + DELTA_T / 2) / DELTA_T);
     442              :     }
     443              :     return 0;
     444              : }
     445              : 
     446              : const SUMOVehicleParameter*
     447           40 : MSInsertionControl::getFlowPars(const std::string& id) const {
     448              :     if (hasFlow(id)) {
     449           40 :         for (const Flow& f : myFlows) {
     450           40 :             if (f.pars->id == id) {
     451              :                 return f.pars;
     452              :             }
     453              :         }
     454              :     }
     455              :     return nullptr;
     456              : }
     457              : 
     458              : SUMOVehicle*
     459          808 : MSInsertionControl::getLastFlowVehicle(const std::string& id) const {
     460              :     const auto it = myFlowIDs.find(id);
     461          808 :     if (it != myFlowIDs.end()) {
     462         1616 :         const std::string vehID = id + "." + toString(it->second);
     463          808 :         return MSNet::getInstance()->getVehicleControl().getVehicle(vehID);
     464              :     }
     465              :     return nullptr;
     466              : }
     467              : 
     468              : 
     469              : bool
     470        10550 : MSInsertionControl::hasTaxiFlow() const {
     471        21100 :     SumoRNG tmp("tmp");
     472        18256 :     for (const Flow& flow : myFlows) {
     473        15492 :         if (flow.scale != 0 &&
     474        38730 :                 (StringUtils::toBool(flow.pars->getParameter("has.taxi.device", "false"))
     475         7746 :                  || hasTaxiDeviceType(flow.pars->vtypeid, tmp))) {
     476              :             return true;
     477              :         }
     478              :     }
     479              :     return false;
     480              : }
     481              : 
     482              : 
     483              : bool
     484         7746 : MSInsertionControl::hasTaxiDeviceType(const std::string& vtypeId, SumoRNG& rng) {
     485         7746 :     MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
     486         7746 :     const MSVehicleType* vtype = vehControl.getVType(vtypeId, &rng);
     487        15492 :     return StringUtils::toBool(vtype->getParameter().getParameter("has.taxi.device", "false"));
     488              : }
     489              : 
     490              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1