LCOV - code coverage report
Current view: top level - src/microsim/output - MSE2Collector.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 92.6 % 530 491
Test Date: 2026-05-06 15:47:47 Functions: 94.3 % 35 33

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

Generated by: LCOV version 2.0-1