LCOV - code coverage report
Current view: top level - src/microsim/output - MSInductLoop.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 98.2 % 278 273
Test Date: 2026-09-20 15:45:03 Functions: 100.0 % 30 30

            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    MSInductLoop.cpp
      15              : /// @author  Christian Roessel
      16              : /// @author  Daniel Krajzewicz
      17              : /// @author  Jakob Erdmann
      18              : /// @author  Sascha Krieg
      19              : /// @author  Michael Behrisch
      20              : /// @author  Laura Bieker
      21              : /// @author  Mirko Barthauer
      22              : /// @date    2004-11-23
      23              : ///
      24              : // An unextended detector measuring at a fixed position on a fixed lane.
      25              : /****************************************************************************/
      26              : #include <config.h>
      27              : 
      28              : #include "MSInductLoop.h"
      29              : #include <cassert>
      30              : #include <numeric>
      31              : #include <utility>
      32              : #ifdef HAVE_FOX
      33              : #include <utils/common/ScopedLocker.h>
      34              : #endif
      35              : #include <utils/common/WrappingCommand.h>
      36              : #include <utils/common/ToString.h>
      37              : #include <microsim/MSEventControl.h>
      38              : #include <microsim/MSLane.h>
      39              : #include <microsim/MSEdge.h>
      40              : #include <microsim/MSVehicle.h>
      41              : #include <microsim/MSNet.h>
      42              : #include <microsim/MSVehicleControl.h>
      43              : #include <microsim/transportables/MSTransportable.h>
      44              : #include <microsim/transportables/MSPModel.h>
      45              : #include <mesosim/MELoop.h>
      46              : #include <mesosim/MESegment.h>
      47              : #include <mesosim/MEVehicle.h>
      48              : #include <utils/common/MsgHandler.h>
      49              : #include <utils/common/UtilExceptions.h>
      50              : #include <utils/common/StringUtils.h>
      51              : #include <utils/iodevices/OutputDevice.h>
      52              : 
      53              : #define HAS_NOT_LEFT_DETECTOR -1
      54              : 
      55              : //#define DEBUG_E1_NOTIFY_MOVE
      56              : 
      57              : #define DEBUG_COND (true)
      58              : //#define DEBUG_COND (isSelected())
      59              : //#define DEBUG_COND (getID()=="")
      60              : 
      61              : // ===========================================================================
      62              : // method definitions
      63              : // ===========================================================================
      64        29935 : MSInductLoop::MSInductLoop(const std::string& id, MSLane* const lane,
      65              :                            double positionInMeters,
      66              :                            double length, std::string name,
      67              :                            const std::string& vTypes,
      68              :                            const std::string& nextEdges,
      69              :                            int detectPersons,
      70        29935 :                            const bool needLocking) :
      71              :     MSMoveReminder(id, lane),
      72              :     MSDetectorFileOutput(id, vTypes, nextEdges, detectPersons),
      73        29931 :     myName(name),
      74        29931 :     myPosition(positionInMeters),
      75        29931 :     myEndPosition(myPosition + length),
      76        29931 :     myNeedLock(needLocking || MSGlobals::gNumSimThreads > 1),
      77              :     // initialize in a way which doesn't impact actualted traffic lights at simulation start (yet doesn't look ugly in the outputs)
      78        29931 :     myLastLeaveTime(-3600),
      79        29931 :     myOverrideTime(-1),
      80        29931 :     myOverrideEntryTime(-1),
      81              :     myVehicleDataCont(),
      82              :     myVehiclesOnDet(),
      83        29931 :     myLastIntervalEnd(-1),
      84        29935 :     mySegment(nullptr) {
      85              :     assert(length >= 0);
      86              :     assert(myPosition >= 0 && myEndPosition <= myLane->getLength());
      87        29931 :     reset();
      88        29931 :     if (MSGlobals::gUseMesoSim) {
      89              :         // used by actuated tls
      90         1586 :         mySegment = MSGlobals::gMesoNet->getSegmentForEdge(lane->getEdge(), myPosition);
      91         1586 :         mySegment->addDetector(this);
      92         1586 :         mySegmentPos = myPosition - mySegment->getIndex() * mySegment->getLength();
      93              :     }
      94        29935 : }
      95              : 
      96              : 
      97        57410 : MSInductLoop::~MSInductLoop() {
      98        87219 : }
      99              : 
     100              : 
     101              : void
     102       162823 : MSInductLoop::reset() {
     103              : #ifdef HAVE_FOX
     104       162823 :     ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     105              : #endif
     106       162823 :     myEnteredVehicleNumber = 0;
     107       162823 :     myLastVehicleDataCont = myVehicleDataCont;
     108              :     myVehicleDataCont.clear();
     109       162823 :     myLastIntervalBegin = myLastIntervalEnd;
     110       162823 :     myLastIntervalEnd = SIMSTEP;
     111       162823 : }
     112              : 
     113              : 
     114              : bool
     115      1404092 : MSInductLoop::notifyEnter(SUMOTrafficObject& veh, Notification reason, const MSLane* /* enteredLane */) {
     116              :     // vehicles must be kept if the "inductionloop" wants to detect passeengers
     117      1404092 :     if (!vehicleApplies(veh) && (veh.isPerson() || myDetectPersons <= (int)PersonMode::WALK)) {
     118              :         return false;
     119              :     }
     120      1399141 :     if (MSGlobals::gUseMesoSim) {
     121       184720 :         MEVehicle* mesoveh = dynamic_cast<MEVehicle*>(&veh);
     122              :         assert(mesoveh != nullptr);
     123              :         const MESegment* seg = mesoveh->getSegment();
     124       184720 :         if (seg != nullptr && (seg->numQueues() == 1 || mesoveh->getQueIndex() == myLane->getIndex())) {
     125       160932 :             const SUMOTime onSegTime = mesoveh->getEventTime() - mesoveh->getLastEntryTime();
     126              :             // extrapolate movement
     127       160932 :             const SUMOTime exLeaveTime = mesoveh->getLastEntryTime() + (SUMOTime)((double)onSegTime * mySegmentPos / seg->getLength());
     128              :             //std::cout << SIMTIME << " det=" << getID() << " veh=" << veh.getID() << " entry=" << STEPS2TIME(mesoveh->getLastEntryTime()) << " et=" << STEPS2TIME(mesoveh->getEventTime()) << " exLeaveTime=" << STEPS2TIME(exLeaveTime) << "\n";
     129       160932 :             myNextMesoLeaveTimes.push(exLeaveTime);
     130              :         }
     131       184720 :         return true;
     132              :     }
     133      1214421 :     if (reason != NOTIFICATION_JUNCTION) { // the junction case is handled in notifyMove
     134       244896 :         if (veh.getBackPositionOnLane(myLane) >= myPosition) {
     135              :             return false;
     136              :         }
     137       242097 :         if (veh.getPositionOnLane() >= myPosition) {
     138              : #ifdef HAVE_FOX
     139         1247 :             ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     140              : #endif
     141         1247 :             myVehiclesOnDet[&veh] = SIMTIME;
     142         1247 :             myEnteredVehicleNumber++;
     143              :         }
     144              :     }
     145              :     return true;
     146              : }
     147              : 
     148              : 
     149              : bool
     150     23844900 : MSInductLoop::notifyMove(SUMOTrafficObject& veh, double oldPos,
     151              :                          double newPos, double newSpeed) {
     152     23844900 :     if (newPos < myPosition) {
     153              :         // detector not reached yet
     154              :         return true;
     155              :     }
     156      2760515 :     if (myDetectPersons > (int)PersonMode::WALK && !veh.isPerson()) {
     157              :         bool keep = false;
     158           38 :         MSBaseVehicle& v = dynamic_cast<MSBaseVehicle&>(veh);
     159           76 :         for (MSTransportable* p : v.getPersons()) {
     160           38 :             keep = notifyMove(*p, oldPos, newPos, newSpeed);
     161              :         }
     162           38 :         return keep;
     163              :     }
     164              : #ifdef HAVE_FOX
     165      2760477 :     ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     166              : #endif
     167      2760477 :     const double oldSpeed = veh.getPreviousSpeed();
     168      2760477 :     if (newPos >= myPosition && oldPos < myPosition) {
     169              :         // entered the detector by move
     170      1182675 :         const double timeBeforeEnter = MSCFModel::passingTime(oldPos, myPosition, newPos, oldSpeed, newSpeed);
     171      1182675 :         myVehiclesOnDet[&veh] = SIMTIME + timeBeforeEnter;
     172      1182675 :         myEnteredVehicleNumber++;
     173              : #ifdef DEBUG_E1_NOTIFY_MOVE
     174              :         if (DEBUG_COND) {
     175              :             std::cout << SIMTIME << " det=" << getID() << " enteredVeh=" << veh.getID() << "\n";
     176              :         }
     177              : #endif
     178              :     }
     179      2760477 :     double oldBackPos = oldPos - veh.getVehicleType().getLength();
     180      2760477 :     double newBackPos = newPos - veh.getVehicleType().getLength();
     181      2760477 :     if (newBackPos > myEndPosition) {
     182              :         // vehicle passed the detector (it may have changed onto this lane somewhere past the detector)
     183              :         // assert(!MSGlobals::gSemiImplicitEulerUpdate || newSpeed > 0 || myVehiclesOnDet.find(&veh) == myVehiclesOnDet.end());
     184              :         // assertion is invalid in case of teleportation
     185      1180300 :         if (oldBackPos <= myEndPosition) {
     186              :             const std::map<SUMOTrafficObject*, double>::iterator it = myVehiclesOnDet.find(&veh);
     187      1180296 :             if (it != myVehiclesOnDet.end()) {
     188      1180296 :                 const double entryTime = it->second;
     189      1180296 :                 const double leaveTime = SIMTIME + MSCFModel::passingTime(oldBackPos, myEndPosition, newBackPos, oldSpeed, newSpeed);
     190              :                 myVehiclesOnDet.erase(it);
     191              :                 assert(entryTime <= leaveTime);
     192      1180296 :                 myVehicleDataCont.push_back(VehicleData(veh, entryTime, leaveTime, false, myEndPosition - myPosition));
     193      1180296 :                 myLastLeaveTime = leaveTime;
     194              : #ifdef DEBUG_E1_NOTIFY_MOVE
     195              :                 if (DEBUG_COND) {
     196              :                     std::cout << SIMTIME << " det=" << getID() << " leftVeh=" << veh.getID() << " oldBackPos=" << oldBackPos << " newBackPos=" << newBackPos << "\n";
     197              :                 }
     198              : #endif
     199              :             } else {
     200              : #ifdef DEBUG_E1_NOTIFY_MOVE
     201              :                 if (DEBUG_COND) {
     202              :                     std::cout << SIMTIME << " det=" << getID() << " leftVeh=" << veh.getID() << " oldBackPos=" << oldBackPos << " newBackPos=" << newBackPos << " (notFound)\n";
     203              :                 }
     204              : #endif
     205              :             }
     206              :         } else {
     207              :             // vehicle is already beyond the detector...
     208              :             // This can happen even if it is still registered in myVehiclesOnDet, e.g., after teleport.
     209            4 :             myVehiclesOnDet.erase(&veh);
     210              : #ifdef DEBUG_E1_NOTIFY_MOVE
     211              :             if (DEBUG_COND) {
     212              :                 std::cout << SIMTIME << " det=" << getID() << " leftVeh=" << veh.getID() << " oldBackPos=" << oldBackPos << " newBackPos=" << newBackPos << " (unusual)\n";
     213              :             }
     214              : #endif
     215              :         }
     216      1180300 :         return false;
     217              :     }
     218              :     // vehicle stays on the detector
     219              :     return true;
     220              : }
     221              : 
     222              : 
     223              : bool
     224      1043657 : MSInductLoop::notifyLeave(SUMOTrafficObject& veh, double lastPos, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
     225      1043657 :     if (veh.isPerson() && myDetectPersons != (int)PersonMode::NONE) {
     226         3203 :         const int lastDir = lastPos < 0 ? MSPModel::BACKWARD : MSPModel::FORWARD;
     227         3203 :         notifyMovePerson(dynamic_cast<MSTransportable*>(&veh), lastDir, lastPos);
     228              :     }
     229      1043657 :     if (MSGlobals::gUseMesoSim && reason <= MSMoveReminder::NOTIFICATION_SEGMENT && vehicleApplies(veh)) {
     230       184586 :         MEVehicle* mesoveh = dynamic_cast<MEVehicle*>(&veh);
     231              :         assert(mesoveh != nullptr);
     232              :         const MESegment* seg = mesoveh->getSegment();
     233       184586 :         if (seg != nullptr && (seg->numQueues() == 1 || mesoveh->getQueIndex() == myLane->getIndex())) {
     234              :             // interpolate movement + 1 extra second
     235              :             // We can only interpolate the passing time at the detector after the vehicle leaves the segment. Consequently our information is always out of date
     236              :             // (by the time the current vehicle left, the follower vehicle should already have reached the detector so the estimated time gap to the last detection is always too large)
     237              :             // the prior implementation used seg->getBlockTime() which included the headway which somewhat worked around this deficit by accident
     238       173051 :             const SUMOTime inLeaveTime = mesoveh->getEventTime() - TIME2STEPS((seg->getLength() - mySegmentPos - mesoveh->getLength()) / myLane->getSpeedLimit());
     239              :             //std::cout << SIMTIME << " det=" << getID() << " veh=" << veh.getID() << " et=" << STEPS2TIME(mesoveh->getEventTime()) << " inLeaveTime=" << STEPS2TIME(inLeaveTime) << "\n";
     240       160809 :             auto q = seg->getQueue(mesoveh->getQueIndex());
     241       160809 :             if (q.size() > 1 && vehicleApplies(*q[q.size() - 2])) {
     242       146135 :                 myLastMesoLeaveTimes.push(inLeaveTime + TIME2STEPS(seg->getLastHeadwaySeconds()));
     243              :                 //std::cout << "    inLeaveTime2=" << (STEPS2TIME(inLeaveTime) + seg->getLastHeadwaySeconds()) << "\n";
     244              :             }
     245       160809 :             myLastMesoLeaveTimes.push(inLeaveTime);
     246       160809 :             myEnteredVehicleNumber++;
     247       160809 :         }
     248       184586 :         return false;
     249              :     }
     250       859071 :     if (reason != MSMoveReminder::NOTIFICATION_JUNCTION || (veh.isPerson() && myDetectPersons != (int)PersonMode::NONE)) {
     251              : #ifdef HAVE_FOX
     252        31610 :         ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     253              : #endif
     254              :         const std::map<SUMOTrafficObject*, double>::iterator it = myVehiclesOnDet.find(&veh);
     255        31610 :         if (it != myVehiclesOnDet.end()) {
     256         3609 :             const double entryTime = it->second;
     257         3609 :             const double leaveTime = SIMTIME + TS;
     258              :             myVehiclesOnDet.erase(it);
     259         3609 :             myVehicleDataCont.push_back(VehicleData(veh, entryTime, leaveTime, true));
     260         3609 :             myLastLeaveTime = leaveTime;
     261              :         }
     262              :         return false;
     263              :     }
     264              :     return true;
     265              : }
     266              : 
     267              : 
     268              : double
     269          145 : MSInductLoop::getSpeed(const int offset) const {
     270          145 :     const std::vector<VehicleData>& d = collectVehiclesOnDet(SIMSTEP - offset);
     271          174 :     return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, speedSum) / (double) d.size();
     272          145 : }
     273              : 
     274              : 
     275              : double
     276          145 : MSInductLoop::getVehicleLength(const int offset) const {
     277          145 :     const std::vector<VehicleData>& d = collectVehiclesOnDet(SIMSTEP - offset);
     278          174 :     return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, lengthSum) / (double)d.size();
     279          145 : }
     280              : 
     281              : 
     282              : double
     283        55851 : MSInductLoop::getOccupancy() const {
     284        55851 :     if (myOverrideTime >= 0) {
     285           48 :         return myOverrideTime < TS ? (TS - myOverrideTime) / TS * 100 : 0;
     286              :     }
     287        55803 :     const SUMOTime tbeg = SIMSTEP - DELTA_T;
     288              :     double occupancy = 0;
     289        55803 :     const double csecond = SIMTIME;
     290        61647 :     for (const VehicleData& i : collectVehiclesOnDet(tbeg, false, false, true)) {
     291         5844 :         const double leaveTime = i.leaveTimeM == HAS_NOT_LEFT_DETECTOR ? csecond : MIN2(i.leaveTimeM, csecond);
     292         5844 :         const double entryTime = MAX2(i.entryTimeM, STEPS2TIME(tbeg));
     293         8615 :         occupancy += MIN2(leaveTime - entryTime, TS);
     294        55803 :     }
     295        55803 :     return occupancy / TS * 100.;
     296              : }
     297              : 
     298              : 
     299              : double
     300         3024 : MSInductLoop::getEnteredNumber(const int offset) const {
     301         3024 :     if (myOverrideTime >= 0) {
     302           48 :         return myOverrideTime < TS ? 1 : 0;
     303              :     }
     304         2976 :     return (double)collectVehiclesOnDet(SIMSTEP - offset, true, true).size();
     305              : }
     306              : 
     307              : 
     308              : std::vector<std::string>
     309          875 : MSInductLoop::getVehicleIDs(const int offset) const {
     310              :     std::vector<std::string> ret;
     311         1101 :     for (const VehicleData& i : collectVehiclesOnDet(SIMSTEP - offset, true, true)) {
     312          226 :         ret.push_back(i.idM);
     313          875 :     }
     314          875 :     return ret;
     315            0 : }
     316              : 
     317              : 
     318              : double
     319      1122418 : MSInductLoop::getTimeSinceLastDetection() const {
     320      1122418 :     if (myOverrideTime >= 0) {
     321              :         return myOverrideTime;
     322              :     }
     323      1122370 :     if (myVehiclesOnDet.size() != 0) {
     324              :         // detector is occupied
     325              :         return 0;
     326              :     }
     327      1060821 :     if (MSGlobals::gUseMesoSim) {
     328              :         //std::cout << SIMTIME << " det=" << getID() << " qBefore=" << myNextMesoLeaveTimes.size() << " last=" << STEPS2TIME(getLastDetectionTime()) << " qAfter=" << myNextMesoLeaveTimes.size() << " top=" << (myNextMesoLeaveTimes.empty() ? -1 : myNextMesoLeaveTimes.top()) << "\n";
     329       440170 :         return SIMTIME - STEPS2TIME(getLastDetectionTime());
     330              :     }
     331       620651 :     return SIMTIME - myLastLeaveTime;
     332              : }
     333              : 
     334              : 
     335              : void
     336           16 : MSInductLoop::loadTimeSinceLastDetection(double time) {
     337           16 :     myLastLeaveTime = SIMTIME - time;
     338           16 : }
     339              : 
     340              : 
     341              : double
     342       184760 : MSInductLoop::getOccupancyTime() const {
     343              : #ifdef HAVE_FOX
     344       184760 :     ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     345              : #endif
     346       184760 :     if (myOverrideTime >= 0) {
     347            0 :         return SIMTIME - myOverrideEntryTime;
     348              :     }
     349       184760 :     if (myVehiclesOnDet.size() == 0) {
     350              :         // detector is unoccupied
     351              :         return 0;
     352              :     } else {
     353              :         double minEntry = std::numeric_limits<double>::max();
     354         4530 :         for (const auto& i : myVehiclesOnDet) {
     355         2265 :             minEntry = MIN2(i.second, minEntry);
     356              :         }
     357         2265 :         return SIMTIME - minEntry;
     358              :     }
     359              : }
     360              : 
     361              : 
     362              : double
     363         2266 : MSInductLoop::getArrivalDelay() const {
     364              : #ifdef HAVE_FOX
     365         2266 :     ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     366              : #endif
     367         2266 :     MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
     368              :     double result = -INVALID_DOUBLE;
     369         2294 :     for (const auto& item : collectVehiclesOnDet(SIMSTEP - DELTA_T)) {
     370           28 :         SUMOVehicle* v = vc.getVehicle(item.idM);
     371           28 :         if (v != nullptr) {
     372           28 :             MSBaseVehicle* veh = dynamic_cast<MSBaseVehicle*>(v);
     373           28 :             double ad = veh->getStopArrivalDelay();
     374           28 :             if (ad != INVALID_DOUBLE) {
     375              :                 result = MAX2(result, ad);
     376              :             }
     377              :         }
     378         2266 :     }
     379         2266 :     return result;
     380              : }
     381              : 
     382              : 
     383              : void
     384       501512 : MSInductLoop::discardOldTimes(TimeQueue& tq, SUMOTime now) {
     385              :     // discard times in the past except for the latest one
     386       501512 :     SUMOTime last = tq.top();
     387       501512 :     if (tq.size() > 1) {
     388              :         tq.pop();
     389       743608 :         while (tq.size() > 0 && tq.top() < now) {
     390       455658 :             last = tq.top();
     391              :             tq.pop();
     392              :         }
     393       287950 :         tq.push(last);
     394              :     }
     395       501512 : }
     396              : 
     397              : 
     398              : SUMOTime
     399       849830 : MSInductLoop::getLastDetectionTime() const {
     400       849830 :     if (myOverrideTime >= 0) {
     401            0 :         return SIMSTEP - TIME2STEPS(myOverrideTime);
     402              :     }
     403       849830 :     if (myVehiclesOnDet.size() != 0) {
     404        12696 :         return MSNet::getInstance()->getCurrentTimeStep();
     405              :     }
     406       837134 :     const SUMOTime now = SIMSTEP;
     407              :     if (MSGlobals::gUseMesoSim
     408       571624 :             && !myNextMesoLeaveTimes.empty()
     409      1089670 :             && myNextMesoLeaveTimes.top() < now) {
     410       252210 :         discardOldTimes(myNextMesoLeaveTimes, now);
     411       252210 :         SUMOTime last = myNextMesoLeaveTimes.top();
     412       252210 :         if (!myLastMesoLeaveTimes.empty() && myLastMesoLeaveTimes.top() < now) {
     413       249302 :             discardOldTimes(myLastMesoLeaveTimes, now);
     414       249302 :             last = MAX2(last, myLastMesoLeaveTimes.top());
     415              :         }
     416              :         //std::cout << SIMTIME << " det=" << getID() << " result=" << last << " last=" << myNextMesoLeaveTimes.top() << " last2=" << (myLastMesoLeaveTimes.size() > 0 ? myLastMesoLeaveTimes.top() : -1) << "\n";
     417       252210 :         return last;
     418              :     }
     419      1014946 :     return TIME2STEPS(myLastLeaveTime);
     420              : }
     421              : 
     422              : 
     423              : double
     424          732 : MSInductLoop::getIntervalOccupancy(bool lastInterval) const {
     425              :     double occupancy = 0;
     426          732 :     const double csecond = lastInterval ? STEPS2TIME(myLastIntervalEnd) : SIMTIME;
     427          732 :     const double aggTime = csecond - STEPS2TIME(lastInterval ? myLastIntervalBegin : myLastIntervalEnd);
     428          732 :     if (aggTime == 0) {
     429              :         return 0;
     430              :     }
     431         3033 :     for (const VehicleData& i : collectVehiclesOnDet(myLastIntervalEnd, false, false, true, lastInterval)) {
     432         2304 :         const double leaveTime = i.leaveTimeM == HAS_NOT_LEFT_DETECTOR ? csecond : MIN2(i.leaveTimeM, csecond);
     433         2304 :         const double entryTime = MAX2(i.entryTimeM, STEPS2TIME(lastInterval ? myLastIntervalBegin : myLastIntervalEnd));
     434         2304 :         occupancy += MIN2(leaveTime - entryTime, aggTime);
     435          729 :     }
     436          729 :     return occupancy / aggTime * 100.;
     437              : }
     438              : 
     439              : 
     440              : double
     441          732 : MSInductLoop::getIntervalMeanSpeed(bool lastInterval) const {
     442          732 :     const std::vector<VehicleData>& d = collectVehiclesOnDet(myLastIntervalEnd, false, false, false, lastInterval);
     443         1236 :     return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, speedSum) / (double) d.size();
     444          732 : }
     445              : 
     446              : 
     447              : int
     448          732 : MSInductLoop::getIntervalVehicleNumber(bool lastInterval) const {
     449          732 :     return (int)collectVehiclesOnDet(myLastIntervalEnd, false, false, false, lastInterval).size();
     450              : }
     451              : 
     452              : 
     453              : std::vector<std::string>
     454          732 : MSInductLoop::getIntervalVehicleIDs(bool lastInterval) const {
     455              :     std::vector<std::string> ret;
     456         3036 :     for (const VehicleData& i : collectVehiclesOnDet(myLastIntervalEnd, false, false, false, lastInterval)) {
     457         2304 :         ret.push_back(i.idM);
     458          732 :     }
     459          732 :     return ret;
     460            0 : }
     461              : 
     462              : 
     463              : void
     464           20 : MSInductLoop::overrideTimeSinceDetection(double time) {
     465           20 :     myOverrideTime = time;
     466           20 :     if (time < 0) {
     467            4 :         myOverrideEntryTime = -1;
     468              :     } else {
     469           16 :         const double entryTime = MAX2(0.0, SIMTIME - time);
     470           16 :         if (myOverrideEntryTime >= 0) {
     471              :             // maintain earlier entry time to achive continous detection
     472           12 :             myOverrideEntryTime = MIN2(myOverrideEntryTime, entryTime);
     473              :         } else {
     474            4 :             myOverrideEntryTime = entryTime;
     475              :         }
     476              :     }
     477           20 : }
     478              : 
     479              : void
     480        29927 : MSInductLoop::writeXMLDetectorProlog(OutputDevice& dev) const {
     481        59854 :     dev.writeXMLHeader("detector", "det_e1_file.xsd");
     482        29927 : }
     483              : 
     484              : 
     485              : void
     486       132892 : MSInductLoop::writeXMLOutput(OutputDevice& dev, SUMOTime startTime, SUMOTime stopTime) {
     487       132892 :     if (dev.isNull()) {
     488        29683 :         reset();
     489        29683 :         return;
     490              :     }
     491       103209 :     const double t(STEPS2TIME(stopTime - startTime));
     492       103209 :     double occupancy = 0.;
     493              :     double speedSum = 0.;
     494              :     double lengthSum = 0.;
     495       103209 :     int contrib = 0;
     496              :     // to approximate the space mean speed
     497              :     double inverseSpeedSum = 0.;
     498      1094069 :     for (const VehicleData& vData : myVehicleDataCont) {
     499      1977791 :         const double timeOnDetDuringInterval = vData.leaveTimeM - MAX2(STEPS2TIME(startTime), vData.entryTimeM);
     500       990860 :         occupancy += MIN2(timeOnDetDuringInterval, t);
     501       990860 :         if (!vData.leftEarlyM) {
     502       987847 :             speedSum += vData.speedM;
     503              :             assert(vData.speedM > 0.);
     504       987847 :             inverseSpeedSum += 1. / vData.speedM;
     505       987847 :             lengthSum += vData.lengthM;
     506       987847 :             contrib++;
     507              :         }
     508              :     }
     509       103209 :     if (MSGlobals::gUseMesoSim) {
     510          168 :         contrib = myEnteredVehicleNumber;
     511              :     }
     512       103209 :     const double flow = (double)contrib / t * 3600.;
     513       107380 :     for (std::map< SUMOTrafficObject*, double >::const_iterator i = myVehiclesOnDet.begin(); i != myVehiclesOnDet.end(); ++i) {
     514         8261 :         occupancy += STEPS2TIME(stopTime) - MAX2(STEPS2TIME(startTime), i->second);
     515              :     }
     516       103209 :     occupancy *= 100. / t;
     517       103209 :     const double meanSpeed = contrib != 0 ? speedSum / (double)contrib : -1;
     518       103209 :     const double harmonicMeanSpeed = contrib != 0 ? (double)contrib / inverseSpeedSum : -1;
     519       103209 :     const double meanLength = contrib != 0 ? lengthSum / (double)contrib : -1;
     520       206418 :     dev.openTag(SUMO_TAG_INTERVAL).writeTime(SUMO_ATTR_BEGIN, startTime).writeTime(SUMO_ATTR_END, stopTime);
     521       103209 :     dev.writeAttr(SUMO_ATTR_ID, StringUtils::escapeXML(getID())).writeAttr("nVehContrib", contrib);
     522       103209 :     dev.writeAttr("flow", flow).writeAttr("occupancy", occupancy).writeAttr("speed", meanSpeed).writeAttr("harmonicMeanSpeed", harmonicMeanSpeed);
     523       103209 :     dev.writeAttr("length", meanLength).writeAttr("nVehEntered", myEnteredVehicleNumber).closeTag();
     524       103209 :     reset();
     525              : }
     526              : 
     527              : 
     528              : void
     529     27632012 : MSInductLoop::detectorUpdate(const SUMOTime /* step */) {
     530     27632012 :     if (myDetectPersons == (int)PersonMode::NONE) {
     531              :         return;
     532              :     }
     533        78492 :     if (myLane->hasPedestrians()) {
     534       202544 :         for (MSTransportable* p : myLane->getEdge().getPersons()) {
     535       183796 :             if (p->getLane() != myLane || !vehicleApplies(*p)) {
     536        28500 :                 continue;
     537              :             }
     538       155296 :             notifyMovePerson(p, p->getDirection(), p->getPositionOnLane());
     539              :         }
     540              :     }
     541              : }
     542              : 
     543              : 
     544              : void
     545       158499 : MSInductLoop::notifyMovePerson(MSTransportable* p, int dir, double pos) {
     546       158499 :     if (personApplies(*p, dir)) {
     547       114067 :         const double newSpeed = p->getSpeed();
     548       114067 :         const double newPos = (dir == MSPModel::FORWARD
     549       114067 :                                ? pos
     550              :                                // position relative to detector
     551        31992 :                                : myPosition - (pos - myPosition));
     552       114067 :         const double oldPos = newPos - SPEED2DIST(newSpeed);
     553       114067 :         if (oldPos - p->getVehicleType().getLength() <= myPosition) {
     554        80986 :             notifyMove(*p, oldPos, newPos, newSpeed);
     555              :         }
     556              :     }
     557       158499 : }
     558              : 
     559              : 
     560              : std::vector<MSInductLoop::VehicleData>
     561       117546 : MSInductLoop::collectVehiclesOnDet(SUMOTime tMS, bool includeEarly, bool leaveTime, bool forOccupancy, bool lastInterval) const {
     562              : #ifdef HAVE_FOX
     563       117546 :     ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     564              : #endif
     565       117546 :     const double t = STEPS2TIME(tMS);
     566              :     std::vector<VehicleData> ret;
     567      1194903 :     for (const VehicleData& i : myVehicleDataCont) {
     568      1077357 :         if ((includeEarly || !i.leftEarlyM) && (!lastInterval || i.entryTimeM < t)) {
     569      1074531 :             if (i.entryTimeM >= t || (leaveTime && i.leaveTimeM >= t)) {
     570         5682 :                 ret.push_back(i);
     571              :             }
     572              :         }
     573              :     }
     574       710997 :     for (const VehicleData& i : myLastVehicleDataCont) {
     575       593451 :         if (includeEarly || !i.leftEarlyM) {
     576       593451 :             if ((!lastInterval && (i.entryTimeM >= t || (leaveTime && i.leaveTimeM >= t)))
     577         6120 :                     || (lastInterval && i.leaveTimeM <= t + STEPS2TIME(myLastIntervalEnd - myLastIntervalBegin))) { // TODO: check duration of last interval
     578         6529 :                 ret.push_back(i);
     579              :             }
     580              :         }
     581              :     }
     582       127642 :     for (const auto& i : myVehiclesOnDet) {
     583        10096 :         if ((!lastInterval && (i.second >= t || leaveTime || forOccupancy))
     584           72 :                 || (lastInterval && i.second < t && t - i.second < STEPS2TIME(DELTA_T))) { // no need to check leave time, they are still on the detector
     585        10018 :             SUMOTrafficObject* const v = i.first;
     586        10018 :             VehicleData d(*v, i.second, HAS_NOT_LEFT_DETECTOR, false);
     587        10018 :             d.speedM = v->getSpeed();
     588        10018 :             ret.push_back(d);
     589              :         }
     590              :     }
     591       117546 :     return ret;
     592            0 : }
     593              : 
     594              : 
     595      1193923 : MSInductLoop::VehicleData::VehicleData(const SUMOTrafficObject& v, double entryTimestep,
     596      1193923 :                                        double leaveTimestep, const bool leftEarly, const double detLength)
     597      1193923 :     : idM(v.getID()), lengthM(v.getVehicleType().getLength()), entryTimeM(entryTimestep), leaveTimeM(leaveTimestep),
     598      1203941 :       speedM((v.getVehicleType().getLength() + detLength) / MAX2(leaveTimestep - entryTimestep, NUMERICAL_EPS)), typeIDM(v.getVehicleType().getID()),
     599      1193923 :       leftEarlyM(leftEarly) {}
     600              : 
     601              : 
     602              : void
     603           16 : MSInductLoop::clearState(SUMOTime time) {
     604           16 :     myLastLeaveTime = STEPS2TIME(time);
     605           16 :     myEnteredVehicleNumber = 0;
     606           16 :     myLastVehicleDataCont.clear();
     607           16 :     myVehicleDataCont.clear();
     608              :     myVehiclesOnDet.clear();
     609           16 : }
     610              : 
     611              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1