LCOV - code coverage report
Current view: top level - src/microsim/output - MSInductLoop.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 97.7 % 259 253
Test Date: 2026-07-26 16:30:20 Functions: 100.0 % 29 29

            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        29857 : 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        29857 :                            const bool needLocking) :
      71              :     MSMoveReminder(id, lane),
      72              :     MSDetectorFileOutput(id, vTypes, nextEdges, detectPersons),
      73        29853 :     myName(name),
      74        29853 :     myPosition(positionInMeters),
      75        29853 :     myEndPosition(myPosition + length),
      76        29853 :     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        29853 :     myLastLeaveTime(-3600),
      79        29853 :     myOverrideTime(-1),
      80        29853 :     myOverrideEntryTime(-1),
      81              :     myVehicleDataCont(),
      82              :     myVehiclesOnDet(),
      83        29853 :     myLastIntervalEnd(-1),
      84        29857 :     mySegment(nullptr) {
      85              :     assert(length >= 0);
      86              :     assert(myPosition >= 0 && myEndPosition <= myLane->getLength());
      87        29853 :     reset();
      88        29853 :     if (MSGlobals::gUseMesoSim) {
      89              :         // used by actuated tls
      90         1524 :         mySegment = MSGlobals::gMesoNet->getSegmentForEdge(lane->getEdge(), myPosition);
      91         1524 :         mySegment->addDetector(this);
      92         1524 :         mySegmentPos = myPosition - mySegment->getIndex() * mySegment->getLength();
      93              :     }
      94        29857 : }
      95              : 
      96              : 
      97        57296 : MSInductLoop::~MSInductLoop() {
      98        87027 : }
      99              : 
     100              : 
     101              : void
     102       162230 : MSInductLoop::reset() {
     103              : #ifdef HAVE_FOX
     104       162230 :     ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     105              : #endif
     106       162230 :     myEnteredVehicleNumber = 0;
     107       162230 :     myLastVehicleDataCont = myVehicleDataCont;
     108              :     myVehicleDataCont.clear();
     109       162230 :     myLastIntervalBegin = myLastIntervalEnd;
     110       162230 :     myLastIntervalEnd = SIMSTEP;
     111       162230 : }
     112              : 
     113              : 
     114              : bool
     115      1381540 : MSInductLoop::notifyEnter(SUMOTrafficObject& veh, Notification reason, const MSLane* /* enteredLane */) {
     116              :     // vehicles must be kept if the "inductionloop" wants to detect passeengers
     117      1381540 :     if (!vehicleApplies(veh) && (veh.isPerson() || myDetectPersons <= (int)PersonMode::WALK)) {
     118              :         return false;
     119              :     }
     120      1376788 :     if (MSGlobals::gUseMesoSim) {
     121       168906 :         MEVehicle* mesoveh = dynamic_cast<MEVehicle*>(&veh);
     122              :         assert(mesoveh != nullptr);
     123              :         const MESegment* seg = mesoveh->getSegment();
     124       168906 :         if (seg != nullptr && (seg->numQueues() == 1 || mesoveh->getQueIndex() == myLane->getIndex())) {
     125       146814 :             const SUMOTime onSegTime = mesoveh->getEventTime() - mesoveh->getLastEntryTime();
     126              :             // extrapolate movement
     127       146814 :             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       146814 :             myNextMesoLeaveTimes.push(exLeaveTime);
     130              :         }
     131       168906 :         return false;
     132              :     }
     133      1207882 :     if (reason != NOTIFICATION_JUNCTION) { // the junction case is handled in notifyMove
     134       238368 :         if (veh.getBackPositionOnLane(myLane) >= myPosition) {
     135              :             return false;
     136              :         }
     137       235586 :         if (veh.getPositionOnLane() >= myPosition) {
     138              : #ifdef HAVE_FOX
     139         1253 :             ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     140              : #endif
     141         1253 :             myVehiclesOnDet[&veh] = SIMTIME;
     142         1253 :             myEnteredVehicleNumber++;
     143              :         }
     144              :     }
     145              :     return true;
     146              : }
     147              : 
     148              : 
     149              : bool
     150     23177782 : MSInductLoop::notifyMove(SUMOTrafficObject& veh, double oldPos,
     151              :                          double newPos, double newSpeed) {
     152     23177782 :     if (newPos < myPosition) {
     153              :         // detector not reached yet
     154              :         return true;
     155              :     }
     156      2748847 :     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      2748809 :     ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     166              : #endif
     167      2748809 :     const double oldSpeed = veh.getPreviousSpeed();
     168      2748809 :     if (newPos >= myPosition && oldPos < myPosition) {
     169              :         // entered the detector by move
     170      1175604 :         const double timeBeforeEnter = MSCFModel::passingTime(oldPos, myPosition, newPos, oldSpeed, newSpeed);
     171      1175604 :         myVehiclesOnDet[&veh] = SIMTIME + timeBeforeEnter;
     172      1175604 :         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      2748809 :     double oldBackPos = oldPos - veh.getVehicleType().getLength();
     180      2748809 :     double newBackPos = newPos - veh.getVehicleType().getLength();
     181      2748809 :     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      1173228 :         if (oldBackPos <= myEndPosition) {
     186              :             const std::map<SUMOTrafficObject*, double>::iterator it = myVehiclesOnDet.find(&veh);
     187      1173228 :             if (it != myVehiclesOnDet.end()) {
     188      1173228 :                 const double entryTime = it->second;
     189      1173228 :                 const double leaveTime = SIMTIME + MSCFModel::passingTime(oldBackPos, myEndPosition, newBackPos, oldSpeed, newSpeed);
     190              :                 myVehiclesOnDet.erase(it);
     191              :                 assert(entryTime <= leaveTime);
     192      1173228 :                 myVehicleDataCont.push_back(VehicleData(veh, entryTime, leaveTime, false, myEndPosition - myPosition));
     193      1173228 :                 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            0 :             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      1173228 :         return false;
     217              :     }
     218              :     // vehicle stays on the detector
     219              :     return true;
     220              : }
     221              : 
     222              : 
     223              : bool
     224       859083 : MSInductLoop::notifyLeave(SUMOTrafficObject& veh, double lastPos, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
     225       859083 :     if (veh.isPerson() && myDetectPersons != (int)PersonMode::NONE) {
     226         3204 :         const int lastDir = lastPos < 0 ? MSPModel::BACKWARD : MSPModel::FORWARD;
     227         3204 :         notifyMovePerson(dynamic_cast<MSTransportable*>(&veh), lastDir, lastPos);
     228              :     }
     229       859083 :     if (reason != MSMoveReminder::NOTIFICATION_JUNCTION || (veh.isPerson() && myDetectPersons != (int)PersonMode::NONE)) {
     230              : #ifdef HAVE_FOX
     231        32334 :         ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     232              : #endif
     233              :         const std::map<SUMOTrafficObject*, double>::iterator it = myVehiclesOnDet.find(&veh);
     234        32334 :         if (it != myVehiclesOnDet.end()) {
     235         3614 :             const double entryTime = it->second;
     236         3614 :             const double leaveTime = SIMTIME + TS;
     237              :             myVehiclesOnDet.erase(it);
     238         3614 :             myVehicleDataCont.push_back(VehicleData(veh, entryTime, leaveTime, true));
     239         3614 :             myLastLeaveTime = leaveTime;
     240              :         }
     241              :         return false;
     242              :     }
     243              :     return true;
     244              : }
     245              : 
     246              : 
     247              : double
     248          146 : MSInductLoop::getSpeed(const int offset) const {
     249          146 :     const std::vector<VehicleData>& d = collectVehiclesOnDet(SIMSTEP - offset);
     250          175 :     return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, speedSum) / (double) d.size();
     251          146 : }
     252              : 
     253              : 
     254              : double
     255          146 : MSInductLoop::getVehicleLength(const int offset) const {
     256          146 :     const std::vector<VehicleData>& d = collectVehiclesOnDet(SIMSTEP - offset);
     257          175 :     return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, lengthSum) / (double)d.size();
     258          146 : }
     259              : 
     260              : 
     261              : double
     262        55946 : MSInductLoop::getOccupancy() const {
     263        55946 :     if (myOverrideTime >= 0) {
     264           48 :         return myOverrideTime < TS ? (TS - myOverrideTime) / TS * 100 : 0;
     265              :     }
     266        55898 :     const SUMOTime tbeg = SIMSTEP - DELTA_T;
     267              :     double occupancy = 0;
     268        55898 :     const double csecond = SIMTIME;
     269        61783 :     for (const VehicleData& i : collectVehiclesOnDet(tbeg, false, false, true)) {
     270         5885 :         const double leaveTime = i.leaveTimeM == HAS_NOT_LEFT_DETECTOR ? csecond : MIN2(i.leaveTimeM, csecond);
     271         5885 :         const double entryTime = MAX2(i.entryTimeM, STEPS2TIME(tbeg));
     272         8697 :         occupancy += MIN2(leaveTime - entryTime, TS);
     273        55898 :     }
     274        55898 :     return occupancy / TS * 100.;
     275              : }
     276              : 
     277              : 
     278              : double
     279         3217 : MSInductLoop::getEnteredNumber(const int offset) const {
     280         3217 :     if (myOverrideTime >= 0) {
     281           48 :         return myOverrideTime < TS ? 1 : 0;
     282              :     }
     283         3169 :     return (double)collectVehiclesOnDet(SIMSTEP - offset, true, true).size();
     284              : }
     285              : 
     286              : 
     287              : std::vector<std::string>
     288          970 : MSInductLoop::getVehicleIDs(const int offset) const {
     289              :     std::vector<std::string> ret;
     290         1239 :     for (const VehicleData& i : collectVehiclesOnDet(SIMSTEP - offset, true, true)) {
     291          269 :         ret.push_back(i.idM);
     292          970 :     }
     293          970 :     return ret;
     294            0 : }
     295              : 
     296              : 
     297              : double
     298      1111606 : MSInductLoop::getTimeSinceLastDetection() const {
     299      1111606 :     if (myOverrideTime >= 0) {
     300              :         return myOverrideTime;
     301              :     }
     302      1111558 :     if (myVehiclesOnDet.size() != 0) {
     303              :         // detector is occupied
     304              :         return 0;
     305              :     }
     306      1052712 :     if (MSGlobals::gUseMesoSim) {
     307              :         //std::cout << SIMTIME << " det=" << getID() << " qBefore=" << myNextMesoLeaveTimes.size() << " last=" << STEPS2TIME(getLastDetectionTime()) << " qAfter=" << myNextMesoLeaveTimes.size() << " top=" << (myNextMesoLeaveTimes.empty() ? -1 : myNextMesoLeaveTimes.top()) << "\n";
     308       439250 :         return SIMTIME - STEPS2TIME(getLastDetectionTime());
     309              :     }
     310       613462 :     return SIMTIME - myLastLeaveTime;
     311              : }
     312              : 
     313              : 
     314              : void
     315           16 : MSInductLoop::loadTimeSinceLastDetection(double time) {
     316           16 :     myLastLeaveTime = SIMTIME - time;
     317           16 : }
     318              : 
     319              : 
     320              : double
     321       185150 : MSInductLoop::getOccupancyTime() const {
     322              : #ifdef HAVE_FOX
     323       185150 :     ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     324              : #endif
     325       185150 :     if (myOverrideTime >= 0) {
     326            0 :         return SIMTIME - myOverrideEntryTime;
     327              :     }
     328       185150 :     if (myVehiclesOnDet.size() == 0) {
     329              :         // detector is unoccupied
     330              :         return 0;
     331              :     } else {
     332              :         double minEntry = std::numeric_limits<double>::max();
     333         4530 :         for (const auto& i : myVehiclesOnDet) {
     334         2265 :             minEntry = MIN2(i.second, minEntry);
     335              :         }
     336         2265 :         return SIMTIME - minEntry;
     337              :     }
     338              : }
     339              : 
     340              : 
     341              : double
     342         1044 : MSInductLoop::getArrivalDelay() const {
     343              : #ifdef HAVE_FOX
     344         1044 :     ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     345              : #endif
     346         1044 :     MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
     347              :     double result = -INVALID_DOUBLE;
     348         1072 :     for (const auto& item : collectVehiclesOnDet(SIMSTEP - DELTA_T)) {
     349           28 :         SUMOVehicle* v = vc.getVehicle(item.idM);
     350           28 :         if (v != nullptr) {
     351           28 :             MSBaseVehicle* veh = dynamic_cast<MSBaseVehicle*>(v);
     352           28 :             double ad = veh->getStopArrivalDelay();
     353           28 :             if (ad != INVALID_DOUBLE) {
     354              :                 result = MAX2(result, ad);
     355              :             }
     356              :         }
     357         1044 :     }
     358         1044 :     return result;
     359              : }
     360              : 
     361              : 
     362              : SUMOTime
     363       845212 : MSInductLoop::getLastDetectionTime() const {
     364       845212 :     if (myOverrideTime >= 0) {
     365            0 :         return SIMSTEP - TIME2STEPS(myOverrideTime);
     366              :     }
     367       845212 :     if (myVehiclesOnDet.size() != 0) {
     368        12676 :         return MSNet::getInstance()->getCurrentTimeStep();
     369              :     }
     370              :     if (MSGlobals::gUseMesoSim
     371       568714 :             && !myNextMesoLeaveTimes.empty()
     372      1078344 :             && myNextMesoLeaveTimes.top() < SIMSTEP) {
     373              :         // find the latest time that is already in the past
     374       245494 :         SUMOTime last = myNextMesoLeaveTimes.top();
     375       245494 :         if (myNextMesoLeaveTimes.size() > 1) {
     376              :             myNextMesoLeaveTimes.pop();
     377       307412 :             while (myNextMesoLeaveTimes.size() > 0 && myNextMesoLeaveTimes.top() < SIMSTEP) {
     378       142706 :                 last = myNextMesoLeaveTimes.top();
     379              :                 myNextMesoLeaveTimes.pop();
     380              :             }
     381       164706 :             myNextMesoLeaveTimes.push(last);
     382              :         }
     383       245494 :         const SUMOTime blockTime = mySegment->getQueueBlockTime(MIN2(mySegment->numQueues() - 1, myLane->getIndex()));
     384       245494 :         const SUMOTime last2 = blockTime - TIME2STEPS((mySegment->getLength() - mySegmentPos) / myLane->getSpeedLimit());
     385              :         //std::cout << SIMTIME << " det=" << getID() << " last=" << last << " last2=" << last2 << " block=" << blockTime << " times=" << myNextMesoLeaveTimes.size() << "\n";
     386       245494 :         return MAX2(last, last2);
     387              :     }
     388      1019754 :     return TIME2STEPS(myLastLeaveTime);
     389              : }
     390              : 
     391              : 
     392              : double
     393          972 : MSInductLoop::getIntervalOccupancy(bool lastInterval) const {
     394              :     double occupancy = 0;
     395          972 :     const double csecond = lastInterval ? STEPS2TIME(myLastIntervalEnd) : SIMTIME;
     396          972 :     const double aggTime = csecond - STEPS2TIME(lastInterval ? myLastIntervalBegin : myLastIntervalEnd);
     397          972 :     if (aggTime == 0) {
     398              :         return 0;
     399              :     }
     400         4040 :     for (const VehicleData& i : collectVehiclesOnDet(myLastIntervalEnd, false, false, true, lastInterval)) {
     401         3072 :         const double leaveTime = i.leaveTimeM == HAS_NOT_LEFT_DETECTOR ? csecond : MIN2(i.leaveTimeM, csecond);
     402         3072 :         const double entryTime = MAX2(i.entryTimeM, STEPS2TIME(lastInterval ? myLastIntervalBegin : myLastIntervalEnd));
     403         3072 :         occupancy += MIN2(leaveTime - entryTime, aggTime);
     404          968 :     }
     405          968 :     return occupancy / aggTime * 100.;
     406              : }
     407              : 
     408              : 
     409              : double
     410          972 : MSInductLoop::getIntervalMeanSpeed(bool lastInterval) const {
     411          972 :     const std::vector<VehicleData>& d = collectVehiclesOnDet(myLastIntervalEnd, false, false, false, lastInterval);
     412         1644 :     return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, speedSum) / (double) d.size();
     413          972 : }
     414              : 
     415              : 
     416              : int
     417          972 : MSInductLoop::getIntervalVehicleNumber(bool lastInterval) const {
     418          972 :     return (int)collectVehiclesOnDet(myLastIntervalEnd, false, false, false, lastInterval).size();
     419              : }
     420              : 
     421              : 
     422              : std::vector<std::string>
     423          972 : MSInductLoop::getIntervalVehicleIDs(bool lastInterval) const {
     424              :     std::vector<std::string> ret;
     425         4044 :     for (const VehicleData& i : collectVehiclesOnDet(myLastIntervalEnd, false, false, false, lastInterval)) {
     426         3072 :         ret.push_back(i.idM);
     427          972 :     }
     428          972 :     return ret;
     429            0 : }
     430              : 
     431              : 
     432              : void
     433           20 : MSInductLoop::overrideTimeSinceDetection(double time) {
     434           20 :     myOverrideTime = time;
     435           20 :     if (time < 0) {
     436            4 :         myOverrideEntryTime = -1;
     437              :     } else {
     438           16 :         const double entryTime = MAX2(0.0, SIMTIME - time);
     439           16 :         if (myOverrideEntryTime >= 0) {
     440              :             // maintain earlier entry time to achive continous detection
     441           12 :             myOverrideEntryTime = MIN2(myOverrideEntryTime, entryTime);
     442              :         } else {
     443            4 :             myOverrideEntryTime = entryTime;
     444              :         }
     445              :     }
     446           20 : }
     447              : 
     448              : void
     449        29849 : MSInductLoop::writeXMLDetectorProlog(OutputDevice& dev) const {
     450        59698 :     dev.writeXMLHeader("detector", "det_e1_file.xsd");
     451        29849 : }
     452              : 
     453              : 
     454              : void
     455       132377 : MSInductLoop::writeXMLOutput(OutputDevice& dev, SUMOTime startTime, SUMOTime stopTime) {
     456       132377 :     if (dev.isNull()) {
     457        29198 :         reset();
     458        29198 :         return;
     459              :     }
     460       103179 :     const double t(STEPS2TIME(stopTime - startTime));
     461       103179 :     double occupancy = 0.;
     462              :     double speedSum = 0.;
     463              :     double lengthSum = 0.;
     464       103179 :     int contrib = 0;
     465              :     // to approximate the space mean speed
     466              :     double inverseSpeedSum = 0.;
     467      1093703 :     for (const VehicleData& vData : myVehicleDataCont) {
     468      1977125 :         const double timeOnDetDuringInterval = vData.leaveTimeM - MAX2(STEPS2TIME(startTime), vData.entryTimeM);
     469       990524 :         occupancy += MIN2(timeOnDetDuringInterval, t);
     470       990524 :         if (!vData.leftEarlyM) {
     471       987507 :             speedSum += vData.speedM;
     472              :             assert(vData.speedM > 0.);
     473       987507 :             inverseSpeedSum += 1. / vData.speedM;
     474       987507 :             lengthSum += vData.lengthM;
     475       987507 :             contrib++;
     476              :         }
     477              :     }
     478       103179 :     const double flow = (double)contrib / t * 3600.;
     479       107344 :     for (std::map< SUMOTrafficObject*, double >::const_iterator i = myVehiclesOnDet.begin(); i != myVehiclesOnDet.end(); ++i) {
     480         8249 :         occupancy += STEPS2TIME(stopTime) - MAX2(STEPS2TIME(startTime), i->second);
     481              :     }
     482       103179 :     occupancy *= 100. / t;
     483       103179 :     const double meanSpeed = contrib != 0 ? speedSum / (double)contrib : -1;
     484       103179 :     const double harmonicMeanSpeed = contrib != 0 ? (double)contrib / inverseSpeedSum : -1;
     485       103179 :     const double meanLength = contrib != 0 ? lengthSum / (double)contrib : -1;
     486       206358 :     dev.openTag(SUMO_TAG_INTERVAL).writeTime(SUMO_ATTR_BEGIN, startTime).writeTime(SUMO_ATTR_END, stopTime);
     487       103179 :     dev.writeAttr(SUMO_ATTR_ID, StringUtils::escapeXML(getID())).writeAttr("nVehContrib", contrib);
     488       103179 :     dev.writeAttr("flow", flow).writeAttr("occupancy", occupancy).writeAttr("speed", meanSpeed).writeAttr("harmonicMeanSpeed", harmonicMeanSpeed);
     489       103179 :     dev.writeAttr("length", meanLength).writeAttr("nVehEntered", myEnteredVehicleNumber).closeTag();
     490       103179 :     reset();
     491              : }
     492              : 
     493              : 
     494              : void
     495     27497251 : MSInductLoop::detectorUpdate(const SUMOTime /* step */) {
     496     27497251 :     if (myDetectPersons == (int)PersonMode::NONE) {
     497              :         return;
     498              :     }
     499        78592 :     if (myLane->hasPedestrians()) {
     500       202640 :         for (MSTransportable* p : myLane->getEdge().getPersons()) {
     501       183844 :             if (p->getLane() != myLane || !vehicleApplies(*p)) {
     502        28504 :                 continue;
     503              :             }
     504       155340 :             notifyMovePerson(p, p->getDirection(), p->getPositionOnLane());
     505              :         }
     506              :     }
     507              : }
     508              : 
     509              : 
     510              : void
     511       158544 : MSInductLoop::notifyMovePerson(MSTransportable* p, int dir, double pos) {
     512       158544 :     if (personApplies(*p, dir)) {
     513       114112 :         const double newSpeed = p->getSpeed();
     514       114112 :         const double newPos = (dir == MSPModel::FORWARD
     515       114112 :                                ? pos
     516              :                                // position relative to detector
     517        31992 :                                : myPosition - (pos - myPosition));
     518       114112 :         const double oldPos = newPos - SPEED2DIST(newSpeed);
     519       114112 :         if (oldPos - p->getVehicleType().getLength() <= myPosition) {
     520        80988 :             notifyMove(*p, oldPos, newPos, newSpeed);
     521              :         }
     522              :     }
     523       158544 : }
     524              : 
     525              : 
     526              : std::vector<MSInductLoop::VehicleData>
     527       117763 : MSInductLoop::collectVehiclesOnDet(SUMOTime tMS, bool includeEarly, bool leaveTime, bool forOccupancy, bool lastInterval) const {
     528              : #ifdef HAVE_FOX
     529       117763 :     ScopedLocker<> lock(myNotificationMutex, myNeedLock);
     530              : #endif
     531       117763 :     const double t = STEPS2TIME(tMS);
     532              :     std::vector<VehicleData> ret;
     533      1197167 :     for (const VehicleData& i : myVehicleDataCont) {
     534      1079404 :         if ((includeEarly || !i.leftEarlyM) && (!lastInterval || i.entryTimeM < t)) {
     535      1075636 :             if (i.entryTimeM >= t || (leaveTime && i.leaveTimeM >= t)) {
     536         6578 :                 ret.push_back(i);
     537              :             }
     538              :         }
     539              :     }
     540       715294 :     for (const VehicleData& i : myLastVehicleDataCont) {
     541       597531 :         if (includeEarly || !i.leftEarlyM) {
     542       597531 :             if ((!lastInterval && (i.entryTimeM >= t || (leaveTime && i.leaveTimeM >= t)))
     543         8160 :                     || (lastInterval && i.leaveTimeM <= t + STEPS2TIME(myLastIntervalEnd - myLastIntervalBegin))) { // TODO: check duration of last interval
     544         8689 :                 ret.push_back(i);
     545              :             }
     546              :         }
     547              :     }
     548       128116 :     for (const auto& i : myVehiclesOnDet) {
     549        10353 :         if ((!lastInterval && (i.second >= t || leaveTime || forOccupancy))
     550           96 :                 || (lastInterval && i.second < t && t - i.second < STEPS2TIME(DELTA_T))) { // no need to check leave time, they are still on the detector
     551        10249 :             SUMOTrafficObject* const v = i.first;
     552        10249 :             VehicleData d(*v, i.second, HAS_NOT_LEFT_DETECTOR, false);
     553        10249 :             d.speedM = v->getSpeed();
     554        10249 :             ret.push_back(d);
     555              :         }
     556              :     }
     557       117763 :     return ret;
     558            0 : }
     559              : 
     560              : 
     561      1187091 : MSInductLoop::VehicleData::VehicleData(const SUMOTrafficObject& v, double entryTimestep,
     562      1187091 :                                        double leaveTimestep, const bool leftEarly, const double detLength)
     563      1187091 :     : idM(v.getID()), lengthM(v.getVehicleType().getLength()), entryTimeM(entryTimestep), leaveTimeM(leaveTimestep),
     564      1197340 :       speedM((v.getVehicleType().getLength() + detLength) / MAX2(leaveTimestep - entryTimestep, NUMERICAL_EPS)), typeIDM(v.getVehicleType().getID()),
     565      1187091 :       leftEarlyM(leftEarly) {}
     566              : 
     567              : 
     568              : void
     569           16 : MSInductLoop::clearState(SUMOTime time) {
     570           16 :     myLastLeaveTime = STEPS2TIME(time);
     571           16 :     myEnteredVehicleNumber = 0;
     572           16 :     myLastVehicleDataCont.clear();
     573           16 :     myVehicleDataCont.clear();
     574              :     myVehiclesOnDet.clear();
     575           16 : }
     576              : 
     577              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1