LCOV - code coverage report
Current view: top level - src/microsim/output - MSE2Collector.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 93.7 % 557 522
Test Date: 2025-11-13 15:38:19 Functions: 94.1 % 34 32

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    MSE2Collector.cpp
      15              : /// @author  Christian Roessel
      16              : /// @author  Daniel Krajzewicz
      17              : /// @author  Sascha Krieg
      18              : /// @author  Michael Behrisch
      19              : /// @author  Robbin Blokpoel
      20              : /// @author  Jakob Erdmann
      21              : /// @author  Leonhard Luecken
      22              : /// @date    Mon Feb 03 2014 10:13 CET
      23              : ///
      24              : // An areal detector covering a sequence of consecutive lanes
      25              : /****************************************************************************/
      26              : 
      27              : 
      28              : /* TODO:
      29              :  * tests:
      30              :  *  - subsecond variant, ballistic variant
      31              :  * allow omitting jam processing (?)
      32              :  *
      33              :  * Meso-compatibility? (esp. enteredLane-argument for MSBaseVehicle::notifyEnter() is not treated)
      34              :  * Compatibility without internal lanes?
      35              :  * Include leftVehicles into output?
      36              :  */
      37              : #include <config.h>
      38              : 
      39              : #include <cassert>
      40              : #include <algorithm>
      41              : #ifdef HAVE_FOX
      42              : #include <utils/common/ScopedLocker.h>
      43              : #endif
      44              : #include <microsim/MSLane.h>
      45              : #include <microsim/MSEdge.h>
      46              : #include <microsim/MSLink.h>
      47              : #include <microsim/MSNet.h>
      48              : #include <microsim/MSVehicle.h>
      49              : #include <microsim/MSVehicleType.h>
      50              : #include <microsim/transportables/MSTransportable.h>
      51              : #include <microsim/transportables/MSPModel.h>
      52              : #include "MSE2Collector.h"
      53              : 
      54              : //#define DEBUG_E2_CONSTRUCTOR
      55              : //#define DEBUG_E2_NOTIFY_ENTER_AND_LEAVE
      56              : //#define DEBUG_E2_NOTIFY_MOVE
      57              : //#define DEBUG_E2_MAKE_VEHINFO
      58              : //#define DEBUG_E2_DETECTOR_UPDATE
      59              : //#define DEBUG_E2_TIME_ON_DETECTOR
      60              : //#define DEBUG_E2_JAMS
      61              : //#define DEBUG_E2_XML_OUT
      62              : //#define DEBUG_COND (true)
      63              : //#define DEBUG_COND (getID()=="e2Detector_e5.601A_1_SAa")
      64              : //#define DEBUG_COND (getID()=="702057")
      65              : //#define DEBUG_COND (getID()=="det0")
      66              : 
      67         6034 : MSE2Collector::MSE2Collector(const std::string& id,
      68              :                              DetectorUsage usage, MSLane* lane, double startPos, double endPos, double length,
      69              :                              SUMOTime haltingTimeThreshold, double haltingSpeedThreshold, double jamDistThreshold,
      70              :                              const std::string name, const std::string& vTypes,
      71              :                              const std::string& nextEdges,
      72         6034 :                              int detectPersons) :
      73              :     MSMoveReminder(id, lane, false),
      74              :     MSDetectorFileOutput(id, vTypes, nextEdges, detectPersons),
      75         6034 :     myUsage(usage),
      76         6034 :     myName(name),
      77         6034 :     myJamHaltingSpeedThreshold(haltingSpeedThreshold),
      78         6034 :     myJamHaltingTimeThreshold(haltingTimeThreshold),
      79         6034 :     myJamDistanceThreshold(jamDistThreshold),
      80         6034 :     myNumberOfEnteredVehicles(0),
      81         6034 :     myNumberOfSeenVehicles(0),
      82         6034 :     myNumberOfLeftVehicles(0),
      83         6034 :     myCurrentVehicleSamples(0),
      84         6034 :     myCurrentOccupancy(0),
      85         6034 :     myCurrentMeanSpeed(0),
      86         6034 :     myCurrentMeanLength(0),
      87         6034 :     myCurrentJamNo(0),
      88         6034 :     myCurrentMaxJamLengthInMeters(0),
      89         6034 :     myCurrentJamLengthInMeters(0),
      90         6034 :     myCurrentJamLengthInVehicles(0),
      91         6034 :     myCurrentHaltingsNumber(0),
      92         6034 :     myPreviousMeanOccupancy(0),
      93         6034 :     myPreviousMeanSpeed(0),
      94         6034 :     myPreviousMeanTimeLoss(0),
      95         6034 :     myPreviousMaxJamLengthInMeters(0),
      96         6034 :     myPreviousNumberOfSeenVehicles(0),
      97         6034 :     myOverrideVehNumber(-1) {
      98         6034 :     reset();
      99              : 
     100              : #ifdef DEBUG_E2_CONSTRUCTOR
     101              :     if (DEBUG_COND) {
     102              :         std::cout << "\n" << "Creating MSE2Collector " << id
     103              :                   << " with lane = " << lane->getID()
     104              :                   << " startPos = " << startPos
     105              :                   << " endPos = " << endPos
     106              :                   << " length = " << length
     107              :                   << " haltingTimeThreshold = " << haltingTimeThreshold
     108              :                   << " haltingSpeedThreshold = " << haltingSpeedThreshold
     109              :                   << " jamDistThreshold = " << jamDistThreshold
     110              :                   << std::endl;
     111              :     }
     112              : #endif
     113              : 
     114              :     assert(lane != 0);
     115              : 
     116              :     // check that exactly one of length, startPos, endPos is invalid
     117         6034 :     bool lengthInvalid = length == std::numeric_limits<double>::max() || length <= 0;
     118         6034 :     bool endPosInvalid = endPos == std::numeric_limits<double>::max();
     119              :     bool posInvalid = startPos == std::numeric_limits<double>::max();
     120              : 
     121              :     // check and normalize positions (assure positive values for pos and endPos, snap to lane-ends)
     122         6034 :     if (lengthInvalid) {
     123              :         // assume that the detector is only located on a single lane
     124           23 :         if (posInvalid) {
     125            0 :             WRITE_WARNING(TL("No valid detector length and start position given. Assuming startPos = 0 and length = end position"));
     126              :             startPos = 0;
     127              :         }
     128           23 :         if (endPosInvalid) {
     129            2 :             WRITE_WARNING(TL("No valid detector length and end position given. Assuming endPos = lane length and length = endPos-startPos"));
     130              :             endPos = lane->getLength();
     131              :         }
     132           23 :         endPos = endPos < 0 ? lane->getLength() + endPos : endPos;
     133           23 :         startPos = startPos < 0 ? lane->getLength() + startPos : startPos;
     134           23 :         bool valid = endPos <= lane->getLength() && 0 <= startPos && startPos < endPos;
     135              :         if (!valid) {
     136            0 :             throw InvalidArgument("Error in specification for E2Detector '" + id + "'. Positional argument is malformed. 0 <= pos < endPos <= lane.getLength() is required.");
     137              :         }
     138              :         // snap detector ends to lane ends
     139           23 :         endPos = snap(endPos, lane->getLength(), POSITION_EPS);
     140           23 :         startPos = snap(startPos, 0., POSITION_EPS);
     141           23 :         length = endPos - startPos;
     142         6011 :     } else if (posInvalid) {
     143              :         // endPosInvalid == false
     144         1737 :         endPos = endPos < 0 ? lane->getLength() + endPos : endPos;
     145         1737 :         endPos = snap(endPos, lane->getLength(), POSITION_EPS);
     146              :     } else {
     147              :         // posInvalid == false
     148         4274 :         startPos = startPos < 0 ? lane->getLength() + startPos : startPos;
     149         4274 :         startPos = snap(startPos, 0., POSITION_EPS);
     150              :     }
     151              : 
     152         6034 :     myStartPos = startPos;
     153         6034 :     myEndPos = endPos;
     154              : 
     155              :     std::vector<MSLane*> lanes;
     156         6034 :     if (posInvalid) {
     157         3474 :         lanes = selectLanes(lane, length, "bw");
     158         4297 :     } else if (endPosInvalid) {
     159         8550 :         lanes = selectLanes(lane, length, "fw");
     160              :     } else {
     161              :         // assuming detector is only located at a single lane
     162           22 :         lanes.push_back(lane);
     163              :     }
     164              : 
     165         6034 :     initAuxiliaries(lanes);
     166         6034 :     checkPositioning(endPosInvalid, length);
     167         6034 :     addDetectorToLanes(lanes);
     168         6034 : }
     169              : 
     170              : 
     171          331 : MSE2Collector::MSE2Collector(const std::string& id,
     172              :                              DetectorUsage usage, std::vector<MSLane*> lanes, double startPos, double endPos,
     173              :                              SUMOTime haltingTimeThreshold, double haltingSpeedThreshold, double jamDistThreshold,
     174              :                              const std::string name, const std::string& vTypes,
     175              :                              const std::string& nextEdges,
     176          331 :                              int detectPersons) :
     177              :     MSMoveReminder(id, lanes[lanes.size() - 1], false), // assure that lanes.size() > 0 at caller side!!!
     178              :     MSDetectorFileOutput(id, vTypes, nextEdges, detectPersons),
     179          331 :     myUsage(usage),
     180          331 :     myName(name),
     181          331 :     myFirstLane(lanes[0]),
     182          331 :     myLastLane(lanes[lanes.size() - 1]),
     183          331 :     myStartPos(startPos),
     184          331 :     myEndPos(endPos),
     185          331 :     myJamHaltingSpeedThreshold(haltingSpeedThreshold),
     186          331 :     myJamHaltingTimeThreshold(haltingTimeThreshold),
     187          331 :     myJamDistanceThreshold(jamDistThreshold),
     188          331 :     myNumberOfEnteredVehicles(0),
     189          331 :     myNumberOfSeenVehicles(0),
     190          331 :     myNumberOfLeftVehicles(0),
     191          331 :     myCurrentVehicleSamples(0),
     192          331 :     myCurrentOccupancy(0),
     193          331 :     myCurrentMeanSpeed(0),
     194          331 :     myCurrentMeanLength(0),
     195          331 :     myCurrentJamNo(0),
     196          331 :     myCurrentJamLengthInMeters(0),
     197          331 :     myCurrentJamLengthInVehicles(0),
     198          331 :     myCurrentJamDuration(0),
     199          331 :     myCurrentHaltingsNumber(0),
     200          331 :     myPreviousMeanOccupancy(0),
     201          331 :     myPreviousMeanSpeed(0),
     202          331 :     myPreviousMeanTimeLoss(0),
     203          331 :     myPreviousMaxJamLengthInMeters(0),
     204          331 :     myPreviousNumberOfSeenVehicles(0),
     205          331 :     myOverrideVehNumber(-1) {
     206          331 :     reset();
     207              : 
     208         1092 :     for (std::vector<MSLane*>::const_iterator i = lanes.begin(); i != lanes.end(); ++i) {
     209              :         assert((*i) != 0);
     210              :     }
     211              : 
     212              : #ifdef DEBUG_E2_CONSTRUCTOR
     213              :     if (DEBUG_COND) {
     214              :         std::cout << "\n" << "Creating MSE2Collector " << id
     215              :                   << " with endLane = " << myLastLane->getID()
     216              :                   << " endPos = " << endPos
     217              :                   << " startLane = " << myFirstLane->getID()
     218              :                   << " startPos = " << startPos
     219              :                   << " haltingTimeThreshold = " << haltingTimeThreshold
     220              :                   << " haltingSpeedThreshold = " << haltingSpeedThreshold
     221              :                   << " jamDistThreshold = " << jamDistThreshold
     222              :                   << std::endl;
     223              :     }
     224              : #endif
     225              : 
     226          331 :     myStartPos = myStartPos < 0 ? lanes[0]->getLength() + myStartPos : myStartPos;
     227          331 :     myEndPos = myEndPos < 0 ? lanes[lanes.size() - 1]->getLength() + myEndPos : myEndPos;
     228              : 
     229          331 :     if (myStartPos < POSITION_EPS) {
     230            1 :         myStartPos = 0;
     231              :     }
     232          331 :     if (myEndPos > lanes[lanes.size() - 1]->getLength() - POSITION_EPS) {
     233            2 :         myEndPos = lanes[lanes.size() - 1]->getLength();
     234              :     }
     235              : 
     236              : 
     237          331 :     initAuxiliaries(lanes);
     238          325 :     checkPositioning();
     239          325 :     addDetectorToLanes(lanes);
     240          367 : }
     241              : 
     242              : 
     243              : void
     244         6359 : MSE2Collector::checkPositioning(bool posGiven, double desiredLength) {
     245              :     // check if detector was truncated
     246         6359 :     if (desiredLength > 0 && myDetectorLength < desiredLength - NUMERICAL_EPS) {
     247           89 :         std::stringstream ss;
     248              :         ss << "Cannot build detector of length " << desiredLength
     249           89 :            << " because no further continuation lane was found for lane '" << (posGiven ? myLastLane->getID() : myFirstLane->getID())
     250          178 :            << "'! Truncated detector at length " << myDetectorLength << ".";
     251           89 :         WRITE_WARNING(ss.str());
     252           89 :     }
     253              : 
     254         6359 :     if (myDetectorLength < POSITION_EPS && (myStartPos > 0. || myEndPos < myLastLane->getLength())) {
     255              :         // assure minimal detector length
     256            5 :         double prolong = POSITION_EPS - myDetectorLength;
     257            5 :         double startPos = MAX2(0., myStartPos - prolong); // new startPos
     258            5 :         prolong -= myStartPos - startPos;
     259            5 :         myStartPos = startPos;
     260            5 :         if (prolong > 0.) {
     261            6 :             myEndPos = MIN2(myEndPos + prolong, myLastLane->getLength());
     262              :         }
     263           25 :         WRITE_WARNING("Adjusted detector positioning to meet requirement length >= " + toString(POSITION_EPS)
     264              :                       + ". New position is [" + toString(myStartPos) + "," + toString(myEndPos) + "]");
     265              :     }
     266              : 
     267              :     // do some regularization snapping...
     268         6359 :     myStartPos = snap(myStartPos, 0., POSITION_EPS);
     269         6359 :     myStartPos = snap(myStartPos, myFirstLane->getLength() - POSITION_EPS, POSITION_EPS);
     270         6359 :     myStartPos = snap(myStartPos, 0., POSITION_EPS);
     271         6359 :     myEndPos = snap(myEndPos, myLastLane->getLength(), POSITION_EPS);
     272         6359 :     myEndPos = snap(myEndPos, POSITION_EPS, POSITION_EPS);
     273         6359 :     myEndPos = snap(myEndPos, myLastLane->getLength(), POSITION_EPS);
     274         6359 :     recalculateDetectorLength();
     275              : 
     276              : #ifdef DEBUG_E2_CONSTRUCTOR
     277              :     if (DEBUG_COND) {
     278              :         std::stringstream ss;
     279              :         //    ss << std::setprecision(32) << myEndPos << " : " << POSITION_EPS;
     280              :         //    std::cout << ss.str() << std::endl;
     281              :         std::cout << "myStartPos = " << myStartPos << std::endl;
     282              :         std::cout << "myEndPos = " << myEndPos << std::endl;
     283              :         std::cout << "myLastLane->getLength() = " << myLastLane->getLength() << std::endl;
     284              :     }
     285              : #endif
     286              : 
     287              : 
     288              :     assert((myStartPos >= POSITION_EPS  || myStartPos == 0) && myStartPos < myFirstLane->getLength());
     289              :     assert(myEndPos <= myLastLane->getLength() - POSITION_EPS || myEndPos == myLastLane->getLength());
     290              :     assert(myFirstLane != myLastLane || myEndPos - myStartPos > 0);
     291         6359 : }
     292              : 
     293              : 
     294              : double
     295        44211 : MSE2Collector::snap(double value, double snapPoint, double snapDist) {
     296        44211 :     if (fabs(value - snapPoint) < snapDist) {
     297              :         return snapPoint;
     298              :     } else {
     299        30511 :         return value;
     300              :     }
     301              : }
     302              : 
     303              : 
     304              : void
     305         6359 : MSE2Collector::recalculateDetectorLength() {
     306              :     std::vector<std::string>::const_iterator i;
     307              :     std::vector<MSLane*> lanes;
     308              :     // get real lanes
     309      2038020 :     for (i = myLanes.begin(); i != myLanes.end(); ++i) {
     310      2031661 :         MSLane* lane = MSLane::dictionary(*i);
     311      2031661 :         lanes.push_back(lane);
     312              :     }
     313              : 
     314              :     // sum up their lengths
     315              :     std::vector<MSLane*>::const_iterator j;
     316              :     MSLane* previous = nullptr;
     317         6359 :     myDetectorLength = 0;
     318      2038020 :     for (j = lanes.begin(); j != lanes.end(); ++j) {
     319              :         // lane length
     320      2031661 :         myDetectorLength += (*j)->getLength();
     321      2031661 :         if (previous != nullptr && !MSGlobals::gUsingInternalLanes) {
     322              :             // eventually link length
     323           14 :             myDetectorLength += previous->getLinkTo(*j)->getLength();
     324              :         }
     325      2031661 :         previous = *j;
     326              :     }
     327              :     // subtract uncovered area on first and last lane
     328         6359 :     myDetectorLength -= myStartPos;
     329         6359 :     myDetectorLength -= myLastLane->getLength() - myEndPos;
     330              : 
     331              : #ifdef DEBUG_E2_CONSTRUCTOR
     332              :     if (DEBUG_COND) {
     333              :         std::cout << "Total detector length after recalculation = " << myDetectorLength << std::endl;
     334              :     }
     335              : #endif
     336         6359 : }
     337              : 
     338              : 
     339        12479 : MSE2Collector::~MSE2Collector() {
     340              :     // clear move notifications
     341         6359 :     clearState(SUMOTime_MAX);
     342        31556 : }
     343              : 
     344              : 
     345              : std::vector<MSLane*>
     346         6012 : MSE2Collector::selectLanes(MSLane* lane, double length, std::string dir) {
     347              :     // direction of detector extension
     348              :     assert(dir == "fw" || dir == "bw");
     349         6012 :     bool fw = dir == "fw";
     350              :     double linkLength = 0; // linkLength (used if no internal lanes are present)
     351              :     bool subtractedLinkLength = false; // whether linkLength was subtracted during the last iteration.
     352              : 
     353              : #ifdef DEBUG_E2_CONSTRUCTOR
     354              :     if (DEBUG_COND) {
     355              :         std::cout << "\n" << "selectLanes()" << (fw ? "(forward)" : "(backward)") << std::endl;
     356              :     }
     357              : #endif
     358              :     std::vector<MSLane*> lanes;
     359              :     // Selected lanes are stacked into vector 'lanes'. If dir == "bw" lanes will be reversed after this is done.
     360              :     // The length is reduced while adding lanes to the detector
     361              :     // First we adjust the starting value for length (in the first iteration, the whole length of the first considered lane is subtracted,
     362              :     // while it might only be partially covered by the detector)
     363         6012 :     if (fw) {
     364              :         assert(myStartPos != std::numeric_limits<double>::max());
     365         4275 :         length += myStartPos;
     366              :     } else {
     367              :         assert(myEndPos != std::numeric_limits<double>::max());
     368         1737 :         length += lane->getLength() - myEndPos;
     369              :     }
     370              :     length = MAX2(POSITION_EPS, length); // this assures to add at least one lane to lanes
     371      2036513 :     while (length >= POSITION_EPS && lane != nullptr) {
     372              :         // Break loop for length <= NUMERICAL_EPS to avoid placement of very small
     373              :         // detector piece on the end or beginning of one lane due to numerical rounding errors.
     374      2030501 :         lanes.push_back(lane);
     375              : #ifdef DEBUG_E2_CONSTRUCTOR
     376              :         if (DEBUG_COND) {
     377              :             std::cout << "Added lane " << lane->getID()
     378              :                       << " (length: " << lane->getLength() << ")" << std::endl;
     379              :         }
     380              : #endif
     381              : 
     382      2030501 :         length -= lane->getLength();
     383              : 
     384              :         // proceed to upstream predecessor
     385      2030501 :         if (fw) {
     386      2027480 :             lane = lane->getCanonicalSuccessorLane();
     387              :         } else {
     388         3021 :             lane = lane->getCanonicalPredecessorLane();
     389              :         }
     390              : 
     391              : 
     392              :         subtractedLinkLength = false;
     393      2030501 :         if (lane != nullptr && !MSGlobals::gUsingInternalLanes && length > POSITION_EPS) {
     394              :             // In case wher no internal lanes are used,
     395              :             // take into account the link length for the detector range
     396              :             linkLength = 0;
     397            8 :             if (fw) {
     398            8 :                 linkLength = lanes.back()->getLinkTo(lane)->getLength();
     399              :             } else {
     400            0 :                 linkLength = lane->getLinkTo(lanes.back())->getLength();
     401              :             }
     402            8 :             length -= linkLength;
     403              :             subtractedLinkLength = true;
     404              :         }
     405              : 
     406              : 
     407              : #ifdef DEBUG_E2_CONSTRUCTOR
     408              :         if (DEBUG_COND) {
     409              :             if (lane != 0) {
     410              :                 std::cout << (fw ? "Successor lane: " : "Predecessor lane: ") << "'" << lane->getID() << "'";
     411              :             }
     412              :             std::cout << std::endl;
     413              :         }
     414              : #endif
     415              :     }
     416              : 
     417         6012 :     if (subtractedLinkLength) {
     418              :         // if the link's length was subtracted during the last step,
     419              :         // the start/endPos would lie on a non-existing internal lane,
     420              :         // therefore revert and truncate detector part on the non-existing internal lane.
     421            0 :         length += linkLength;
     422              :     }
     423              : 
     424              : 
     425              :     // 1) At this point a negative <length> means that not the whole last stored lane lanes[lanes.size()-1]
     426              :     // should be added to the detector, but the detector should spare out a part with length = -<length>
     427              :     // If this part is too small (of length < POSITION_EPS) we take the whole lane
     428              :     // 2) The whole lane is also taken for the case that <length> is positive. This corresponds to on
     429              :     // of three situations: i) <length> < POSITION_EPS (break condition -> don't take too small pieces on the next lane)
     430              :     //                 ii&iii) <length> >= POS_EPSILON may arise either if no continuation lane was found (lane==0), or
     431              :     //                         in case of not using internal lanes if the detector end/start falls on a link.
     432              :     // In all cases we take the whole last lane.
     433         6012 :     if (fw) {
     434         4275 :         if (length > -POSITION_EPS) {
     435         2188 :             myEndPos = lanes[lanes.size() - 1]->getLength();
     436         2087 :         } else if (length < 0) {
     437         2087 :             myEndPos = lanes[lanes.size() - 1]->getLength() + length;
     438              :         }
     439              :     } else {
     440         1737 :         if (length > -POSITION_EPS) {
     441          527 :             myStartPos = 0;
     442         1210 :         } else if (length < 0) {
     443         1210 :             myStartPos = -length;
     444              :         }
     445              :     }
     446              : 
     447              :     // reverse lanes if lane selection was backwards
     448         6012 :     if (!fw) {
     449              :         std::reverse(lanes.begin(), lanes.end());
     450              :     }
     451              : 
     452         6012 :     return lanes;
     453            0 : }
     454              : 
     455              : void
     456         6359 : MSE2Collector::addDetectorToLanes(std::vector<MSLane*>& lanes) {
     457              : #ifdef DEBUG_E2_CONSTRUCTOR
     458              :     if (DEBUG_COND) {
     459              :         std::cout << "\n" << "Adding detector " << myID << " to lanes:" << std::endl;
     460              :     }
     461              : #endif
     462      2038020 :     for (std::vector<MSLane*>::iterator l = lanes.begin(); l != lanes.end(); ++l) {
     463      2031661 :         (*l)->addMoveReminder(this);
     464              : #ifdef DEBUG_E2_CONSTRUCTOR
     465              :         if (DEBUG_COND) {
     466              :             std::cout << (*l)->getID() << std::endl;
     467              :         }
     468              : #endif
     469              :     }
     470         6359 : }
     471              : 
     472              : void
     473         6365 : MSE2Collector::initAuxiliaries(std::vector<MSLane*>& lanes) {
     474              :     // Checks integrity of myLanes, adds internal-lane information, inits myLength, myFirstLane, myLastLane, myOffsets, myEndPos/myStartPos
     475         6365 :     myFirstLane = lanes[0];
     476         6365 :     myLastLane = lanes[lanes.size() - 1];
     477              : 
     478              : #ifdef DEBUG_E2_CONSTRUCTOR
     479              :     if (DEBUG_COND) {
     480              :         std::cout << "\n" << "Initializing auxiliaries:"
     481              :                   << "\nFirst lane: " << myFirstLane->getID() << " (startPos = " << myStartPos << ")"
     482              :                   << "\nLast lane: " << myLastLane->getID() << " (endPos = " << myEndPos << ")"
     483              :                   << std::endl;
     484              :     }
     485              : #endif
     486              : 
     487              :     // Init myOffsets and myDetectorLength.
     488              :     // The loop below runs through the given lanes assuming the possibility that only non-internal lanes are given
     489              :     // or at least not all relevant internal lanes are considered. During this a new, complete list of lane ids is
     490              :     // built into myLanes.
     491              :     myLanes.clear();
     492              : 
     493              :     // myDetectorLength will be increased in the loop below, always giving
     494              :     // the offset of the currently considered lane to the detector start
     495         6365 :     myDetectorLength = -myStartPos;
     496              :     myOffsets.clear();
     497              : 
     498              :     // loop over detector lanes and accumulate offsets with respect to the first lane's begin
     499              :     // (these will be corrected afterwards by subtracting the start position.)
     500              :     std::vector<MSLane*>::iterator il = lanes.begin();
     501              : 
     502              :     // start on an internal lane?
     503              :     // (This may happen if specifying the detector by its upstream
     504              :     //  length starting from a given end position)
     505         6365 :     const MSLane* internal = (*il)->isInternal() ? *il : nullptr;
     506              : 
     507              : #ifdef DEBUG_E2_CONSTRUCTOR
     508              :     if (DEBUG_COND) {
     509              :         std::cout << "\n" << "Initializing offsets:" << std::endl;
     510              :     }
     511              : #endif
     512              : 
     513              :     while (true) {
     514              :         // Consider the next internal lanes
     515      2030483 :         while (internal != nullptr) {
     516         1193 :             myLanes.push_back(internal->getID());
     517         1193 :             myOffsets.push_back(myDetectorLength);
     518              : 
     519              : #ifdef DEBUG_E2_CONSTRUCTOR
     520              :             if (DEBUG_COND) {
     521              :                 std::cout << "Offset for lane " << internal->getID() << " = " << myDetectorLength
     522              :                           << std::endl;
     523              :             }
     524              : #endif
     525              : 
     526         1193 :             myDetectorLength += internal->getLength();
     527         1193 :             if (internal->getID() == myLastLane->getID()) {
     528              :                 break;
     529              :             }
     530              : 
     531              :             // There should be a unique continuation for each internal lane
     532              :             assert(internal->getLinkCont().size() == 1);
     533              : 
     534         1193 :             internal = internal->getLinkCont()[0]->getViaLaneOrLane();
     535         1193 :             if (!internal->isInternal()) {
     536              :                 // passed the junction
     537              :                 internal = nullptr;
     538              :                 break;
     539              :             }
     540              :         }
     541              : 
     542              :         // Consider the next non-internal lane
     543              :         // This is the first lane in the first iteration, if it is non-internal
     544              :         // However, it can equal myLanes.end() if the myLastLane is internal. In that case we break.
     545              : 
     546              :         // Move il to next non-internal
     547      2031274 :         while (il != lanes.end() && (*il)->isInternal()) {
     548              :             il++;
     549              :         }
     550      2030474 :         if (il == lanes.end()) {
     551              :             break;
     552              :         }
     553              : 
     554              :         // There is still a non-internal lane to consider
     555      2030474 :         MSLane* lane = *il;
     556      2030474 :         myLanes.push_back(lane->getID());
     557              : 
     558              : #ifdef DEBUG_E2_CONSTRUCTOR
     559              :         if (DEBUG_COND) {
     560              :             std::cout << "Offset for lane " << lane->getID() << " = " << myDetectorLength
     561              :                       << std::endl;
     562              :         }
     563              : #endif
     564              : 
     565              :         // Offset to detector start for this lane
     566      2030474 :         myOffsets.push_back(myDetectorLength);
     567              : 
     568              :         // Add the lanes length to the detector offset
     569      2030474 :         myDetectorLength += lane->getLength();
     570              : 
     571              :         // Get the next lane if this lane isn't the last one
     572      2030474 :         if (++il == lanes.end()) {
     573              :             break;
     574              :         }
     575              : 
     576      2024115 :         if ((*il)->isInternal()) {
     577              :             // next lane in myLanes is internal
     578          536 :             internal = *il;
     579          536 :             continue;
     580              :         }
     581              : 
     582              :         // find the connection to next
     583      2023579 :         const MSLink* link = lane->getLinkTo(*il);
     584      2023579 :         if (link == nullptr) {
     585           18 :             throw InvalidArgument("Lanes '" + lane->getID() + "' and '" + (*il)->getID() + "' are not consecutive in definition of e2Detector '" + getID() + "'");
     586              :         }
     587              : 
     588      2023573 :         if (!MSGlobals::gUsingInternalLanes) {
     589           14 :             myDetectorLength += link->getLength();
     590              :         } else {
     591              :             internal = link->getViaLane();
     592              :         }
     593              :     }
     594              : 
     595              :     // Subtract distance not covered on the last considered lane
     596         6359 :     bool fw = myEndPos == std::numeric_limits<double>::max();
     597         6359 :     if (fw) {
     598            0 :         myDetectorLength -= myStartPos;
     599              :     } else {
     600         6359 :         myDetectorLength -= myLastLane->getLength() - myEndPos;
     601              :     }
     602              : 
     603              : #ifdef DEBUG_E2_CONSTRUCTOR
     604              :     if (DEBUG_COND) {
     605              :         std::cout << "Total detector length after initAuxiliaries() = " << myDetectorLength << std::endl;
     606              :     }
     607              : #endif
     608              : 
     609              : // make lanes a complete list including internal lanes
     610         6359 :     lanes = getLanes();
     611         6359 : }
     612              : 
     613              : 
     614              : std::vector<MSLane*>
     615       100606 : MSE2Collector::getLanes() {
     616              :     std::vector<MSLane*> res;
     617      2726600 :     for (std::vector<std::string>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
     618      2625994 :         res.push_back(MSLane::dictionary(*i));
     619              :     }
     620       100606 :     return res;
     621            0 : }
     622              : 
     623              : 
     624              : bool
     625    105294318 : MSE2Collector::notifyMove(SUMOTrafficObject& veh, double oldPos,
     626              :                           double newPos, double newSpeed) {
     627              : 
     628    105294318 :     if (myDetectPersons > (int)PersonMode::WALK && !veh.isPerson()) {
     629              :         bool keep = false;
     630          270 :         MSBaseVehicle& v = dynamic_cast<MSBaseVehicle&>(veh);
     631          540 :         for (MSTransportable* p : v.getPersons()) {
     632          270 :             keep = notifyMove(*p, oldPos, newPos, newSpeed);
     633              :         }
     634          270 :         return keep;
     635              :     }
     636              : #ifdef HAVE_FOX
     637    105294048 :     ScopedLocker<> lock(myNotificationMutex, MSGlobals::gNumSimThreads > 1);
     638              : #endif
     639              :     VehicleInfoMap::iterator vi = myVehicleInfos.find(veh.getID());
     640    105294048 :     if (vi == myVehicleInfos.end()) {
     641            0 :         const std::string objectType = veh.isPerson() ? "Person" : "Vehicle";
     642            0 :         if (myNextEdges.size() > 0) {
     643            0 :             WRITE_WARNING(objectType + " '" + veh.getID() + "' appeared inside detector '" + getID() + "' after previously being filtered out. time=" + time2string(SIMSTEP) + ".");
     644              :         } else {
     645            0 :             WRITE_WARNING(objectType + " '" + veh.getID() + "' suddenly appeared inside detector '" + getID() + "'. time=" + time2string(SIMSTEP) + ".");
     646              :         }
     647              :         return false;
     648              :     }
     649              : 
     650              :     const std::string& vehID = veh.getID();
     651    105294048 :     VehicleInfo& vehInfo = *(vi->second);
     652              : 
     653              :     // position relative to the detector start
     654    105294048 :     double relPos = vehInfo.entryOffset + newPos;
     655              : 
     656              :     // update current distance to the detector end
     657    105294048 :     vehInfo.distToDetectorEnd = myDetectorLength - relPos;
     658              : 
     659              : #ifdef DEBUG_E2_NOTIFY_MOVE
     660              :     if (DEBUG_COND) {
     661              :         std::cout << "\n" << SIMTIME
     662              :                   << " MSE2Collector::notifyMove() (detID = " << myID << " on lane '" << myLane->getID() << "')"
     663              :                   << " called by vehicle '" << vehID << "'"
     664              :                   << " at relative position " << relPos
     665              :                   << ", distToDetectorEnd = " << vehInfo.distToDetectorEnd << std::endl;
     666              :     }
     667              : #endif
     668              : 
     669              :     // Check whether vehicle has reached the detector begin
     670    105294048 :     if (relPos <= 0) {
     671              :         // detector not yet reached, request being informed further
     672              : #ifdef DEBUG_E2_NOTIFY_MOVE
     673              :         if (DEBUG_COND) {
     674              :             std::cout << "Vehicle has not yet reached the detector start position." << std::endl;
     675              :         }
     676              : #endif
     677              :         return true;
     678     47370580 :     } else if (!vehInfo.hasEntered) {
     679       595569 :         vehInfo.hasEntered = true;
     680       595569 :         myNumberOfEnteredVehicles++;
     681       595569 :         myNumberOfSeenVehicles++;
     682              :     }
     683              : 
     684              : 
     685              :     // determine whether vehicle has moved beyond the detector's end
     686     47370580 :     bool vehPassedDetectorEnd = - vehInfo.exitOffset <= newPos - veh.getVehicleType().getLength();
     687              : 
     688              :     // determine whether vehicle has been on the detector at all
     689     47370580 :     bool vehicleEnteredLaneAfterDetector = vehPassedDetectorEnd && (-vehInfo.exitOffset <= oldPos - veh.getVehicleType().getLength());
     690              :     // ... if not, dont create any notification at all
     691              :     if (vehicleEnteredLaneAfterDetector) {
     692              : #ifdef DEBUG_E2_NOTIFY_MOVE
     693              :         if (DEBUG_COND) {
     694              :             std::cout << "Vehicle entered lane behind detector." << std::endl;
     695              :         }
     696              : #endif
     697              :     } else {
     698     47370516 :         myMoveNotifications.push_back(makeMoveNotification(veh, oldPos, newPos, newSpeed, vehInfo));
     699              :     }
     700              : 
     701              : 
     702     47370580 :     if (vehPassedDetectorEnd) {
     703              : #ifdef DEBUG_E2_NOTIFY_MOVE
     704              :         if (DEBUG_COND) {
     705              :             std::cout << "Vehicle has left the detector longitudinally." << std::endl;
     706              :         }
     707              : #endif
     708              :         // Vehicle is beyond the detector, unsubscribe and register removal from myVehicleInfos
     709              :         myLeftVehicles.insert(vehID);
     710              :         return false;
     711              :     } else {
     712              :         // Receive further notifications
     713              :         return true;
     714              :     }
     715              : }
     716              : 
     717              : bool
     718       785988 : MSE2Collector::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/, MSMoveReminder::Notification reason, const MSLane* enteredLane) {
     719              : #ifdef DEBUG_E2_NOTIFY_ENTER_AND_LEAVE
     720              :     if (DEBUG_COND) {
     721              :         std::cout << "\n" << SIMTIME << " notifyLeave() (detID = " << myID << " on lane '" << myLane->getID() << "')"
     722              :                   << "called by vehicle '" << veh.getID() << "'" << std::endl;
     723              :     }
     724              : #endif
     725              : 
     726              : #ifdef HAVE_FOX
     727       785988 :     ScopedLocker<> lock(myNotificationMutex, MSGlobals::gNumSimThreads > 1);
     728              : #endif
     729       785988 :     if (reason == MSMoveReminder::NOTIFICATION_JUNCTION && !veh.isPerson()) {
     730              :         // vehicle left lane via junction, unsubscription and registering in myLeftVehicles when
     731              :         // moving beyond the detector end is controlled in notifyMove.
     732              : #ifdef DEBUG_E2_NOTIFY_ENTER_AND_LEAVE
     733              :         if (DEBUG_COND) {
     734              :             std::cout << SIMTIME << " Left longitudinally (along junction) -> keep subscription [handle exit in notifyMove()]" << std::endl;
     735              :         }
     736              : #endif
     737              : 
     738       778743 :         if (enteredLane == nullptr || std::find(myLanes.begin(), myLanes.end(), enteredLane->getID()) == myLanes.end()) {
     739              :             // Entered lane is not part of the detector
     740              :             VehicleInfoMap::iterator vi = myVehicleInfos.find(veh.getID());
     741              :             // Determine exit offset, where vehicle left the detector
     742       712947 :             double exitOffset = vi->second->entryOffset - myOffsets[vi->second->currentOffsetIndex] - vi->second->currentLane->getLength();
     743      1424622 :             vi->second->exitOffset = MAX2(vi->second->exitOffset, exitOffset);
     744              : #ifdef DEBUG_E2_NOTIFY_ENTER_AND_LEAVE
     745              :             if (DEBUG_COND) {
     746              :                 std::cout << SIMTIME << " Vehicle '" << veh.getID() << "' leaves the detector. Exit offset = " << vi->second->exitOffset << std::endl;
     747              :             }
     748              : #endif
     749              :         }
     750              : 
     751       778743 :         return true;
     752              :     } else {
     753              :         VehicleInfoMap::iterator vi = myVehicleInfos.find(veh.getID());
     754         7245 :         if (vi != myVehicleInfos.end()) {
     755              :             // erase vehicle, which leaves in a non-longitudinal way, immediately
     756         5975 :             if (vi->second->hasEntered) {
     757         1482 :                 myNumberOfLeftVehicles++;
     758              :             }
     759         5975 :             delete vi->second;
     760              :             myVehicleInfos.erase(vi);
     761              : #ifdef DEBUG_E2_NOTIFY_ENTER_AND_LEAVE
     762              :             if (DEBUG_COND) {
     763              :                 std::cout << SIMTIME << " Left non-longitudinally (lanechange, teleport, parking, etc) -> discard subscription" << std::endl;
     764              :             }
     765              : #endif
     766              :         } else {
     767              :             assert(veh.isPerson());
     768              :         }
     769              :         return false;
     770              :     }
     771              : }
     772              : 
     773              : 
     774              : bool
     775       671818 : MSE2Collector::notifyEnter(SUMOTrafficObject& veh, MSMoveReminder::Notification reason, const MSLane* enteredLane) {
     776              : #ifdef DEBUG_E2_NOTIFY_ENTER_AND_LEAVE
     777              :     if (DEBUG_COND) {
     778              :         std::cout << std::endl << SIMTIME << " notifyEnter() (detID = " << myID << " on lane '" << myLane->getID() << "')"
     779              :                   << " called by vehicle '" << veh.getID()
     780              :                   << "' entering lane '" << (enteredLane != 0 ? enteredLane->getID() : "NULL") << "'" << std::endl;
     781              :     }
     782              : #endif
     783              :     // notifyEnter() should only be called for lanes of the detector
     784              :     assert(std::find(myLanes.begin(), myLanes.end(), enteredLane->getID()) != myLanes.end());
     785              :     assert(veh.getLane() == enteredLane || !veh.isVehicle());
     786              : 
     787              :     // vehicles must be kept if the "inductionloop" wants to detect passeengers
     788       671818 :     if (!vehicleApplies(veh) && (veh.isPerson() || myDetectPersons <= (int)PersonMode::WALK)) {
     789              :         // That's not my type...
     790              :         return false;
     791              :     }
     792       666699 :     if (myDetectPersons > (int)PersonMode::WALK && !veh.isPerson()) {
     793              :         bool keep = false;
     794           40 :         MSBaseVehicle& v = dynamic_cast<MSBaseVehicle&>(veh);
     795           80 :         for (MSTransportable* p : v.getPersons()) {
     796           40 :             keep = notifyEnter(*p, reason, enteredLane);
     797              :         }
     798           40 :         return keep;
     799              :     }
     800              : 
     801              :     // determine whether the vehicle entered the lane behind the detector end
     802              :     // e.g. due to lane change manoeuver
     803       666659 :     if (reason != NOTIFICATION_JUNCTION) {
     804       386244 :         const double vehBackPos = veh.getBackPositionOnLane(enteredLane);
     805       386244 :         bool vehEnteredBehindDetectorEnd = (enteredLane == myLastLane) && myEndPos <= vehBackPos;
     806              :         if (vehEnteredBehindDetectorEnd) {
     807              :             // this vehicle cannot influence detector readings, do not subscribe
     808              :             // to move notifications
     809              : #ifdef DEBUG_E2_NOTIFY_ENTER_AND_LEAVE
     810              :             if (DEBUG_COND) {
     811              :                 std::cout << "Vehicle entered the lane behind the detector, ignoring it." << std::endl;
     812              :                 std::cout << "(myEndPos = " << this->myEndPos << ", veh.getBackPositionOnLane() = " << vehBackPos << ")" << std::endl;
     813              :             }
     814              : #endif
     815              :             return false;
     816              :         }
     817              :     }
     818              : 
     819              : #ifdef DEBUG_E2_NOTIFY_ENTER_AND_LEAVE
     820              :     if (DEBUG_COND) {
     821              :         if (veh.isVehicle() && !dynamic_cast<SUMOVehicle&>(veh).isOnRoad()) {
     822              :             // Vehicle is teleporting over the edge
     823              :             std::cout << "Vehicle is off road (teleporting over edge)..." << std::endl;
     824              :         }
     825              :     }
     826              : #endif
     827              : 
     828              : #ifdef HAVE_FOX
     829       666449 :     ScopedLocker<> lock(myNotificationMutex, MSGlobals::gNumSimThreads > 1);
     830              : #endif
     831              :     const std::string& vehID = veh.getID();
     832              :     VehicleInfoMap::iterator vi = myVehicleInfos.find(vehID);
     833       666449 :     if (vi != myVehicleInfos.end()) {
     834              :         // Register move current offset to the next lane
     835        65805 :         if (vi->second->currentLane != enteredLane) {
     836        65796 :             vi->second->currentOffsetIndex++;
     837        65796 :             vi->second->currentLane = enteredLane;
     838              :         }
     839              : 
     840              : #ifdef DEBUG_E2_NOTIFY_ENTER_AND_LEAVE
     841              :         if (DEBUG_COND) {
     842              :             std::cout << SIMTIME << " Vehicle '" << veh.getID() << "' on lane '" << veh.getLane()->getID()
     843              :                       << "' already known. No new VehicleInfo is created.\n"
     844              :                       << "enteredLane = " << enteredLane->getID() << "\nmyLanes[vi->offset] = " << myLanes[vi->second->currentOffsetIndex]
     845              :                       << std::endl;
     846              :         }
     847              : #endif
     848              :         assert(myLanes[vi->second->currentOffsetIndex] == enteredLane->getID());
     849              : 
     850              :         // but don't add a second subscription for another lane
     851        65805 :         return false;
     852              :     }
     853              : 
     854              : 
     855              : 
     856              : #ifdef DEBUG_E2_NOTIFY_ENTER_AND_LEAVE
     857              :     if (DEBUG_COND) {
     858              :         std::cout << SIMTIME << " Adding VehicleInfo for vehicle '" << veh.getID() << "'." << std::endl;
     859              :     }
     860              : #endif
     861              : 
     862              :     // Add vehicle info
     863      1201288 :     myVehicleInfos.insert(std::make_pair(vehID, makeVehicleInfo(veh, enteredLane)));
     864              :     // Subscribe to vehicle's movement notifications
     865       600644 :     return true;
     866              : }
     867              : 
     868              : 
     869              : MSE2Collector::VehicleInfo*
     870       600644 : MSE2Collector::makeVehicleInfo(const SUMOTrafficObject& veh, const MSLane* enteredLane) const {
     871              :     // The vehicle's distance to the detector end
     872       600644 :     int j = (int)(std::find(myLanes.begin(), myLanes.end(), enteredLane->getID()) - myLanes.begin());
     873              :     assert(j >= 0 && j < (int)myLanes.size());
     874       600644 :     double entryOffset = myOffsets[j];
     875       600644 :     double distToDetectorEnd = myDetectorLength - (entryOffset + veh.getPositionOnLane());
     876       600644 :     bool onDetector = -entryOffset < veh.getPositionOnLane() && distToDetectorEnd > -veh.getVehicleType().getLength();
     877              : 
     878              : #ifdef DEBUG_E2_MAKE_VEHINFO
     879              :     if (DEBUG_COND) {
     880              :         std::cout << SIMTIME << " Making VehicleInfo for vehicle '" << veh.getID() << "'."
     881              :                   << "\ndistToDetectorEnd = " << distToDetectorEnd
     882              :                   << "\nveh.getPositionOnLane() = " << veh.getPositionOnLane()
     883              :                   << "\nentry lane offset (lane begin from detector begin) = " << entryOffset
     884              :                   << std::endl;
     885              :     }
     886              : #endif
     887       600644 :     return new VehicleInfo(veh.getID(), veh.getVehicleType().getID(), veh.getVehicleType().getLength(), veh.getVehicleType().getMinGap(), enteredLane, entryOffset, j,
     888      1801932 :                            myOffsets[j] - myDetectorLength, distToDetectorEnd, onDetector);
     889              : }
     890              : 
     891              : 
     892              : void
     893        74344 : MSE2Collector::notifyMovePerson(MSTransportable* p, int dir, double pos) {
     894        74344 :     if (personApplies(*p, dir)) {
     895        53898 :         const double newSpeed = p->getSpeed();
     896        53898 :         const double newPos = (dir == MSPModel::FORWARD
     897        53898 :                                ? pos
     898              :                                // position relative to detector end position
     899        14202 :                                : myEndPos - (pos - myEndPos));
     900        53898 :         const double oldPos = newPos - SPEED2DIST(newSpeed);
     901        53898 :         if (oldPos - p->getVehicleType().getLength() <= myEndPos) {
     902        31976 :             notifyMove(*p, oldPos, newPos, newSpeed);
     903              :         }
     904              :     }
     905        74344 : }
     906              : 
     907              : 
     908              : void
     909     14923500 : MSE2Collector::detectorUpdate(const SUMOTime /* step */) {
     910              : 
     911     14923500 :     if (myDetectPersons != (int)PersonMode::NONE) {
     912        93291 :         if (myLanes.size() > 1) {
     913              :             /// code is more complicated because we have to make persons with
     914              :             //dir=BACKWARD send a virtual forward-lane-sequence
     915           18 :             throw ProcessError(TL("Multi-lane e2Detector does not support detecting persons yet"));
     916              :         }
     917       186564 :         for (MSLane* lane : getLanes()) {
     918        93282 :             if (lane->hasPedestrians()) {
     919       114648 :                 for (MSTransportable* p : myLane->getEdge().getPersons()) {
     920       102816 :                     if (p->getLane() == lane && vehicleApplies(*p)) {
     921        74344 :                         notifyMovePerson(p, p->getDirection(), p->getPositionOnLane());
     922              :                     }
     923              :                 }
     924              :             }
     925        93282 :         }
     926              :     }
     927              : 
     928              : #ifdef DEBUG_E2_DETECTOR_UPDATE
     929              :     if (DEBUG_COND) {
     930              :         std::cout << "\n" << SIMTIME << " detectorUpdate() for detector '" << myID << "'"
     931              :                   << "\nmyCurrentMeanSpeed = " << myCurrentMeanSpeed
     932              :                   << "\nmyCurrentMeanLength = " << myCurrentMeanLength
     933              :                   << "\nmyNumberOfEnteredVehicles = " << myNumberOfEnteredVehicles
     934              :                   << "\nmyNumberOfLeftVehicles = " << myNumberOfLeftVehicles
     935              :                   << "\nmyNumberOfSeenVehicles = " << myNumberOfSeenVehicles
     936              :                   << std::endl;
     937              :     }
     938              : #endif
     939              : 
     940              : // sort myMoveNotifications (required for jam processing) ascendingly according to vehicle's distance to the detector end
     941              : // (min = myMoveNotifications[0].distToDetectorEnd)
     942     14923491 :     std::sort(myMoveNotifications.begin(), myMoveNotifications.end(), compareMoveNotification);
     943              : 
     944              :     // reset values concerning current time step (these are updated in integrateMoveNotification() and aggregateOutputValues())
     945     14923491 :     myCurrentVehicleSamples = 0;
     946     14923491 :     myCurrentMeanSpeed = 0;
     947     14923491 :     myCurrentMeanLength = 0;
     948     14923491 :     myCurrentStartedHalts = 0;
     949     14923491 :     myCurrentHaltingsNumber = 0;
     950              : 
     951     14923491 :     JamInfo* currentJam = nullptr;
     952              :     std::vector<JamInfo*> jams;
     953              :     std::map<std::string, SUMOTime> haltingVehicles;
     954              :     std::map<std::string, SUMOTime> intervalHaltingVehicles;
     955              : 
     956              :     // go through the list of vehicles positioned on the detector
     957     62294007 :     for (std::vector<MoveNotificationInfo*>::iterator i = myMoveNotifications.begin(); i != myMoveNotifications.end(); ++i) {
     958              :         // The ID of the vehicle that has sent this notification in the last step
     959     47370516 :         const std::string& vehID = (*i)->id;
     960              :         VehicleInfoMap::iterator vi = myVehicleInfos.find(vehID);
     961              : 
     962     47370516 :         if (vi == myVehicleInfos.end()) {
     963              :             // The vehicle has already left the detector by lanechange, teleport, etc. (not longitudinal)
     964         1398 :             integrateMoveNotification(nullptr, *i);
     965              :         } else {
     966              :             // Add move notification infos to detector values and VehicleInfo
     967     47369118 :             integrateMoveNotification(vi->second, *i);
     968              :         }
     969              :         // construct jam structure
     970     47370516 :         bool isInJam = checkJam(i, haltingVehicles, intervalHaltingVehicles);
     971     47370516 :         buildJam(isInJam, i, currentJam, jams);
     972              :     }
     973              : 
     974              :     // extract some aggregated values from the jam structure
     975     14923491 :     processJams(jams, currentJam);
     976              : 
     977              :     // Aggregate and normalize values for the detector output
     978     14923491 :     aggregateOutputValues();
     979              : 
     980              :     // save information about halting vehicles
     981              :     myHaltingVehicleDurations = haltingVehicles;
     982              :     myIntervalHaltingVehicleDurations = intervalHaltingVehicles;
     983              : 
     984              : #ifdef DEBUG_E2_DETECTOR_UPDATE
     985              :     if (DEBUG_COND) {
     986              :         std::cout << "\n" << SIMTIME << " Current lanes for vehicles still on or approaching the detector:" << std::endl;
     987              :     }
     988              : #endif
     989              : // update current and entered lanes for remaining vehicles
     990              :     VehicleInfoMap::iterator iv;
     991              :     for (iv = myVehicleInfos.begin(); iv != myVehicleInfos.end(); ++iv) {
     992              : #ifdef DEBUG_E2_DETECTOR_UPDATE
     993              :         if (DEBUG_COND) {
     994              :             std::cout << " Vehicle '" << iv->second->id << "'" << ": '"
     995              :                       << iv->second->currentLane->getID() << "'"
     996              :                       << std::endl;
     997              :         }
     998              : #endif
     999              :     }
    1000              : 
    1001              : #ifdef DEBUG_E2_DETECTOR_UPDATE
    1002              :     if (DEBUG_COND) {
    1003              :         std::cout << SIMTIME << " Discarding vehicles that have left the detector:" << std::endl;
    1004              :     }
    1005              : #endif
    1006              : // Remove the vehicles that have left the detector
    1007              :     std::set<std::string>::const_iterator i;
    1008     15513863 :     for (i = myLeftVehicles.begin(); i != myLeftVehicles.end(); ++i) {
    1009              :         VehicleInfoMap::iterator j = myVehicleInfos.find(*i);
    1010       590372 :         delete j->second;
    1011              :         myVehicleInfos.erase(*i);
    1012       590372 :         myNumberOfLeftVehicles++;
    1013              : #ifdef DEBUG_E2_DETECTOR_UPDATE
    1014              :         if (DEBUG_COND) {
    1015              :             std::cout << "Erased vehicle '" << *i << "'" << std::endl;
    1016              :         }
    1017              : #endif
    1018              :     }
    1019              :     myLeftVehicles.clear();
    1020              : 
    1021              :     // reset move notifications
    1022     62294007 :     for (std::vector<MoveNotificationInfo*>::iterator j = myMoveNotifications.begin(); j != myMoveNotifications.end(); ++j) {
    1023     47370516 :         delete *j;
    1024              :     }
    1025              :     myMoveNotifications.clear();
    1026     14923491 : }
    1027              : 
    1028              : 
    1029              : void
    1030     14923491 : MSE2Collector::aggregateOutputValues() {
    1031     14923491 :     myTimeSamples += 1;
    1032              :     // compute occupancy values (note myCurrentMeanLength is still not normalized here, but holds the sum of all vehicle lengths)
    1033     14923491 :     const double currentOccupancy = myCurrentMeanLength / myDetectorLength * (double) 100.;
    1034     14923491 :     myCurrentOccupancy = currentOccupancy;
    1035     14923491 :     myOccupancySum += currentOccupancy;
    1036     14923491 :     myMaxOccupancy = MAX2(myMaxOccupancy, currentOccupancy);
    1037              :     // compute jam values
    1038     14923491 :     myMeanMaxJamInVehicles += myCurrentMaxJamLengthInVehicles;
    1039     14923491 :     myMeanMaxJamInMeters += myCurrentMaxJamLengthInMeters;
    1040     14923491 :     myMaxJamInVehicles = MAX2(myMaxJamInVehicles, myCurrentMaxJamLengthInVehicles);
    1041     28630960 :     myMaxJamInMeters = MAX2(myMaxJamInMeters, myCurrentMaxJamLengthInMeters);
    1042              :     // compute information about vehicle numbers
    1043     14923491 :     const int numVehicles = (int)myMoveNotifications.size();
    1044     14923491 :     myMeanVehicleNumber += numVehicles;
    1045     14923491 :     myMaxVehicleNumber = MAX2(numVehicles, myMaxVehicleNumber);
    1046              :     // norm current values
    1047     14923491 :     myCurrentMeanSpeed = numVehicles != 0 ? myCurrentMeanSpeed / myCurrentVehicleSamples : -1;
    1048     14923491 :     myCurrentMeanLength = numVehicles != 0 ? myCurrentMeanLength / (double) numVehicles : -1;
    1049     14923491 : }
    1050              : 
    1051              : 
    1052              : 
    1053              : void
    1054     47370516 : MSE2Collector::integrateMoveNotification(VehicleInfo* vi, const MoveNotificationInfo* mni) {
    1055              : 
    1056              : #ifdef DEBUG_E2_DETECTOR_UPDATE
    1057              :     if (DEBUG_COND) {
    1058              :         std::cout << SIMTIME << " integrateMoveNotification() for vehicle '" << mni->id << "'"
    1059              :                   << "\ntimeOnDetector = " << mni->timeOnDetector
    1060              :                   << "\nlengthOnDetector = " << mni->lengthOnDetector
    1061              :                   << "\ntimeLoss = " << mni->timeLoss
    1062              :                   << "\nspeed = " << mni->speed
    1063              :                   << std::endl;
    1064              :     }
    1065              : #endif
    1066              : 
    1067              : // Accumulate detector values
    1068     47370516 :     myVehicleSamples += mni->timeOnDetector;
    1069     47370516 :     myTotalTimeLoss += mni->timeLoss;
    1070     47370516 :     mySpeedSum += mni->speed * mni->timeOnDetector;
    1071     47370516 :     myCurrentVehicleSamples += mni->timeOnDetector;
    1072     47370516 :     myCurrentMeanSpeed += mni->speed * mni->timeOnDetector;
    1073     47370516 :     myCurrentMeanLength += mni->lengthOnDetector;
    1074              : 
    1075     47370516 :     if (vi != nullptr) {
    1076              :         // Accumulate individual values for the vehicle.
    1077              :         // @note vi==0 occurs, if the vehicle info has been erased at
    1078              :         //       notifyLeave() in case of a non-longitudinal exit (lanechange, teleport, etc.)
    1079     47369118 :         vi->totalTimeOnDetector += mni->timeOnDetector;
    1080     47369118 :         vi->accumulatedTimeLoss += mni->timeLoss;
    1081     47369118 :         vi->lastAccel = mni->accel;
    1082     47369118 :         vi->lastSpeed = mni->speed;
    1083     47369118 :         vi->lastPos = myStartPos + vi->entryOffset + mni->newPos;
    1084     47369118 :         vi->onDetector = mni->onDetector;
    1085              :     }
    1086     47370516 : }
    1087              : 
    1088              : 
    1089              : 
    1090              : MSE2Collector::MoveNotificationInfo*
    1091     47370516 : MSE2Collector::makeMoveNotification(const SUMOTrafficObject& veh, double oldPos, double newPos, double newSpeed, const VehicleInfo& vehInfo) const {
    1092              : #ifdef DEBUG_E2_NOTIFY_MOVE
    1093              :     if (DEBUG_COND) {
    1094              :         std::cout << SIMTIME << " makeMoveNotification() for vehicle '" << veh.getID() << "'"
    1095              :                   << " oldPos = " << oldPos << " newPos = " << newPos << " newSpeed = " << newSpeed
    1096              :                   << std::endl;
    1097              :     }
    1098              : #endif
    1099              : 
    1100              :     // Timefraction in [0,TS] the vehicle has spend on the detector in the last step
    1101              :     double timeOnDetector;
    1102              :     // Note that at this point, vehInfo.currentLane points to the lane at the beginning of the last timestep,
    1103              :     // and vehInfo.enteredLanes is a list of lanes entered in the last timestep
    1104              :     double timeLoss;
    1105     47370516 :     calculateTimeLossAndTimeOnDetector(veh, oldPos, newPos, vehInfo, timeOnDetector, timeLoss);
    1106              : 
    1107              :     // The length of the part of the vehicle on the detector at the end of the last time step
    1108              :     // may be shorter than vehicle's length if its back reaches out
    1109     47370516 :     double lengthOnDetector = MAX2(MIN2(vehInfo.length, newPos + vehInfo.entryOffset), 0.);
    1110              : 
    1111              :     // XXX: Fulfulling the specifications of the documentation (lengthOnDetector = time integral
    1112              :     //      over length of the vehicle's part on the detector) would be much more cumbersome.
    1113     47370516 :     double distToExit = -vehInfo.exitOffset - newPos;
    1114              :     // Eventually decrease further to account for the front reaching out
    1115     47370516 :     lengthOnDetector = MAX2(0., lengthOnDetector + MIN2(0., distToExit));
    1116              : 
    1117              :     // whether the vehicle is still on the detector at the end of the time step
    1118     47370516 :     bool stillOnDetector = -distToExit < vehInfo.length;
    1119              : 
    1120              : #ifdef DEBUG_E2_NOTIFY_MOVE
    1121              :     if (DEBUG_COND) {
    1122              :         std::cout << SIMTIME << " lengthOnDetector = " << lengthOnDetector
    1123              :                   << "\nvehInfo.exitOffset = " << vehInfo.exitOffset
    1124              :                   << " vehInfo.entryOffset = " << vehInfo.entryOffset
    1125              :                   << " distToExit = " << distToExit
    1126              :                   << std::endl;
    1127              :     }
    1128              : #endif
    1129              : 
    1130              :     /* Store new infos */
    1131     47370516 :     return new MoveNotificationInfo(veh.getID(), oldPos, newPos, newSpeed, veh.getAcceleration(),
    1132     47370516 :                                     myDetectorLength - (vehInfo.entryOffset + newPos),
    1133              :                                     timeOnDetector, lengthOnDetector, timeLoss,
    1134     94741032 :                                     STEPS2TIME(veh.getWaitingTime()), stillOnDetector);
    1135              : }
    1136              : 
    1137              : void
    1138     47370516 : MSE2Collector::buildJam(bool isInJam, std::vector<MoveNotificationInfo*>::const_iterator mni, JamInfo*& currentJam, std::vector<JamInfo*>& jams) {
    1139              : #ifdef DEBUG_E2_JAMS
    1140              :     if (DEBUG_COND) {
    1141              :         std::cout << SIMTIME << " buildJam() for vehicle '" << (*mni)->id << "'" << std::endl;
    1142              :     }
    1143              : #endif
    1144     47370516 :     if (isInJam) {
    1145              :         // The vehicle is in a jam;
    1146              :         //  it may be a new one or already an existing one
    1147     33413904 :         if (currentJam == nullptr) {
    1148              : #ifdef DEBUG_E2_JAMS
    1149              :             if (DEBUG_COND) {
    1150              :                 std::cout << SIMTIME << " vehicle '" << (*mni)->id << "' forms the start of the first jam" << std::endl;
    1151              :             }
    1152              : #endif
    1153              :             // the vehicle is the first vehicle in a jam
    1154      2088048 :             currentJam = new JamInfo();
    1155      2088048 :             currentJam->firstStandingVehicle = mni;
    1156              :         } else {
    1157              :             // ok, we have a jam already. But - maybe it is too far away
    1158              :             //  ... honestly, I can hardly find a reason for doing this,
    1159              :             //  but jams were defined this way in an earlier version...
    1160     31325856 :             MoveNotificationInfo* lastVeh = *currentJam->lastStandingVehicle;
    1161     31325856 :             MoveNotificationInfo* currVeh = *mni;
    1162     31325856 :             if (lastVeh->distToDetectorEnd - currVeh->distToDetectorEnd > myJamDistanceThreshold) {
    1163              : #ifdef DEBUG_E2_JAMS
    1164              :                 if (DEBUG_COND) {
    1165              :                     std::cout << SIMTIME << " vehicle '" << (*mni)->id << "' forms the start of a new jam" << std::endl;
    1166              :                 }
    1167              : #endif
    1168              :                 // yep, yep, yep - it's a new one...
    1169              :                 //  close the frist, build a new
    1170            0 :                 jams.push_back(currentJam);
    1171            0 :                 currentJam = new JamInfo();
    1172            0 :                 currentJam->firstStandingVehicle = mni;
    1173              :             }
    1174              :         }
    1175     33413904 :         currentJam->lastStandingVehicle = mni;
    1176              :     } else {
    1177              :         // the vehicle is not part of a jam...
    1178              :         //  maybe we have to close an already computed jam
    1179     13956612 :         if (currentJam != nullptr) {
    1180              : #ifdef DEBUG_E2_JAMS
    1181              :             if (DEBUG_COND) {
    1182              :                 std::cout << SIMTIME << " Closing current jam." << std::endl;
    1183              :             }
    1184              : #endif
    1185       938750 :             jams.push_back(currentJam);
    1186       938750 :             currentJam = nullptr;
    1187              :         }
    1188              :     }
    1189     47370516 : }
    1190              : 
    1191              : 
    1192              : bool
    1193     47370516 : MSE2Collector::checkJam(std::vector<MoveNotificationInfo*>::const_iterator mni, std::map<std::string, SUMOTime>& haltingVehicles, std::map<std::string, SUMOTime>& intervalHaltingVehicles) {
    1194              : #ifdef DEBUG_E2_JAMS
    1195              :     if (DEBUG_COND) {
    1196              :         std::cout << SIMTIME << " CheckJam() for vehicle '" << (*mni)->id << "'" << std::endl;
    1197              :     }
    1198              : #endif
    1199              :     // jam-checking begins
    1200              :     bool isInJam = false;
    1201              :     // first, check whether the vehicle is slow enough to be counted as halting
    1202     47370516 :     if ((*mni)->speed < myJamHaltingSpeedThreshold) {
    1203     34125712 :         myCurrentHaltingsNumber++;
    1204              :         // we have to track the time it was halting;
    1205              :         // so let's look up whether it was halting before and compute the overall halting time
    1206     34125712 :         bool wasHalting = myHaltingVehicleDurations.count((*mni)->id) > 0;
    1207              :         if (wasHalting) {
    1208     33346991 :             haltingVehicles[(*mni)->id] = myHaltingVehicleDurations[(*mni)->id] + DELTA_T;
    1209     33346991 :             intervalHaltingVehicles[(*mni)->id] = myIntervalHaltingVehicleDurations[(*mni)->id] + DELTA_T;
    1210              :         } else {
    1211              : #ifdef DEBUG_E2_JAMS
    1212              :             if (DEBUG_COND) {
    1213              :                 std::cout << SIMTIME << " vehicle '" << (*mni)->id << "' starts halting." << std::endl;
    1214              :             }
    1215              : #endif
    1216       778721 :             haltingVehicles[(*mni)->id] = DELTA_T;
    1217       778721 :             intervalHaltingVehicles[(*mni)->id] = DELTA_T;
    1218       778721 :             myCurrentStartedHalts++;
    1219       778721 :             myStartedHalts++;
    1220              :         }
    1221              :         // we now check whether the halting time is large enough
    1222     34125712 :         if (haltingVehicles[(*mni)->id] > myJamHaltingTimeThreshold) {
    1223              :             // yep --> the vehicle is a part of a jam
    1224              :             isInJam = true;
    1225              :         }
    1226              :     } else {
    1227              :         // is not standing anymore; keep duration information
    1228     13244804 :         std::map<std::string, SUMOTime>::iterator v = myHaltingVehicleDurations.find((*mni)->id);
    1229     13244804 :         if (v != myHaltingVehicleDurations.end()) {
    1230       774269 :             myPastStandingDurations.push_back(v->second);
    1231              :             myHaltingVehicleDurations.erase(v);
    1232              :         }
    1233     13244804 :         v = myIntervalHaltingVehicleDurations.find((*mni)->id);
    1234     13244804 :         if (v != myIntervalHaltingVehicleDurations.end()) {
    1235       774269 :             myPastIntervalStandingDurations.push_back((*v).second);
    1236              :             myIntervalHaltingVehicleDurations.erase(v);
    1237              :         }
    1238              :     }
    1239              : #ifdef DEBUG_E2_JAMS
    1240              :     if (DEBUG_COND) {
    1241              :         std::cout << SIMTIME << " vehicle '" << (*mni)->id << "'" << (isInJam ? "is jammed." : "is not jammed.") << std::endl;
    1242              :     }
    1243              : #endif
    1244     47370516 :     return isInJam;
    1245              : }
    1246              : 
    1247              : 
    1248              : void
    1249     14923491 : MSE2Collector::processJams(std::vector<JamInfo*>& jams, JamInfo* currentJam) {
    1250              :     // push last jam
    1251     14923491 :     if (currentJam != nullptr) {
    1252      1149298 :         jams.push_back(currentJam);
    1253      1149298 :         currentJam = nullptr;
    1254              :     }
    1255              : 
    1256              : #ifdef DEBUG_E2_JAMS
    1257              :     if (DEBUG_COND) {
    1258              :         std::cout << "\n" << SIMTIME << " processJams()"
    1259              :                   << "\nNumber of jams: " << jams.size() << std::endl;
    1260              :     }
    1261              : #endif
    1262              : 
    1263              :     // process jam information
    1264     14923491 :     myCurrentMaxJamLengthInMeters = 0;
    1265     14923491 :     myCurrentMaxJamLengthInVehicles = 0;
    1266     14923491 :     myCurrentJamLengthInMeters = 0;
    1267     14923491 :     myCurrentJamLengthInVehicles = 0;
    1268     14923491 :     myCurrentJamDuration = 0;
    1269     17011539 :     for (std::vector<JamInfo*>::const_iterator i = jams.begin(); i != jams.end(); ++i) {
    1270              :         // compute current jam's values
    1271      2088048 :         MoveNotificationInfo* lastVeh = *((*i)->lastStandingVehicle);
    1272      2088048 :         MoveNotificationInfo* firstVeh = *((*i)->firstStandingVehicle);
    1273      2088048 :         const double jamLengthInMeters = MAX2(lastVeh->distToDetectorEnd, 0.) -
    1274      2088048 :                                          MAX2(firstVeh->distToDetectorEnd, 0.) +
    1275      2088048 :                                          lastVeh->lengthOnDetector;
    1276      2088048 :         const int jamLengthInVehicles = (int) distance((*i)->firstStandingVehicle, (*i)->lastStandingVehicle) + 1;
    1277              :         // apply them to the statistics
    1278      2088048 :         myCurrentMaxJamLengthInMeters = MAX2(myCurrentMaxJamLengthInMeters, jamLengthInMeters);
    1279      2088048 :         myCurrentMaxJamLengthInVehicles = MAX2(myCurrentMaxJamLengthInVehicles, jamLengthInVehicles);
    1280      2088048 :         myJamLengthInMetersSum += jamLengthInMeters;
    1281      2088048 :         myJamLengthInVehiclesSum += jamLengthInVehicles;
    1282      2088048 :         myCurrentJamLengthInMeters += jamLengthInMeters;
    1283      2088048 :         myCurrentJamLengthInVehicles += jamLengthInVehicles;
    1284      4170376 :         myCurrentJamDuration = MAX2(myCurrentJamDuration, firstVeh->waitingTime);
    1285              : #ifdef DEBUG_E2_JAMS
    1286              :         if (DEBUG_COND) {
    1287              :             std::cout << SIMTIME << " processing jam nr." << ((int) distance((std::vector<JamInfo*>::const_iterator) jams.begin(), i) + 1)
    1288              :                       << "\njamLengthInMeters = " << jamLengthInMeters
    1289              :                       << " jamLengthInVehicles = " << jamLengthInVehicles
    1290              :                       << std::endl;
    1291              :         }
    1292              : #endif
    1293              :     }
    1294     14923491 :     myCurrentJamNo = (int) jams.size();
    1295              : 
    1296              :     // clean up jam structure
    1297     17011539 :     for (std::vector<JamInfo*>::iterator i = jams.begin(); i != jams.end(); ++i) {
    1298      2088048 :         delete *i;
    1299              :     }
    1300     14923491 : }
    1301              : 
    1302              : void
    1303     47370516 : MSE2Collector::calculateTimeLossAndTimeOnDetector(const SUMOTrafficObject& veh, double oldPos, double newPos, const VehicleInfo& vi, double& timeOnDetector, double& timeLoss) const {
    1304              :     assert(veh.getID() == vi.id);
    1305              :     assert(newPos + vi.entryOffset >= 0);
    1306              : 
    1307     47370516 :     if (oldPos == newPos) {
    1308              :         // vehicle is stopped
    1309     33642329 :         timeLoss = TS;
    1310     33642329 :         timeOnDetector = TS;
    1311     33642329 :         return;
    1312              :     }
    1313              : 
    1314              :     // Eventual positional offset of the detector start from the lane's start
    1315     13728187 :     double entryPos = MAX2(-vi.entryOffset, 0.);
    1316              :     // Time of this vehicle entering the detector in the last time step
    1317              :     double entryTime = 0;
    1318              :     // Take into account the time before entering the detector, if there is.
    1319     13728187 :     if (oldPos < entryPos) {
    1320              :         // Vehicle entered the detector in the last step, either traversing the detector start or somewhere in the middle.
    1321       591593 :         entryTime = MSCFModel::passingTime(oldPos, entryPos, newPos, veh.getPreviousSpeed(), veh.getSpeed());
    1322              :     }
    1323              :     // speed at detector entry
    1324     13728187 :     double entrySpeed = MSCFModel::speedAfterTime(entryTime, veh.getPreviousSpeed(), newPos - oldPos);
    1325              :     // Calculate time spent on detector until reaching newPos or a detector exit
    1326     13728187 :     double exitPos = MIN2(newPos, -vi.exitOffset + vi.length);
    1327              :     assert(entryPos < exitPos);
    1328              : 
    1329              :     // calculate vehicle's time spent on the detector
    1330              :     double exitTime;
    1331     13728187 :     if (exitPos == newPos) {
    1332     13137947 :         exitTime  = TS;
    1333              :     } else {
    1334       590240 :         exitTime = MSCFModel::passingTime(oldPos, exitPos, newPos, veh.getPreviousSpeed(), veh.getSpeed());
    1335              :     }
    1336              : 
    1337              :     // Vehicle's Speed when leaving the detector
    1338     13728187 :     double exitSpeed = MSCFModel::speedAfterTime(exitTime, veh.getPreviousSpeed(), newPos - oldPos);
    1339              : 
    1340              :     // Maximal speed on vehicle's current lane (== lane before last time step)
    1341              :     // Note: this disregards the possibility of different maximal speeds on different traversed lanes.
    1342              :     //       (we accept this as discretization error)
    1343     13728187 :     double vmax = MAX2(veh.getLane()->getVehicleMaxSpeed(&veh), NUMERICAL_EPS);
    1344              : 
    1345              :     // Time loss suffered on the detector
    1346     13728187 :     timeOnDetector = exitTime - entryTime;
    1347     27428331 :     timeLoss = MAX2(0., timeOnDetector * (vmax - (entrySpeed + exitSpeed) / 2) / vmax);
    1348              : 
    1349              : #ifdef DEBUG_E2_TIME_ON_DETECTOR
    1350              :     if (DEBUG_COND) {
    1351              :         std::cout << SIMTIME << " calculateTimeLoss() for vehicle '" << veh.getID() << "'"
    1352              :                   << " oldPos = " << oldPos << " newPos = " << newPos
    1353              :                   << " entryPos = " << entryPos << " exitPos = " << exitPos
    1354              :                   << " timeOnDetector = " << timeOnDetector
    1355              :                   << " timeLoss = " << timeLoss
    1356              :                   << std::endl;
    1357              :     }
    1358              : #endif
    1359              : }
    1360              : 
    1361              : 
    1362              : void
    1363         4819 : MSE2Collector::writeXMLDetectorProlog(OutputDevice& dev) const {
    1364         9638 :     dev.writeXMLHeader("detector", "det_e2_file.xsd");
    1365         4819 : }
    1366              : 
    1367              : void
    1368       318263 : MSE2Collector::writeXMLOutput(OutputDevice& dev, SUMOTime startTime, SUMOTime stopTime) {
    1369       318263 :     const double meanSpeed = getIntervalMeanSpeed();
    1370       318263 :     const double meanOccupancy = getIntervalOccupancy();
    1371       318263 :     const double meanTimeLoss = getIntervalMeanTimeLoss();
    1372       318263 :     myPreviousMeanOccupancy = meanOccupancy;
    1373       318263 :     myPreviousMeanSpeed = meanSpeed;
    1374       318263 :     myPreviousMaxJamLengthInMeters = myMaxJamInMeters;
    1375       318263 :     myPreviousNumberOfSeenVehicles = myNumberOfSeenVehicles;
    1376       318263 :     myPreviousMeanTimeLoss = meanTimeLoss;
    1377              : 
    1378       318263 :     if (dev.isNull()) {
    1379        24778 :         reset();
    1380        24778 :         return;
    1381              :     }
    1382       586970 :     dev << "   <interval begin=\"" << time2string(startTime) << "\" end=\"" << time2string(stopTime) << "\" " << "id=\"" << getID() << "\" ";
    1383              : 
    1384              : 
    1385       293485 :     const double meanJamLengthInMeters = myTimeSamples != 0 ? myMeanMaxJamInMeters / (double) myTimeSamples : 0;
    1386       293485 :     const double meanJamLengthInVehicles = myTimeSamples != 0 ? myMeanMaxJamInVehicles / (double) myTimeSamples : 0;
    1387       293485 :     const double meanVehicleNumber = myTimeSamples != 0 ? (double) myMeanVehicleNumber / (double) myTimeSamples : 0;
    1388              : 
    1389              :     SUMOTime haltingDurationSum = 0;
    1390              :     SUMOTime maxHaltingDuration = 0;
    1391              :     int haltingNo = 0;
    1392       949039 :     for (std::vector<SUMOTime>::iterator i = myPastStandingDurations.begin(); i != myPastStandingDurations.end(); ++i) {
    1393       655554 :         haltingDurationSum += (*i);
    1394              :         maxHaltingDuration = MAX2(maxHaltingDuration, (*i));
    1395       655554 :         haltingNo++;
    1396              :     }
    1397      5211217 :     for (std::map<std::string, SUMOTime> ::iterator i = myHaltingVehicleDurations.begin(); i != myHaltingVehicleDurations.end(); ++i) {
    1398      4917732 :         haltingDurationSum += (*i).second;
    1399              :         maxHaltingDuration = MAX2(maxHaltingDuration, (*i).second);
    1400      4917732 :         haltingNo++;
    1401              :     }
    1402       293485 :     const SUMOTime meanHaltingDuration = haltingNo != 0 ? haltingDurationSum / haltingNo : 0;
    1403              : 
    1404              :     SUMOTime intervalHaltingDurationSum = 0;
    1405              :     SUMOTime intervalMaxHaltingDuration = 0;
    1406              :     int intervalHaltingNo = 0;
    1407       949039 :     for (std::vector<SUMOTime>::iterator i = myPastIntervalStandingDurations.begin(); i != myPastIntervalStandingDurations.end(); ++i) {
    1408       655554 :         intervalHaltingDurationSum += (*i);
    1409              :         intervalMaxHaltingDuration = MAX2(intervalMaxHaltingDuration, (*i));
    1410       655554 :         intervalHaltingNo++;
    1411              :     }
    1412      5211217 :     for (std::map<std::string, SUMOTime> ::iterator i = myIntervalHaltingVehicleDurations.begin(); i != myIntervalHaltingVehicleDurations.end(); ++i) {
    1413      4917732 :         intervalHaltingDurationSum += (*i).second;
    1414              :         intervalMaxHaltingDuration = MAX2(intervalMaxHaltingDuration, (*i).second);
    1415      4917732 :         intervalHaltingNo++;
    1416              :     }
    1417       293485 :     const SUMOTime intervalMeanHaltingDuration = intervalHaltingNo != 0 ? intervalHaltingDurationSum / intervalHaltingNo : 0;
    1418              : 
    1419              : #ifdef DEBUG_E2_XML_OUT
    1420              :     if (DEBUG_COND) {
    1421              :         std::stringstream ss;
    1422              :         ss  << "sampledSeconds=\"" << myVehicleSamples << "\" "
    1423              :             << "myTimeSamples=\"" << myTimeSamples << "\" "
    1424              :             << "myOccupancySum=\"" << myOccupancySum << "\" "
    1425              :             << "myMeanVehicleNumber=\"" << myMeanVehicleNumber << "\" "
    1426              :             << "nVehEntered=\"" << myNumberOfEnteredVehicles << "\" "
    1427              :             << "meanSpeed=\"" << meanSpeed << "\"";
    1428              :         std::cout << ss.str() << std::endl;
    1429              :     }
    1430              : #endif
    1431              : 
    1432              : 
    1433       293485 :     dev << "sampledSeconds=\"" << myVehicleSamples << "\" "
    1434       293485 :         << "nVehEntered=\"" << myNumberOfEnteredVehicles << "\" "
    1435       293485 :         << "nVehLeft=\"" << myNumberOfLeftVehicles << "\" "
    1436       293485 :         << "nVehSeen=\"" << myNumberOfSeenVehicles << "\" "
    1437       293485 :         << "meanSpeed=\"" << meanSpeed << "\" "
    1438       293485 :         << "meanTimeLoss=\"" << meanTimeLoss << "\" "
    1439       293485 :         << "meanOccupancy=\"" << meanOccupancy << "\" "
    1440       293485 :         << "maxOccupancy=\"" << myMaxOccupancy << "\" "
    1441       293485 :         << "meanMaxJamLengthInVehicles=\"" << meanJamLengthInVehicles << "\" "
    1442       293485 :         << "meanMaxJamLengthInMeters=\"" << meanJamLengthInMeters << "\" "
    1443       293485 :         << "maxJamLengthInVehicles=\"" << myMaxJamInVehicles << "\" "
    1444       293485 :         << "maxJamLengthInMeters=\"" << myMaxJamInMeters << "\" "
    1445       293485 :         << "jamLengthInVehiclesSum=\"" << myJamLengthInVehiclesSum << "\" "
    1446       293485 :         << "jamLengthInMetersSum=\"" << myJamLengthInMetersSum << "\" "
    1447       293485 :         << "meanHaltingDuration=\"" << STEPS2TIME(meanHaltingDuration) << "\" "
    1448       293485 :         << "maxHaltingDuration=\"" << STEPS2TIME(maxHaltingDuration) << "\" "
    1449       293485 :         << "haltingDurationSum=\"" << STEPS2TIME(haltingDurationSum) << "\" "
    1450       293485 :         << "meanIntervalHaltingDuration=\"" << STEPS2TIME(intervalMeanHaltingDuration) << "\" "
    1451       293485 :         << "maxIntervalHaltingDuration=\"" << STEPS2TIME(intervalMaxHaltingDuration) << "\" "
    1452       293485 :         << "intervalHaltingDurationSum=\"" << STEPS2TIME(intervalHaltingDurationSum) << "\" "
    1453       293485 :         << "startedHalts=\"" << myStartedHalts << "\" "
    1454       293485 :         << "meanVehicleNumber=\"" << meanVehicleNumber << "\" "
    1455       293485 :         << "maxVehicleNumber=\"" << myMaxVehicleNumber << "\" "
    1456       293485 :         << "/>\n";
    1457              : 
    1458       293485 :     reset();
    1459              : }
    1460              : 
    1461              : void
    1462       324684 : MSE2Collector::reset() {
    1463       324684 :     myVehicleSamples = 0;
    1464       324684 :     myTotalTimeLoss = 0.;
    1465       324684 :     myNumberOfEnteredVehicles = 0;
    1466       324684 :     myNumberOfSeenVehicles -= myNumberOfLeftVehicles;
    1467       324684 :     myNumberOfLeftVehicles = 0;
    1468       324684 :     myMaxVehicleNumber = 0;
    1469              : 
    1470       324684 :     mySpeedSum = 0;
    1471       324684 :     myStartedHalts = 0;
    1472       324684 :     myJamLengthInMetersSum = 0;
    1473       324684 :     myJamLengthInVehiclesSum = 0;
    1474       324684 :     myOccupancySum = 0;
    1475       324684 :     myMaxOccupancy = 0;
    1476       324684 :     myMeanMaxJamInVehicles = 0;
    1477       324684 :     myMeanMaxJamInMeters = 0;
    1478       324684 :     myMaxJamInVehicles = 0;
    1479       324684 :     myMaxJamInMeters = 0;
    1480       324684 :     myTimeSamples = 0;
    1481       324684 :     myMeanVehicleNumber = 0;
    1482      5244735 :     for (std::map<std::string, SUMOTime>::iterator i = myIntervalHaltingVehicleDurations.begin(); i != myIntervalHaltingVehicleDurations.end(); ++i) {
    1483      4920051 :         (*i).second = 0;
    1484              :     }
    1485              :     myPastStandingDurations.clear();
    1486              :     myPastIntervalStandingDurations.clear();
    1487       324684 : }
    1488              : 
    1489              : 
    1490              : int
    1491      2384541 : MSE2Collector::getCurrentVehicleNumber() const {
    1492              :     int result = 0;
    1493      2384541 :     if (myOverrideVehNumber >= 0) {
    1494              :         result = myOverrideVehNumber;
    1495              :     } else {
    1496     18249927 :         for (VehicleInfoMap::const_iterator it = myVehicleInfos.begin(); it != myVehicleInfos.end(); it++) {
    1497     15865446 :             if (it->second->onDetector) {
    1498      2491893 :                 result++;
    1499              :             }
    1500              :         }
    1501              :     }
    1502      2384541 :     return result;
    1503              : }
    1504              : 
    1505              : void
    1506           25 : MSE2Collector::overrideVehicleNumber(int num) {
    1507           25 :     myOverrideVehNumber = num;
    1508           25 : }
    1509              : 
    1510              : 
    1511              : 
    1512              : std::vector<std::string>
    1513          628 : MSE2Collector::getCurrentVehicleIDs() const {
    1514              :     std::vector<std::string> ret;
    1515          981 :     for (VehicleInfoMap::const_iterator i = myVehicleInfos.begin(); i != myVehicleInfos.end(); ++i) {
    1516          353 :         if (i->second->onDetector) {
    1517          201 :             ret.push_back(i->second->id);
    1518              :         }
    1519              :     }
    1520          628 :     std::sort(ret.begin(), ret.end());
    1521          628 :     return ret;
    1522            0 : }
    1523              : 
    1524              : 
    1525              : std::vector<MSE2Collector::VehicleInfo*>
    1526      3370410 : MSE2Collector::getCurrentVehicles() const {
    1527              :     std::vector<VehicleInfo*> res;
    1528              :     VehicleInfoMap::const_iterator i;
    1529      6419591 :     for (i = myVehicleInfos.begin(); i != myVehicleInfos.end(); ++i) {
    1530      3049181 :         if (i->second->onDetector) {
    1531      2183981 :             res.push_back(i->second);
    1532              :         }
    1533              :     }
    1534      3370410 :     return res;
    1535            0 : }
    1536              : 
    1537              : 
    1538              : 
    1539              : int
    1540            0 : MSE2Collector::getEstimatedCurrentVehicleNumber(double speedThreshold) const {
    1541              : 
    1542              :     //    double distance = std::numeric_limits<double>::max();
    1543            0 :     double thresholdSpeed = myLane->getSpeedLimit() / speedThreshold;
    1544              : 
    1545              :     int count = 0;
    1546            0 :     for (VehicleInfoMap::const_iterator it = myVehicleInfos.begin();
    1547            0 :             it != myVehicleInfos.end(); it++) {
    1548            0 :         if (it->second->onDetector) {
    1549              :             //            if (it->position < distance) {
    1550              :             //                distance = it->position;
    1551              :             //            }
    1552              :             //            const double realDistance = myLane->getLength() - distance; // the closer vehicle get to the light the greater is the distance
    1553            0 :             const double realDistance = it->second->distToDetectorEnd;
    1554            0 :             if (it->second->lastSpeed <= thresholdSpeed || it->second->lastAccel > 0) { //TODO speed less half of the maximum speed for the lane NEED TUNING
    1555            0 :                 count = (int)(realDistance / (it->second->length + it->second->minGap)) + 1;
    1556              :             }
    1557              :         }
    1558              :     }
    1559              : 
    1560            0 :     return count;
    1561              : }
    1562              : 
    1563              : double
    1564            0 : MSE2Collector::getEstimateQueueLength() const {
    1565              : 
    1566            0 :     if (myVehicleInfos.empty()) {
    1567              :         return 0;
    1568              :     }
    1569              : 
    1570              :     double distance = 0;
    1571              :     double realDistance = 0;
    1572              :     bool flowing =  true;
    1573            0 :     for (VehicleInfoMap::const_iterator it = myVehicleInfos.begin();
    1574            0 :             it != myVehicleInfos.end(); it++) {
    1575            0 :         if (it->second->onDetector && it->second->totalTimeOnDetector > 0) {
    1576              :             //  double distanceTemp = myLane->getLength() - distance;
    1577            0 :             if (it->second->lastSpeed <= myJamHaltingSpeedThreshold) {
    1578            0 :                 distance = MAX2(it->second->distToDetectorEnd, distance);
    1579            0 :                 realDistance = distance + it->second->length;
    1580              :                 flowing = false;
    1581              :             }
    1582              :             //            DBG(
    1583              :             //                std::ostringstream str;
    1584              :             //                str << time2string(MSNet::getInstance()->getCurrentTimeStep())
    1585              :             //                << " MSE2Collector::getEstimateQueueLength::"
    1586              :             //                << " lane " << myLane->getID()
    1587              :             //                << " vehicle " << it->second.id
    1588              :             //                << " positionOnLane " << it->second.position
    1589              :             //                << " vel " << it->second.speed
    1590              :             //                << " realDistance " << realDistance;
    1591              :             //                WRITE_MESSAGE(str.str());
    1592              :             //            )
    1593              :         }
    1594              :     }
    1595            0 :     if (flowing) {
    1596              :         return 0;
    1597              :     } else {
    1598              :         return realDistance;
    1599              :     }
    1600              : }
    1601              : 
    1602              : 
    1603              : void
    1604         6379 : MSE2Collector::clearState(SUMOTime /* step */) {
    1605         6379 :     for (std::vector<MoveNotificationInfo*>::iterator j = myMoveNotifications.begin(); j != myMoveNotifications.end(); ++j) {
    1606            0 :         delete *j;
    1607              :     }
    1608              :     myMoveNotifications.clear();
    1609              : 
    1610              :     // clear vehicle infos
    1611        10676 :     for (VehicleInfoMap::iterator j = myVehicleInfos.begin(); j != myVehicleInfos.end(); ++j) {
    1612         4297 :         delete j->second;
    1613              :     }
    1614              :     myVehicleInfos.clear();
    1615         6379 : }
    1616              : 
    1617              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1