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

Generated by: LCOV version 2.0-1