LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDevice_SSM.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 83.6 % 1543 1290
Test Date: 2024-11-22 15:46:21 Functions: 95.7 % 69 66

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2013-2024 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    MSDevice_SSM.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Michael Behrisch
      17              : /// @author  Jakob Erdmann
      18              : /// @author  Leonhard Luecken
      19              : /// @author  Mirko Barthauer
      20              : /// @author  Johannes Rummel
      21              : /// @date    11.06.2013
      22              : ///
      23              : // An SSM-device logs encounters / conflicts of the carrying vehicle with other surrounding vehicles
      24              : // XXX: Preliminary implementation. Use with care. Especially rerouting vehicles could be problematic.
      25              : // TODO: implement SSM time-gap (estimated conflict entry and exit times are already calculated for PET calculation)
      26              : /****************************************************************************/
      27              : #include <config.h>
      28              : 
      29              : #include <iostream>
      30              : #include <algorithm>
      31              : #include <utils/common/FileHelpers.h>
      32              : #include <utils/common/StringTokenizer.h>
      33              : #include <utils/common/StringUtils.h>
      34              : #include <utils/geom/GeoConvHelper.h>
      35              : #include <utils/geom/GeomHelper.h>
      36              : #include <utils/geom/Position.h>
      37              : #include <utils/options/OptionsCont.h>
      38              : #include <utils/iodevices/OutputDevice.h>
      39              : #include <utils/vehicle/SUMOVehicle.h>
      40              : #include <microsim/MSNet.h>
      41              : #include <microsim/MSJunction.h>
      42              : #include <microsim/MSLane.h>
      43              : #include <microsim/MSLink.h>
      44              : #include <microsim/MSEdge.h>
      45              : #include <microsim/MSVehicle.h>
      46              : #include <microsim/MSVehicleControl.h>
      47              : #include <microsim/MSJunctionControl.h>
      48              : #include <microsim/lcmodels/MSAbstractLaneChangeModel.h>
      49              : #include "MSDevice_SSM.h"
      50              : 
      51              : // ===========================================================================
      52              : // Debug constants
      53              : // ===========================================================================
      54              : //#define DEBUG_SSM
      55              : //#define DEBUG_SSM_OPPOSITE
      56              : //#define DEBUG_ENCOUNTER
      57              : //#define DEBUG_SSM_SURROUNDING
      58              : //#define DEBUG_SSM_DRAC
      59              : //#define DEBUG_SSM_NOTIFICATIONS
      60              : //#define DEBUG_COND(ego) MSNet::getInstance()->getCurrentTimeStep() > 308000
      61              : //
      62              : //#define DEBUG_EGO_ID ""
      63              : //#define DEBUG_FOE_ID ""
      64              : //#define DEBUG_COND_FIND(ego) (ego.getID() == DEBUG_EGO_ID)
      65              : //#define DEBUG_COND(ego) ((ego)!=nullptr && (ego)->getID() == DEBUG_EGO_ID)
      66              : //#define DEBUG_COND_ENCOUNTER(e) ((DEBUG_EGO_ID == std::string("") || e->egoID == DEBUG_EGO_ID) && (DEBUG_FOE_ID == std::string("") || e->foeID == DEBUG_FOE_ID))
      67              : 
      68              : //#define DEBUG_COND(ego) (ego!=nullptr && ego->isSelected())
      69              : //#define DEBUG_COND_FIND(ego) (ego.isSelected())
      70              : //#define DEBUG_COND_ENCOUNTER(e) (e->ego != nullptr && e->ego->isSelected() && e->foe != nullptr && e->foe->isSelected())
      71              : 
      72              : 
      73              : // ===========================================================================
      74              : // Constants
      75              : // ===========================================================================
      76              : // list of implemented SSMs (NOTE: To add more SSMs, identifiers are added to AVAILABLE_SSMS
      77              : //                                 and a default threshold must be defined. A corresponding
      78              : //                                 case should be added to the switch in buildVehicleDevices,
      79              : //                                 and in computeSSMs(), the SSM-value should be computed.)
      80              : #define AVAILABLE_SSMS "TTC DRAC PET BR SGAP TGAP PPET MDRAC"
      81              : #define DEFAULT_THRESHOLD_TTC 3. // in [s.], events get logged if time to collision is below threshold (1.5s. is an appropriate criticality threshold according to Van der Horst, A. R. A. (1991). Time-to-collision as a Cue for Decision-making in Braking [also see Guido et al. 2011])
      82              : #define DEFAULT_THRESHOLD_DRAC 3. // in [m/s^2], events get logged if "deceleration to avoid a crash" is above threshold (3.4s. is an appropriate criticality threshold according to American Association of State Highway and Transportation Officials (2004). A Policy on Geometric Design of Highways and Streets [also see Guido et al. 2011])
      83              : #define DEFAULT_THRESHOLD_MDRAC 3.4 //in [m/s^2], events get logged if "deceleration to avoid a crash" is above threshold (MDRAC considers reaction time of follower)
      84              : 
      85              : #define DEFAULT_THRESHOLD_PET 2. // in seconds, events get logged if post encroachment time is below threshold
      86              : #define DEFAULT_THRESHOLD_PPET 2. // in seconds, events get logged if predicted post encroachment time is below threshold
      87              : 
      88              : #define DEFAULT_THRESHOLD_BR 0.0 // in [m/s^2], events get logged if brake rate is above threshold
      89              : #define DEFAULT_THRESHOLD_SGAP 0.2 // in [m.], events get logged if the space headway is below threshold.
      90              : #define DEFAULT_THRESHOLD_TGAP 0.5 // in [m.], events get logged if the time headway is below threshold.
      91              : 
      92              : #define DEFAULT_EXTRA_TIME 5.      // in seconds, events get logged for extra time even if encounter is over
      93              : 
      94              : // ===========================================================================
      95              : // static members
      96              : // ===========================================================================
      97              : std::set<const MSEdge*> MSDevice_SSM::myEdgeFilter;
      98              : bool MSDevice_SSM::myEdgeFilterInitialized(false);
      99              : bool MSDevice_SSM::myEdgeFilterActive(false);
     100              : 
     101              : // ===========================================================================
     102              : // method definitions
     103              : // ===========================================================================
     104              : 
     105              : /// Nicer output for EncounterType enum
     106            0 : std::ostream& operator<<(std::ostream& out, MSDevice_SSM::EncounterType type) {
     107            0 :     switch (type) {
     108            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_NOCONFLICT_AHEAD:
     109            0 :             out << "NOCONFLICT_AHEAD";
     110            0 :             break;
     111            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_FOLLOWING:
     112            0 :             out << "FOLLOWING";
     113            0 :             break;
     114            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_FOLLOWING_FOLLOWER:
     115            0 :             out << "FOLLOWING_FOLLOWER";
     116            0 :             break;
     117            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_FOLLOWING_LEADER:
     118            0 :             out << "FOLLOWING_LEADER";
     119            0 :             break;
     120            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_ON_ADJACENT_LANES:
     121            0 :             out << "ON_ADJACENT_LANES";
     122            0 :             break;
     123            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_MERGING:
     124            0 :             out << "MERGING";
     125            0 :             break;
     126            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_MERGING_LEADER:
     127            0 :             out << "MERGING_LEADER";
     128            0 :             break;
     129            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_MERGING_FOLLOWER:
     130            0 :             out << "MERGING_FOLLOWER";
     131            0 :             break;
     132            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_MERGING_ADJACENT:
     133            0 :             out << "MERGING_ADJACENT";
     134            0 :             break;
     135            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_CROSSING:
     136            0 :             out << "CROSSING";
     137            0 :             break;
     138            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_CROSSING_LEADER:
     139            0 :             out << "CROSSING_LEADER";
     140            0 :             break;
     141            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_CROSSING_FOLLOWER:
     142            0 :             out << "CROSSING_FOLLOWER";
     143            0 :             break;
     144            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA:
     145            0 :             out << "EGO_ENTERED_CONFLICT_AREA";
     146            0 :             break;
     147            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA:
     148            0 :             out << "FOE_ENTERED_CONFLICT_AREA";
     149            0 :             break;
     150            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA:
     151            0 :             out << "BOTH_ENTERED_CONFLICT_AREA";
     152            0 :             break;
     153            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA:
     154            0 :             out << "EGO_LEFT_CONFLICT_AREA";
     155            0 :             break;
     156            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA:
     157            0 :             out << "FOE_LEFT_CONFLICT_AREA";
     158            0 :             break;
     159            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA:
     160            0 :             out << "BOTH_LEFT_CONFLICT_AREA";
     161            0 :             break;
     162            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_FOLLOWING_PASSED:
     163            0 :             out << "FOLLOWING_PASSED";
     164            0 :             break;
     165            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_MERGING_PASSED:
     166            0 :             out << "MERGING_PASSED";
     167            0 :             break;
     168              :         // Collision (currently unused, might be differentiated further)
     169            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_COLLISION:
     170            0 :             out << "COLLISION";
     171            0 :             break;
     172            0 :         case MSDevice_SSM::ENCOUNTER_TYPE_ONCOMING:
     173            0 :             out << "ONCOMING";
     174            0 :             break;
     175              :         default:
     176            0 :             out << "unknown type (" << int(type) << ")";
     177            0 :             break;
     178              :     }
     179            0 :     return out;
     180              : }
     181              : 
     182              : 
     183              : // ---------------------------------------------------------------------------
     184              : // static initialisation methods
     185              : // ---------------------------------------------------------------------------
     186              : 
     187              : std::set<MSDevice_SSM*, ComparatorNumericalIdLess>* MSDevice_SSM::myInstances = new std::set<MSDevice_SSM*, ComparatorNumericalIdLess>();
     188              : 
     189              : std::set<std::string> MSDevice_SSM::myCreatedOutputFiles;
     190              : 
     191              : int MSDevice_SSM::myIssuedParameterWarnFlags = 0;
     192              : 
     193              : const std::set<int> MSDevice_SSM::FOE_ENCOUNTERTYPES({
     194              :     ENCOUNTER_TYPE_FOLLOWING_LEADER, ENCOUNTER_TYPE_MERGING_FOLLOWER,
     195              :     ENCOUNTER_TYPE_CROSSING_FOLLOWER, ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA,
     196              :     ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
     197              : });
     198              : const std::set<int> MSDevice_SSM::EGO_ENCOUNTERTYPES({
     199              :     ENCOUNTER_TYPE_FOLLOWING_FOLLOWER, ENCOUNTER_TYPE_MERGING_LEADER,
     200              :     ENCOUNTER_TYPE_CROSSING_LEADER, ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA,
     201              :     ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
     202              : });
     203              : 
     204              : 
     205              : const std::set<MSDevice_SSM*, ComparatorNumericalIdLess>&
     206     91771613 : MSDevice_SSM::getInstances() {
     207     91771613 :     return *myInstances;
     208              : }
     209              : 
     210              : void
     211        40275 : MSDevice_SSM::cleanup() {
     212              :     // Close current encounters and flush conflicts to file for all existing devices
     213        40275 :     if (myInstances != nullptr) {
     214        40275 :         for (MSDevice_SSM* device : *myInstances) {
     215            0 :             device->resetEncounters();
     216            0 :             device->flushConflicts(true);
     217            0 :             device->flushGlobalMeasures();
     218              :         }
     219        40275 :         myInstances->clear();
     220              :     }
     221        40743 :     for (const std::string& fn : myCreatedOutputFiles) {
     222          936 :         OutputDevice::getDevice(fn).closeTag();
     223              :     }
     224              :     myCreatedOutputFiles.clear();
     225              :     myEdgeFilter.clear();
     226        40275 :     myEdgeFilterInitialized = false;
     227        40275 :     myEdgeFilterActive = false;
     228        40275 : }
     229              : 
     230              : 
     231              : void
     232        43644 : MSDevice_SSM::insertOptions(OptionsCont& oc) {
     233        43644 :     oc.addOptionSubTopic("SSM Device");
     234        87288 :     insertDefaultAssignmentOptions("ssm", "SSM Device", oc);
     235              : 
     236              :     // custom options
     237        87288 :     oc.doRegister("device.ssm.measures", new Option_String(""));
     238        87288 :     oc.addDescription("device.ssm.measures", "SSM Device", TL("Specifies which measures will be logged (as a space or comma-separated sequence of IDs in ('TTC', 'DRAC', 'PET', 'PPET', 'MDRAC'))"));
     239        87288 :     oc.doRegister("device.ssm.thresholds", new Option_String(""));
     240        87288 :     oc.addDescription("device.ssm.thresholds", "SSM Device", TL("Specifies space or comma-separated thresholds corresponding to the specified measures (see documentation and watch the order!). Only events exceeding the thresholds will be logged."));
     241        43644 :     oc.doRegister("device.ssm.trajectories",  new Option_Bool(false));
     242        87288 :     oc.addDescription("device.ssm.trajectories", "SSM Device", TL("Specifies whether trajectories will be logged (if false, only the extremal values and times are reported)."));
     243        43644 :     oc.doRegister("device.ssm.range", new Option_Float(50.));
     244        87288 :     oc.addDescription("device.ssm.range", "SSM Device", TL("Specifies the detection range in meters. For vehicles below this distance from the equipped vehicle, SSM values are traced."));
     245        43644 :     oc.doRegister("device.ssm.extratime", new Option_Float(DEFAULT_EXTRA_TIME));
     246        87288 :     oc.addDescription("device.ssm.extratime", "SSM Device", TL("Specifies the time in seconds to be logged after a conflict is over. Required >0 if PET is to be calculated for crossing conflicts."));
     247        43644 :     oc.doRegister("device.ssm.mdrac.prt", new Option_Float(1.));
     248        87288 :     oc.addDescription("device.ssm.mdrac.prt", "SSM Device", TL("Specifies the perception reaction time for MDRAC computation."));
     249        87288 :     oc.doRegister("device.ssm.file", new Option_String(""));
     250        87288 :     oc.addDescription("device.ssm.file", "SSM Device", TL("Give a global default filename for the SSM output"));
     251        43644 :     oc.doRegister("device.ssm.geo", new Option_Bool(false));
     252        87288 :     oc.addDescription("device.ssm.geo", "SSM Device", TL("Whether to use coordinates of the original reference system in output"));
     253        43644 :     oc.doRegister("device.ssm.write-positions", new Option_Bool(false));
     254        87288 :     oc.addDescription("device.ssm.write-positions", "SSM Device", TL("Whether to write positions (coordinates) for each timestep"));
     255        43644 :     oc.doRegister("device.ssm.write-lane-positions", new Option_Bool(false));
     256        87288 :     oc.addDescription("device.ssm.write-lane-positions", "SSM Device", TL("Whether to write lanes and their positions for each timestep"));
     257        87288 :     oc.doRegister("device.ssm.exclude-conflict-types", new Option_String(""));
     258        87288 :     oc.addDescription("device.ssm.exclude-conflict-types", "SSM Device", TL("Which conflicts will be excluded from the log according to the conflict type they have been classified (combination of values in 'ego', 'foe' , '', any numerical valid conflict type code). An empty value will log all and 'ego'/'foe' refer to a certain conflict type subset."));
     259        43644 : }
     260              : 
     261              : 
     262              : void
     263          456 : MSDevice_SSM::initEdgeFilter() {
     264          456 :     myEdgeFilterInitialized = true;
     265          912 :     if (OptionsCont::getOptions().isSet("device.ssm.filter-edges.input-file")) {
     266           40 :         const std::string file = OptionsCont::getOptions().getString("device.ssm.filter-edges.input-file");
     267           20 :         std::ifstream strm(file.c_str());
     268           20 :         if (!strm.good()) {
     269            0 :             throw ProcessError(TLF("Could not load names of edges for filtering SSM device output from '%'.", file));
     270              :         }
     271           20 :         myEdgeFilterActive = true;
     272           48 :         while (strm.good()) {
     273              :             std::string line;
     274           28 :             strm >> line;
     275              :             // maybe we're loading an edge-selection
     276           56 :             if (StringUtils::startsWith(line, "edge:")) {
     277           16 :                 std::string edgeID = line.substr(5);
     278           16 :                 MSEdge* edge = MSEdge::dictionary(edgeID);
     279           16 :                 if (edge != nullptr) {
     280              :                     myEdgeFilter.insert(edge);
     281              :                 } else {
     282           12 :                     WRITE_WARNING("Unknown edge ID '" + edgeID + "' in SSM device edge filter (" + file + "): " + line);
     283              :                 }
     284           24 :             } else if (StringUtils::startsWith(line, "junction:")) {
     285              :                 // get the internal edge(s) of a junction
     286            8 :                 std::string junctionID = line.substr(9);
     287            8 :                 MSJunction* junction = MSNet::getInstance()->getJunctionControl().get(junctionID);
     288            4 :                 if (junction != nullptr) {
     289           60 :                     for (MSLane* const internalLane : junction->getInternalLanes()) {
     290           56 :                         myEdgeFilter.insert(&(internalLane->getEdge()));
     291            4 :                     }
     292              :                 } else {
     293           12 :                     WRITE_WARNING("Unknown junction ID '" + junctionID + "' in SSM device edge filter (" + file + "): " + line);
     294              :                 }
     295            4 :             } else if (line == "") { // ignore empty lines (mostly last line)
     296              :             } else {
     297           12 :                 WRITE_WARNING("Cannot interpret line in SSM device edge filter (" + file + "): " + line);
     298              :             }
     299              :         }
     300           20 :     }
     301          456 : }
     302              : 
     303              : void
     304      5104373 : MSDevice_SSM::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
     305     10208746 :     if (equippedByDefaultAssignmentOptions(OptionsCont::getOptions(), "ssm", v, false)) {
     306         3880 :         if (MSGlobals::gUseMesoSim) {
     307           12 :             WRITE_WARNINGF("SSM Device for vehicle '%' will not be built. (SSMs not supported in MESO)", v.getID());
     308           12 :             return;
     309              :         }
     310              :         // ID for the device
     311         3876 :         std::string deviceID = "ssm_" + v.getID();
     312              : 
     313              :         // Load parameters:
     314              : 
     315              :         // Measures and thresholds
     316              :         std::map<std::string, double> thresholds;
     317         3876 :         bool success = getMeasuresAndThresholds(v, deviceID, thresholds);
     318         3876 :         if (!success) {
     319              :             return;
     320              :         }
     321              : 
     322              :         // TODO: modify trajectory option: "all", "conflictPoints", ("position" && "speed" == "vehState"), "SSMs"!
     323              :         // Trajectories
     324         3868 :         bool trajectories = requestsTrajectories(v);
     325              : 
     326              :         // detection range
     327         3868 :         double range = getDetectionRange(v);
     328              : 
     329              :         // extra time
     330         3868 :         double extraTime = getExtraTime(v);
     331              : 
     332              :         // File
     333         3868 :         std::string file = getOutputFilename(v, deviceID);
     334              : 
     335         3868 :         const bool useGeo = useGeoCoords(v);
     336              : 
     337         3868 :         const bool writePos = writePositions(v);
     338              : 
     339         3868 :         const bool writeLanesPos = writeLanesPositions(v);
     340              : 
     341              :         std::vector<int> conflictTypeFilter;
     342         3868 :         success = filterByConflictType(v, deviceID, conflictTypeFilter);
     343         3868 :         if (!success) {
     344              :             return;
     345              :         }
     346              : 
     347              :         // Build the device (XXX: who deletes it?)
     348        11604 :         MSDevice_SSM* device = new MSDevice_SSM(v, deviceID, file, thresholds, trajectories, range, extraTime, useGeo, writePos, writeLanesPos, conflictTypeFilter);
     349         3868 :         into.push_back(device);
     350              : 
     351              :         // Init spatial filter (once)
     352         3868 :         if (!myEdgeFilterInitialized) {
     353          456 :             initEdgeFilter();
     354              :         }
     355         3868 :     }
     356              : }
     357              : 
     358              : 
     359       666892 : MSDevice_SSM::Encounter::Encounter(const MSVehicle* _ego, const MSVehicle* const _foe, double _begin, double extraTime) :
     360       666892 :     ego(_ego),
     361       666892 :     foe(_foe),
     362       666892 :     egoID(_ego->getID()),
     363       666892 :     foeID(_foe->getID()),
     364       666892 :     begin(_begin),
     365       666892 :     end(-INVALID_DOUBLE),
     366       666892 :     currentType(ENCOUNTER_TYPE_NOCONFLICT_AHEAD),
     367       666892 :     remainingExtraTime(extraTime),
     368       666892 :     egoConflictEntryTime(INVALID_DOUBLE),
     369       666892 :     egoConflictExitTime(INVALID_DOUBLE),
     370       666892 :     foeConflictEntryTime(INVALID_DOUBLE),
     371       666892 :     foeConflictExitTime(INVALID_DOUBLE),
     372       666892 :     minTTC(INVALID_DOUBLE, Position::INVALID, ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID_DOUBLE, INVALID_DOUBLE),
     373              :     maxDRAC(INVALID_DOUBLE, Position::INVALID, ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID_DOUBLE, INVALID_DOUBLE),
     374              :     maxMDRAC(INVALID_DOUBLE, Position::INVALID, ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID_DOUBLE, INVALID_DOUBLE),
     375              :     PET(INVALID_DOUBLE, Position::INVALID, ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID_DOUBLE, INVALID_DOUBLE),
     376              :     minPPET(INVALID_DOUBLE, Position::INVALID, ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID_DOUBLE, INVALID_DOUBLE),
     377       666892 :     closingRequested(false) {
     378              : #ifdef DEBUG_ENCOUNTER
     379              :     if (DEBUG_COND_ENCOUNTER(this)) {
     380              :         std::cout << "\n" << SIMTIME << " Constructing encounter of '" << ego->getID() << "' and '" << foe->getID() << "'" << std::endl;
     381              :     }
     382              : #endif
     383       666892 : }
     384              : 
     385      1333784 : MSDevice_SSM::Encounter::~Encounter() {
     386              : #ifdef DEBUG_ENCOUNTER
     387              :     if (DEBUG_COND_ENCOUNTER(this)) {
     388              :         std::cout << "\n" << SIMTIME << " Destroying encounter of '" << egoID << "' and '" << foeID << "' (begin was " << begin << ")" << std::endl;
     389              :     }
     390              : #endif
     391       666892 : }
     392              : 
     393              : 
     394              : void
     395      6016079 : MSDevice_SSM::Encounter::add(double time, const EncounterType type, Position egoX, std::string egoLane, double egoLanePos, Position egoV,
     396              :                              Position foeX, std::string foeLane, double foeLanePos, Position foeV,
     397              :                              Position conflictPoint, double egoDistToConflict, double foeDistToConflict, double ttc, double drac, std::pair<double, double> pet, double ppet, double mdrac) {
     398              : #ifdef DEBUG_ENCOUNTER
     399              :     if (DEBUG_COND_ENCOUNTER(this))
     400              :         std::cout << time << " Adding data point for encounter of '" << egoID << "' and '" << foeID << "':\n"
     401              :                   << "type=" << type << ", egoDistToConflict=" << (egoDistToConflict == INVALID_DOUBLE ? "NA" : ::toString(egoDistToConflict))
     402              :                   << ", foeDistToConflict=" << (foeDistToConflict == INVALID_DOUBLE ? "NA" : ::toString(foeDistToConflict))
     403              :                   << ",\nttc=" << (ttc == INVALID_DOUBLE ? "NA" : ::toString(ttc))
     404              :                   << ", drac=" << (drac == INVALID_DOUBLE ? "NA" : ::toString(drac))
     405              :                   << ", pet=" << (pet.second == INVALID_DOUBLE ? "NA" : ::toString(pet.second))
     406              :                   << std::endl;
     407              : #endif
     408      6016079 :     currentType = type;
     409              : 
     410      6016079 :     timeSpan.push_back(time);
     411      6016079 :     typeSpan.push_back(type);
     412      6016079 :     egoTrajectory.x.push_back(egoX);
     413      6016079 :     egoTrajectory.lane.push_back(egoLane);
     414      6016079 :     egoTrajectory.lanePos.push_back(egoLanePos);
     415      6016079 :     egoTrajectory.v.push_back(egoV);
     416      6016079 :     foeTrajectory.x.push_back(foeX);
     417      6016079 :     foeTrajectory.lane.push_back(foeLane);
     418      6016079 :     foeTrajectory.lanePos.push_back(foeLanePos);
     419      6016079 :     foeTrajectory.v.push_back(foeV);
     420      6016079 :     conflictPointSpan.push_back(conflictPoint);
     421      6016079 :     egoDistsToConflict.push_back(egoDistToConflict);
     422      6016079 :     foeDistsToConflict.push_back(foeDistToConflict);
     423              : 
     424      6016079 :     TTCspan.push_back(ttc);
     425      6016079 :     if (ttc != INVALID_DOUBLE && (ttc < minTTC.value || minTTC.value == INVALID_DOUBLE)) {
     426       285437 :         minTTC.value = ttc;
     427       285437 :         minTTC.time = time;
     428       285437 :         minTTC.pos = conflictPoint;
     429       570874 :         minTTC.type = ttc <= 0 ? ENCOUNTER_TYPE_COLLISION :  type;
     430       285437 :         minTTC.speed = egoV.distanceTo(Position(0, 0));
     431              :     }
     432              : 
     433      6016079 :     DRACspan.push_back(drac);
     434      6016079 :     if (drac != INVALID_DOUBLE && (drac > maxDRAC.value || maxDRAC.value == INVALID_DOUBLE)) {
     435        91501 :         maxDRAC.value = drac;
     436        91501 :         maxDRAC.time = time;
     437        91501 :         maxDRAC.pos = conflictPoint;
     438        91501 :         maxDRAC.type = type;
     439        91501 :         maxDRAC.speed = egoV.distanceTo(Position(0, 0));
     440              :     }
     441              : 
     442      6016079 :     if (pet.first != INVALID_DOUBLE && (PET.value >= pet.second || PET.value == INVALID_DOUBLE)) {
     443          232 :         PET.value = pet.second;
     444          232 :         PET.time = pet.first;
     445          232 :         PET.pos = conflictPoint;
     446          464 :         PET.type = PET.value <= 0 ? ENCOUNTER_TYPE_COLLISION : type;
     447          232 :         PET.speed = egoV.distanceTo(Position(0, 0));
     448              :     }
     449      6016079 :     PPETspan.push_back(ppet);
     450      6016079 :     if (ppet != INVALID_DOUBLE && (ppet < minPPET.value || minPPET.value == INVALID_DOUBLE)) {
     451          222 :         minPPET.value = ppet;
     452          222 :         minPPET.time = time;
     453          222 :         minPPET.pos = conflictPoint;
     454          437 :         minPPET.type = ppet <= 0 ? ENCOUNTER_TYPE_COLLISION :  type;
     455          222 :         minPPET.speed = egoV.distanceTo(Position(0, 0));
     456              :     }
     457      6016079 :     MDRACspan.push_back(mdrac);
     458      6016079 :     if (mdrac != INVALID_DOUBLE && (mdrac > maxMDRAC.value || maxMDRAC.value == INVALID_DOUBLE)) {
     459         1216 :         maxMDRAC.value = mdrac;
     460         1216 :         maxMDRAC.time = time;
     461         1216 :         maxMDRAC.pos = conflictPoint;
     462         1216 :         maxMDRAC.type = type;
     463         1216 :         maxMDRAC.speed = egoV.distanceTo(Position(0, 0));
     464              :     }
     465      6016079 : }
     466              : 
     467              : 
     468              : void
     469      6248135 : MSDevice_SSM::Encounter::resetExtraTime(double value) {
     470      6248135 :     remainingExtraTime = value;
     471      6248135 : }
     472              : 
     473              : 
     474              : void
     475       385846 : MSDevice_SSM::Encounter::countDownExtraTime(double amount) {
     476       385846 :     remainingExtraTime -= amount;
     477       385846 : }
     478              : 
     479              : 
     480              : double
     481       432108 : MSDevice_SSM::Encounter::getRemainingExtraTime() const {
     482       432108 :     return remainingExtraTime;
     483              : }
     484              : 
     485              : 
     486      6633981 : MSDevice_SSM::EncounterApproachInfo::EncounterApproachInfo(Encounter* e) :
     487      6633981 :     encounter(e),
     488      6633981 :     type(ENCOUNTER_TYPE_NOCONFLICT_AHEAD),
     489      6633981 :     conflictPoint(Position::INVALID),
     490      6633981 :     egoConflictEntryDist(INVALID_DOUBLE),
     491      6633981 :     foeConflictEntryDist(INVALID_DOUBLE),
     492      6633981 :     egoConflictExitDist(INVALID_DOUBLE),
     493      6633981 :     foeConflictExitDist(INVALID_DOUBLE),
     494      6633981 :     egoEstimatedConflictEntryTime(INVALID_DOUBLE),
     495      6633981 :     foeEstimatedConflictEntryTime(INVALID_DOUBLE),
     496      6633981 :     egoEstimatedConflictExitTime(INVALID_DOUBLE),
     497      6633981 :     foeEstimatedConflictExitTime(INVALID_DOUBLE),
     498      6633981 :     egoConflictAreaLength(INVALID_DOUBLE),
     499      6633981 :     foeConflictAreaLength(INVALID_DOUBLE),
     500      6633981 :     ttc(INVALID_DOUBLE),
     501      6633981 :     drac(INVALID_DOUBLE),
     502      6633981 :     mdrac(INVALID_DOUBLE),
     503      6633981 :     pet(std::make_pair(INVALID_DOUBLE, INVALID_DOUBLE)),
     504      6633981 :     ppet(INVALID_DOUBLE) {
     505      6633981 : }
     506              : 
     507              : 
     508              : void
     509      2238261 : MSDevice_SSM::updateAndWriteOutput() {
     510      2238261 :     if (myHolder.isOnRoad()) {
     511      1735785 :         update();
     512              :         // Write out past conflicts
     513      1735785 :         flushConflicts();
     514              :     } else {
     515              : #ifdef DEBUG_SSM
     516              :         if (DEBUG_COND(myHolderMS))
     517              :             std::cout << "\n" << SIMTIME << " Device '" << getID() << "' updateAndWriteOutput()\n"
     518              :                       << "  Holder is off-road! Calling resetEncounters()."
     519              :                       << std::endl;
     520              : #endif
     521       502476 :         resetEncounters();
     522              :         // Write out past conflicts
     523       502476 :         flushConflicts(true);
     524              :     }
     525      2238261 : }
     526              : 
     527              : void
     528      1735785 : MSDevice_SSM::update() {
     529              : #ifdef DEBUG_SSM
     530              :     if (DEBUG_COND(myHolderMS))
     531              :         std::cout << "\n" << SIMTIME << " Device '" << getID() << "' update()\n"
     532              :                   << "Size of myActiveEncounters: " << myActiveEncounters.size()
     533              :                   << "\nSize of myPastConflicts: " << myPastConflicts.size()
     534              :                   << std::endl;
     535              : #endif
     536              :     // Scan surroundings for other vehicles
     537              :     FoeInfoMap foes;
     538              :     bool scan = true;
     539      1735785 :     if (myEdgeFilterActive) {
     540              :         // Is the ego vehicle inside the filtered edge subset?
     541         2800 :         const MSEdge* egoEdge = &((*myHolderMS).getLane()->getEdge());
     542              :         scan = myEdgeFilter.find(egoEdge) != myEdgeFilter.end();
     543              :     }
     544         2800 :     if (scan) {
     545      1733333 :         findSurroundingVehicles(*myHolderMS, myRange, foes);
     546              :     }
     547              : 
     548              : #ifdef DEBUG_SSM
     549              :     if (DEBUG_COND(myHolderMS)) {
     550              :         if (foes.size() > 0) {
     551              :             std::cout << "Scanned surroundings: Found potential foes:\n";
     552              :             for (FoeInfoMap::const_iterator i = foes.begin(); i != foes.end(); ++i) {
     553              :                 std::cout << i->first->getID() << " ";
     554              :             }
     555              :             std::cout << std::endl;
     556              :         } else {
     557              :             std::cout << "Scanned surroundings: No potential conflict could be identified." << std::endl;
     558              :         }
     559              :     }
     560              : #endif
     561              : 
     562              :     // Update encounters and conflicts -> removes all foes (and deletes corresponding FoeInfos) for which already a corresponding encounter exists
     563      1735785 :     processEncounters(foes);
     564              : 
     565              :     // Make new encounters for all foes, which were not removed by processEncounters (and deletes corresponding FoeInfos)
     566      1735785 :     createEncounters(foes);
     567              :     foes.clear();
     568              : 
     569              :     // Compute "global SSMs" (only computed once per time-step)
     570      1735785 :     computeGlobalMeasures();
     571              : 
     572      1735785 : }
     573              : 
     574              : 
     575              : void
     576      1735785 : MSDevice_SSM::computeGlobalMeasures() {
     577      1735785 :     if (myComputeBR || myComputeSGAP || myComputeTGAP) {
     578        68865 :         myGlobalMeasuresTimeSpan.push_back(SIMTIME);
     579        68865 :         if (myWritePositions) {
     580         1120 :             myGlobalMeasuresPositions.push_back(myHolderMS->getPosition());
     581              :         }
     582        68865 :         if (myWriteLanesPositions) {
     583         1120 :             myGlobalMeasuresLaneIDs.push_back(myHolderMS->getLane()->getID());
     584         1120 :             myGlobalMeasuresLanesPositions.push_back(myHolderMS->getPositionOnLane());
     585              :         }
     586        68865 :         if (myComputeBR) {
     587        68865 :             double br = MAX2(-myHolderMS->getAcceleration(), 0.0);
     588        68865 :             if (br > myMaxBR.second) {
     589         2458 :                 myMaxBR = std::make_pair(std::make_pair(SIMTIME, myHolderMS->getPosition()), br);
     590              :             }
     591        68865 :             myBRspan.push_back(br);
     592              :         }
     593              : 
     594              :         double leaderSearchDist = 0;
     595              :         std::pair<const MSVehicle*, double> leader(nullptr, 0.);
     596        68865 :         if (myComputeSGAP) {
     597        67017 :             leaderSearchDist = myThresholds["SGAP"];
     598              :         }
     599        68865 :         if (myComputeTGAP) {
     600       134034 :             leaderSearchDist = MAX2(leaderSearchDist, myThresholds["TGAP"] * myHolderMS->getSpeed());
     601              :         }
     602              : 
     603        68865 :         if (leaderSearchDist > 0.) {
     604        67017 :             leader = myHolderMS->getLeader(leaderSearchDist);
     605              :         }
     606              : 
     607              :         // negative gap indicates theoretical car-following relationship for paths that cross at an intersection
     608        68865 :         if (myComputeSGAP) {
     609        67017 :             if (leader.first == nullptr || leader.second < 0) {
     610        52772 :                 mySGAPspan.push_back(INVALID_DOUBLE);
     611              :             } else {
     612        14245 :                 double sgap = leader.second + myHolder.getVehicleType().getMinGap();
     613        14245 :                 mySGAPspan.push_back(sgap);
     614        14245 :                 if (sgap < myMinSGAP.first.second) {
     615        13610 :                     myMinSGAP = std::make_pair(std::make_pair(std::make_pair(SIMTIME, myHolderMS->getPosition()), sgap), leader.first->getID());
     616              :                 }
     617              :             }
     618              :         }
     619              : 
     620        68865 :         if (myComputeTGAP) {
     621        67017 :             if (leader.first == nullptr || myHolderMS->getSpeed() == 0. || leader.second < 0) {
     622        52977 :                 myTGAPspan.push_back(INVALID_DOUBLE);
     623              :             } else {
     624        14040 :                 const double tgap = (leader.second + myHolder.getVehicleType().getMinGap()) / myHolderMS->getSpeed();
     625        14040 :                 myTGAPspan.push_back(tgap);
     626        14040 :                 if (tgap < myMinTGAP.first.second) {
     627        14702 :                     myMinTGAP = std::make_pair(std::make_pair(std::make_pair(SIMTIME, myHolderMS->getPosition()), tgap), leader.first->getID());
     628              :                 }
     629              :             }
     630              :         }
     631              : 
     632              :     }
     633      1735785 : }
     634              : 
     635              : 
     636              : void
     637      1735785 : MSDevice_SSM::createEncounters(FoeInfoMap& foes) {
     638              : #ifdef DEBUG_SSM
     639              :     if (DEBUG_COND(myHolderMS)) {
     640              :         std::cout << "\n" << SIMTIME << " Device '" << getID() << "' createEncounters()" << std::endl;
     641              :         std::cout << "New foes:\n";
     642              :         for (FoeInfoMap::const_iterator vi = foes.begin(); vi != foes.end(); ++vi) {
     643              :             std::cout << vi->first->getID() << "\n";
     644              :         }
     645              :         std::cout << std::endl;
     646              :     }
     647              : #endif
     648              : 
     649      2402677 :     for (FoeInfoMap::const_iterator foe = foes.begin(); foe != foes.end(); ++foe) {
     650       666892 :         Encounter* e = new Encounter(myHolderMS, foe->first, SIMTIME, myExtraTime);
     651       666892 :         if (updateEncounter(e, foe->second)) {
     652        49010 :             if (myOldestActiveEncounterBegin == INVALID_DOUBLE) {
     653              :                 assert(myActiveEncounters.empty());
     654         5717 :                 myOldestActiveEncounterBegin = e->begin;
     655              :             }
     656              :             assert(myOldestActiveEncounterBegin <= e->begin);
     657        49010 :             myActiveEncounters.push_back(e);
     658              :         } else {
     659              :             // Discard encounters, where one vehicle already left the conflict area
     660       617882 :             delete e;
     661              :         }
     662              :         // free foeInfo
     663       666892 :         delete foe->second;
     664              :     }
     665      1735785 : }
     666              : 
     667              : 
     668              : void
     669       506344 : MSDevice_SSM::resetEncounters() {
     670              :     // Call processEncounters() with empty vehicle set
     671              :     FoeInfoMap foes;
     672              :     // processEncounters with empty argument closes all encounters
     673       506344 :     processEncounters(foes, true);
     674       506344 : }
     675              : 
     676              : 
     677              : void
     678      2242129 : MSDevice_SSM::processEncounters(FoeInfoMap& foes, bool forceClose) {
     679              : #ifdef DEBUG_SSM
     680              :     if (DEBUG_COND(myHolderMS)) {
     681              :         std::cout << "\n" << SIMTIME << " Device '" << getID() << "' processEncounters(forceClose = " << forceClose << ")" << std::endl;
     682              :         std::cout << "Currently present foes:\n";
     683              :         for (FoeInfoMap::const_iterator vi = foes.begin(); vi != foes.end(); ++vi) {
     684              :             std::cout << vi->first->getID() << "\n";
     685              :         }
     686              :         std::cout << std::endl;
     687              :     }
     688              : #endif
     689              : 
     690              :     // Run through active encounters. If corresponding foe is still present in foes update and
     691              :     // remove foe from foes. If the foe has disappeared close the encounter (check if it qualifies
     692              :     // as a conflict and in case transfer it to myPastConflicts).
     693              :     // Afterwards run through remaining elements in foes and create new encounters for them.
     694              :     EncounterVector::iterator ei = myActiveEncounters.begin();
     695      8255480 :     while (ei != myActiveEncounters.end()) {
     696      6013351 :         Encounter* e = *ei;
     697              :         // check whether foe is still on net
     698      6013351 :         bool foeExists = !(MSNet::getInstance()->getVehicleControl().getVehicle(e->foeID) == nullptr);
     699      6013351 :         if (!foeExists) {
     700         5269 :             e->foe = nullptr;
     701              :         }
     702      6013351 :         if (foes.find(e->foe) != foes.end()) {
     703      5581243 :             FoeInfo* foeInfo = foes[e->foe];
     704      5581243 :             EncounterType prevType = e->currentType;
     705              :             // Update encounter
     706      5581243 :             updateEncounter(e, foeInfo);
     707      5581243 :             if (prevType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA
     708           20 :                     && e->currentType != ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
     709              :                 // The encounter classification switched from BOTH_LEFT to another
     710              :                 // => Start new encounter (i.e. don't erase the foe, don't delete the foeInfo and request closing)
     711              :                 // Note that updateEncounter did not add another trajectory point in this case.
     712              : #ifdef DEBUG_SSM
     713              :                 if (DEBUG_COND(myHolderMS)) {
     714              :                     std::cout << "  Requesting encounter closure because both left conflict area of previous encounter but another encounter lies ahead." << std::endl;
     715              :                 }
     716              : #endif
     717           20 :                 e->closingRequested = true;
     718              :             } else {
     719              :                 // Erase foes which were already encountered and should not be used to open a new conflict
     720      5581223 :                 delete foeInfo;
     721              :                 foes.erase(e->foe);
     722              :             }
     723              :         } else {
     724       432108 :             if (e->getRemainingExtraTime() <= 0. || forceClose || !foeExists) {
     725              :                 // Close encounter, extra time has expired (deletes e if it does not qualify as conflict)
     726              : #ifdef DEBUG_SSM
     727              :                 if (DEBUG_COND(myHolderMS)) {
     728              :                     std::cout << "  Requesting encounter closure because..." << std::endl;
     729              :                     if (e->getRemainingExtraTime() <= 0.) {
     730              :                         std::cout << "  ... extra time elapsed." << std::endl;
     731              :                     } else if (forceClose) {
     732              :                         std::cout << "  ... closing was forced." << std::endl;
     733              :                     } else {
     734              :                         std::cout << "  ... foe disappeared." << std::endl;
     735              :                     }
     736              :                 }
     737              : #endif
     738        46262 :                 e->closingRequested = true;
     739              :             } else {
     740       385846 :                 updateEncounter(e, nullptr); // counts down extra time
     741              :             }
     742              :         }
     743              : 
     744      6013351 :         if (e->closingRequested) {
     745        49010 :             double eBegin = e->begin;
     746        49010 :             closeEncounter(e);
     747        49010 :             ei = myActiveEncounters.erase(ei);
     748        49010 :             if (myActiveEncounters.empty()) {
     749         5717 :                 myOldestActiveEncounterBegin = INVALID_DOUBLE;
     750        43293 :             } else if (eBegin == myOldestActiveEncounterBegin) {
     751              :                 // Erased the oldest encounter, update myOldestActiveEncounterBegin
     752              :                 auto i = myActiveEncounters.begin();
     753        14317 :                 myOldestActiveEncounterBegin = (*i++)->begin;
     754        38330 :                 while (i != myActiveEncounters.end()) {
     755        26418 :                     myOldestActiveEncounterBegin = MIN2(myOldestActiveEncounterBegin, (*i++)->begin);
     756              :                 }
     757              :             }
     758              :         } else {
     759              :             ++ei;
     760              :         }
     761              :     }
     762      2242129 : }
     763              : 
     764              : 
     765              : bool
     766        49010 : MSDevice_SSM::qualifiesAsConflict(Encounter* e) {
     767              :     // Check if conflict measure thresholds are exceeded (to decide whether to keep the encounter for writing out)
     768              : #ifdef DEBUG_SSM
     769              :     if (DEBUG_COND(myHolderMS))
     770              :         std::cout << SIMTIME << " qualifiesAsConflict() for encounter of vehicles '"
     771              :                   << e->egoID << "' and '" << e->foeID
     772              :                   << "'" << std::endl;
     773              : #endif
     774              : 
     775        49230 :     if (myComputePET && e->PET.value != INVALID_DOUBLE && e->PET.value <= myThresholds["PET"]) {
     776              :         return true;
     777              :     }
     778       113673 :     if (myComputeTTC && e->minTTC.value != INVALID_DOUBLE && e->minTTC.value <= myThresholds["TTC"]) {
     779              :         return true;
     780              :     }
     781       104788 :     if (myComputeDRAC && e->maxDRAC.value != INVALID_DOUBLE && e->maxDRAC.value >= myThresholds["DRAC"]) {
     782              :         return true;
     783              :     }
     784        45215 :     if (myComputePPET && e->minPPET.value != INVALID_DOUBLE && e->minPPET.value <= myThresholds["PPET"]) {
     785              :         return true;
     786              :     }
     787        45229 :     if (myComputeMDRAC && e->maxMDRAC.value != INVALID_DOUBLE && e->maxMDRAC.value >= myThresholds["MDRAC"]) {
     788              :         return true;
     789              :     }
     790              :     return false;
     791              : }
     792              : 
     793              : 
     794              : void
     795        49010 : MSDevice_SSM::closeEncounter(Encounter* e) {
     796              :     assert(e->size() > 0);
     797              :     // erase pointers (encounter is stored before being destroyed and pointers could become invalid)
     798        49010 :     e->ego = nullptr;
     799        49010 :     e->foe = nullptr;
     800        49010 :     e->end = e->timeSpan.back();
     801        49010 :     bool wasConflict = qualifiesAsConflict(e);
     802              : #ifdef DEBUG_SSM
     803              :     if (DEBUG_COND(myHolderMS)) {
     804              :         std::cout << SIMTIME << " closeEncounter() of vehicles '"
     805              :                   << e->egoID << "' and '" << e->foeID
     806              :                   << "' (was ranked as " << (wasConflict ? "conflict" : "non-conflict") << ")" << std::endl;
     807              :     }
     808              : #endif
     809        49010 :     if (wasConflict) {
     810         3805 :         myPastConflicts.push(e);
     811              : #ifdef DEBUG_SSM
     812              :         if (!myPastConflicts.empty()) {
     813              :             if (DEBUG_COND(myHolderMS)) {
     814              :                 std::cout << "pastConflictsQueue of veh '" << myHolderMS->getID() << "':\n";
     815              :             }
     816              :             auto myPastConflicts_bak = myPastConflicts;
     817              :             double lastBegin = myPastConflicts.top()->begin;
     818              :             while (!myPastConflicts.empty()) {
     819              :                 auto c = myPastConflicts.top();
     820              :                 myPastConflicts.pop();
     821              :                 if (DEBUG_COND(myHolderMS)) {
     822              :                     std::cout << "  Conflict with foe '" << c->foe << "' (time=" << c->begin << "-" << c->end << ")\n";
     823              :                 }
     824              :                 if (c->begin < lastBegin) {
     825              :                     std::cout << "  Queue corrupt...\n";
     826              :                     assert(false);
     827              :                 }
     828              :                 lastBegin = c->begin;
     829              :             }
     830              :             std::cout << std::endl;
     831              :             myPastConflicts = myPastConflicts_bak;
     832              :         }
     833              : #endif
     834              :     } else {
     835        45205 :         delete e;
     836              :     }
     837        49010 :     return;
     838              : }
     839              : 
     840              : 
     841              : bool
     842      6633981 : MSDevice_SSM::updateEncounter(Encounter* e, FoeInfo* foeInfo) {
     843              : #ifdef DEBUG_ENCOUNTER
     844              :     if (DEBUG_COND_ENCOUNTER(e)) {
     845              :         std::cout << SIMTIME << " updateEncounter() of vehicles '" << e->egoID << "' and '" << e->foeID << "'\n";
     846              :     }
     847              : #endif
     848              :     assert(e->foe != 0);
     849              : 
     850              :     // Struct storing distances (determined in classifyEncounter()) and times to potential conflict entry / exit (in estimateConflictTimes())
     851      6633981 :     EncounterApproachInfo eInfo(e);
     852              : 
     853              :     // Classify encounter type based on the present information
     854              :     // More details on follower/lead relation are determined in a second step below, see estimateConflictTimes()
     855              :     // If a crossing situation is ongoing (i.e. one of the vehicles entered the conflict area already in the last step,
     856              :     // this is handled by passedEncounter by only tracing the vehicle's movements)
     857              :     // The further development of the encounter type is done in checkConflictEntryAndExit()
     858      6633981 :     eInfo.type = classifyEncounter(foeInfo, eInfo);
     859              : 
     860              :     // Discard new encounters, where one vehicle has already left the conflict area
     861      6633981 :     if (eInfo.encounter->size() == 0) {
     862       666892 :         if (eInfo.type == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
     863       666892 :                 || eInfo.type == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA) {
     864              :             // Signalize to discard
     865              :             return false;
     866              :         }
     867              :     }
     868              : 
     869      6633981 :     if (eInfo.type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
     870              :         // At this state, eInfo.type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD implies that the foe
     871              :         // is either out of the device's range or its route does not interfere with the ego's route.
     872              : #ifdef DEBUG_ENCOUNTER
     873              :         if (DEBUG_COND_ENCOUNTER(e)) {
     874              :             std::cout << SIMTIME << " Encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "' does not imply any conflict.\n";
     875              :         }
     876              : #endif
     877      1006658 :         updatePassedEncounter(e, foeInfo, eInfo);
     878              : //        return;
     879      5627323 :     } else if (eInfo.type == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
     880              :                || eInfo.type == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
     881              :                || eInfo.type == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
     882              :                || eInfo.type == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
     883              :                || eInfo.type == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
     884      5627323 :                || eInfo.type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
     885              :         // Ongoing encounter. Treat with update passed encounter (trace covered distances)
     886              :         // eInfo.type only holds the previous type
     887         3585 :         updatePassedEncounter(e, foeInfo, eInfo);
     888              : 
     889              :         // Estimate times until a possible conflict / collision
     890         3585 :         estimateConflictTimes(eInfo);
     891              : 
     892              :     } else {
     893              :         // Estimate times until a possible conflict / collision
     894              :         // Not all are used for all types of encounters:
     895              :         // Follow/lead situation doesn't need them at all, currently (might change if more SSMs are implemented).
     896              :         // Crossing / Merging calculates entry times to determine leader/follower and calculates the exit time for the leader.
     897      5623738 :         estimateConflictTimes(eInfo);
     898              : 
     899              :         // reset the remaining extra time (foe could have re-entered the device range after beginning extra time countdown already)
     900      5623738 :         e->resetExtraTime(myExtraTime);
     901              :     }
     902              : 
     903              :     // update entry/exit times for conflict area
     904      6633981 :     checkConflictEntryAndExit(eInfo);
     905      6633981 :     if (e->size() == 0) {
     906              : #ifdef DEBUG_ENCOUNTER
     907              :         if (DEBUG_COND_ENCOUNTER(e)) {
     908              :             std::cout << SIMTIME << " type when creating encounter: " << eInfo.type << "\n";
     909              :         }
     910              : #endif
     911       666892 :         if (eInfo.type == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
     912              :                 || eInfo.type == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
     913       666892 :                 || eInfo.type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA
     914       666892 :                 || eInfo.type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD
     915        49010 :                 || eInfo.type == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA) {
     916              :             return false;
     917              :         }
     918              :     }
     919              : 
     920              :     // update (x,y)-coords of conflict point
     921      6016099 :     determineConflictPoint(eInfo);
     922              : 
     923              :     // Compute SSMs
     924      6016099 :     computeSSMs(eInfo);
     925              : 
     926      6016099 :     if (e->currentType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA
     927         4699 :             && eInfo.type != ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
     928              :         // Don't add a point which switches back to a different encounter type from a passed encounter.
     929              :         // For this situation this encounter will be closed and a new encounter will be created,
     930              :         // @see correspondingly conditionalized code in processEncounters()
     931           20 :         e->currentType = eInfo.type;
     932              :     } else {
     933              :         // Add current states to trajectories and update type
     934     12032158 :         e->add(SIMTIME, eInfo.type, e->ego->getPosition(), e->ego->getLane()->getID(), e->ego->getPositionOnLane(), e->ego->getVelocityVector(),
     935     18048237 :                e->foe->getPosition(), e->foe->getLane()->getID(), e->foe->getPositionOnLane(), e->foe->getVelocityVector(),
     936     12032158 :                eInfo.conflictPoint, eInfo.egoConflictEntryDist, eInfo.foeConflictEntryDist, eInfo.ttc, eInfo.drac, eInfo.pet, eInfo.ppet, eInfo.mdrac);
     937              :     }
     938              :     // Keep encounter
     939              :     return true;
     940              : }
     941              : 
     942              : 
     943              : void
     944      6016099 : MSDevice_SSM::determineConflictPoint(EncounterApproachInfo& eInfo) {
     945              :     /*  Calculates the (x,y)-coordinate for the eventually predicted conflict point and stores the result in
     946              :      *         eInfo.conflictPoint. In case of MERGING and CROSSING, this is the entry point to conflict area for follower
     947              :      *         In case of FOLLOWING it is the position of leader's back. */
     948              : 
     949              : #ifdef DEBUG_SSM
     950              :     if (DEBUG_COND(eInfo.encounter->ego)) {
     951              :         std::cout << SIMTIME << " determineConflictPoint()" << std::endl;
     952              :     }
     953              : #endif
     954              : 
     955              :     const EncounterType& type = eInfo.type;
     956      6016099 :     const Encounter* e = eInfo.encounter;
     957              :     if (type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA
     958              :             || type == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
     959      6016099 :             || type == ENCOUNTER_TYPE_COLLISION) {
     960              :         // Both vehicles have already past the conflict entry.
     961         7091 :         if (e->size() == 0) {
     962            0 :             eInfo.conflictPoint = e->ego->getPosition();
     963            0 :             WRITE_WARNINGF(TL("SSM device of vehicle '%' encountered an unexpected conflict with foe % at time=%. Please review your vehicle behavior settings."), e->egoID, e->foeID, time2string(SIMSTEP));
     964            0 :             return;
     965              :         }
     966              :         assert(e->size() > 0); // A new encounter should not be created if both vehicles already entered the conflict area
     967         7091 :         eInfo.conflictPoint = e->conflictPointSpan.back();
     968              :     } else if (type == ENCOUNTER_TYPE_CROSSING_FOLLOWER
     969              :                || type == ENCOUNTER_TYPE_MERGING_FOLLOWER
     970              :                || type == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
     971              :                || type == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA) {
     972         4528 :         eInfo.conflictPoint = e->ego->getPositionAlongBestLanes(eInfo.egoConflictEntryDist);
     973              :     } else if (type == ENCOUNTER_TYPE_CROSSING_LEADER
     974              :                || type == ENCOUNTER_TYPE_MERGING_LEADER
     975              :                || type == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
     976              :                || type == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA) {
     977         7688 :         eInfo.conflictPoint = e->foe->getPositionAlongBestLanes(eInfo.foeConflictEntryDist);
     978              :     } else if (type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER) {
     979      2315171 :         eInfo.conflictPoint = e->foe->getPosition(-e->foe->getLength());
     980              :     } else if (type == ENCOUNTER_TYPE_FOLLOWING_LEADER) {
     981      2309877 :         eInfo.conflictPoint = e->ego->getPosition(-e->ego->getLength());
     982              :     } else if (type == ENCOUNTER_TYPE_ONCOMING) {
     983         1841 :         eInfo.conflictPoint = (e->ego->getPosition() + e->foe->getPosition()) * 0.5;
     984              :     } else {
     985              : #ifdef DEBUG_SSM
     986              :         if (DEBUG_COND(eInfo.encounter->ego)) {
     987              :             std::cout << "No conflict point associated with encounter type " << type << std::endl;
     988              :         }
     989              : #endif
     990              :         return;
     991              :     }
     992              : 
     993              : #ifdef DEBUG_SSM
     994              :     if (DEBUG_COND(eInfo.encounter->ego)) {
     995              :         std::cout << "    Conflict at " << eInfo.conflictPoint << std::endl;
     996              :     }
     997              : #endif
     998              : }
     999              : 
    1000              : 
    1001              : void
    1002      5627323 : MSDevice_SSM::estimateConflictTimes(EncounterApproachInfo& eInfo) {
    1003              : 
    1004              :     EncounterType& type = eInfo.type;
    1005      5627323 :     Encounter* e = eInfo.encounter;
    1006              : 
    1007              :     assert(type != ENCOUNTER_TYPE_NOCONFLICT_AHEAD); // arrival times not defined, if no conflict is ahead.
    1008              : #ifdef DEBUG_SSM
    1009              :     if (DEBUG_COND(e->ego))
    1010              :         std::cout << SIMTIME << " estimateConflictTimes() for ego '" << e->egoID << "' and foe '" << e->foeID << "'\n"
    1011              :                   << "    encounter type: " << eInfo.type << "\n"
    1012              :                   << "    egoConflictEntryDist=" << writeNA(eInfo.egoConflictEntryDist)
    1013              :                   << " foeConflictEntryDist=" << writeNA(eInfo.foeConflictEntryDist)
    1014              :                   << " egoConflictExitDist=" << writeNA(eInfo.egoConflictExitDist)
    1015              :                   << " foeConflictExitDist=" << writeNA(eInfo.foeConflictExitDist)
    1016              :                   << "\n    ego speed=" << e->ego->getSpeed()
    1017              :                   << ", foe speed=" << e->foe->getSpeed()
    1018              :                   << std::endl;
    1019              : #endif
    1020      5627323 :     if (type == ENCOUNTER_TYPE_COLLISION) {
    1021              : #ifdef DEBUG_SSM
    1022              :         eInfo.egoEstimatedConflictEntryTime = 0;
    1023              :         eInfo.foeEstimatedConflictEntryTime = 0;
    1024              :         if (DEBUG_COND(e->ego))
    1025              :             std::cout << "    encouter type " << type << " -> no exit times to be calculated."
    1026              :                       << std::endl;
    1027              : #endif
    1028              :         return;
    1029              :     }
    1030              : 
    1031      5627323 :     if (type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER || type == ENCOUNTER_TYPE_FOLLOWING_LEADER || type == ENCOUNTER_TYPE_MERGING_ADJACENT || type == ENCOUNTER_TYPE_ON_ADJACENT_LANES) {
    1032              :         // No need to know the times until ...ConflictDistEntry, currently. They would correspond to an estimated time headway or similar.
    1033              :         // TTC must take into account the movement of the leader, as would DRAC, PET doesn't need the time either, since it uses aposteriori
    1034              :         // values.
    1035              : #ifdef DEBUG_SSM
    1036              :         if (DEBUG_COND(e->ego))
    1037              :             std::cout << "    encouter type " << type << " -> no entry/exit times to be calculated."
    1038              :                       << std::endl;
    1039              : #endif
    1040              :         return;
    1041              :     }
    1042              : 
    1043              :     assert(type == ENCOUNTER_TYPE_MERGING || type == ENCOUNTER_TYPE_CROSSING
    1044              :            || type == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
    1045              :            || type == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
    1046              :            || type == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
    1047              :            || type == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
    1048              :            || type == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
    1049              :            || type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA
    1050              :            || type == ENCOUNTER_TYPE_ONCOMING);
    1051              : 
    1052              :     // Determine exit distances
    1053        13461 :     if (type == ENCOUNTER_TYPE_MERGING || type == ENCOUNTER_TYPE_ONCOMING)  {
    1054         3974 :         eInfo.egoConflictExitDist = eInfo.egoConflictEntryDist + e->ego->getVehicleType().getLength();
    1055         3974 :         eInfo.foeConflictExitDist = eInfo.foeConflictEntryDist + e->foe->getVehicleType().getLength();
    1056              :     } else {
    1057         9487 :         eInfo.egoConflictExitDist = eInfo.egoConflictEntryDist + eInfo.egoConflictAreaLength + e->ego->getVehicleType().getLength();
    1058         9487 :         eInfo.foeConflictExitDist = eInfo.foeConflictEntryDist + eInfo.foeConflictAreaLength + e->foe->getVehicleType().getLength();
    1059              :     }
    1060              : 
    1061              :     // Estimate entry times to stipulate a leader / follower relation for the encounter.
    1062        13461 :     if (eInfo.egoConflictEntryDist > NUMERICAL_EPS) {
    1063        20198 :         eInfo.egoEstimatedConflictEntryTime = e->ego->getCarFollowModel().estimateArrivalTime(eInfo.egoConflictEntryDist, e->ego->getSpeed(), e->ego->getMaxSpeedOnLane(), MIN2(0., e->ego->getAcceleration()));
    1064              :         assert(eInfo.egoEstimatedConflictEntryTime > 0.);
    1065              :     } else {
    1066              :         // ego already entered conflict area
    1067         3362 :         eInfo.egoEstimatedConflictEntryTime = 0.;
    1068              :     }
    1069        13461 :     if (eInfo.foeConflictEntryDist > NUMERICAL_EPS) {
    1070        24300 :         eInfo.foeEstimatedConflictEntryTime = e->foe->getCarFollowModel().estimateArrivalTime(eInfo.foeConflictEntryDist, e->foe->getSpeed(), e->foe->getMaxSpeedOnLane(), MIN2(0., e->foe->getAcceleration()));
    1071              :         assert(eInfo.foeEstimatedConflictEntryTime > 0.);
    1072              :     } else {
    1073              :         // foe already entered conflict area
    1074         1311 :         eInfo.foeEstimatedConflictEntryTime = 0.;
    1075              :     }
    1076              : 
    1077        13461 :     if (type == ENCOUNTER_TYPE_ONCOMING) {
    1078         1841 :         eInfo.egoEstimatedConflictEntryTime = eInfo.egoConflictEntryDist / (e->ego->getSpeed() + e->foe->getSpeed());
    1079         1841 :         eInfo.foeEstimatedConflictEntryTime = eInfo.egoEstimatedConflictEntryTime;
    1080              :     }
    1081              : 
    1082              : #ifdef DEBUG_SSM
    1083              :     if (DEBUG_COND(e->ego))
    1084              :         std::cout << "    Conflict type: " << encounterToString(type) << "\n"
    1085              :                   << "    egoConflictEntryTime=" << (eInfo.egoEstimatedConflictEntryTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.egoEstimatedConflictEntryTime))
    1086              :                   << ", foeConflictEntryTime=" << (eInfo.foeEstimatedConflictEntryTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.foeEstimatedConflictEntryTime))
    1087              :                   << std::endl;
    1088              : #endif
    1089              : 
    1090              :     // Estimate exit times from conflict area for leader / follower.
    1091              :     // assume vehicles start to accelerate after entring the intersection
    1092        13461 :     if (eInfo.egoConflictExitDist >= 0.) {
    1093        22258 :         eInfo.egoEstimatedConflictExitTime = e->ego->getCarFollowModel().estimateArrivalTime(eInfo.egoConflictExitDist, e->ego->getSpeed(), e->ego->getMaxSpeedOnLane(), MIN2(0., e->ego->getAcceleration()));
    1094              :         //eInfo.egoEstimatedConflictExitTime = eInfo.egoEstimatedConflictEntryTime + e->ego->getCarFollowModel().estimateArrivalTime(
    1095              :         //        eInfo.egoConflictExitDist - MAX2(0.0, eInfo.egoConflictEntryDist),
    1096              :         //        e->ego->getSpeed(), e->ego->getMaxSpeedOnLane(),
    1097              :         //        e->ego->getCarFollowModel().getMaxAccel());
    1098              :     } else {
    1099         2332 :         eInfo.egoEstimatedConflictExitTime = 0.;
    1100              :     }
    1101        13461 :     if (eInfo.foeConflictExitDist >= 0.) {
    1102        26650 :         eInfo.foeEstimatedConflictExitTime = e->foe->getCarFollowModel().estimateArrivalTime(eInfo.foeConflictExitDist, e->foe->getSpeed(), e->foe->getMaxSpeedOnLane(), MIN2(0., e->foe->getAcceleration()));
    1103              :         //eInfo.foeEstimatedConflictExitTime = eInfo.foeEstimatedConflictEntryTime + e->foe->getCarFollowModel().estimateArrivalTime(
    1104              :         //        eInfo.foeConflictExitDist - MAX2(0.0, eInfo.foeConflictEntryDist),
    1105              :         //        e->foe->getSpeed(), e->foe->getMaxSpeedOnLane(),
    1106              :         //        e->foe->getCarFollowModel().getMaxAccel());
    1107              :     } else {
    1108          136 :         eInfo.foeEstimatedConflictExitTime = 0.;
    1109              :     }
    1110              : 
    1111        13461 :     if (type == ENCOUNTER_TYPE_ONCOMING) {
    1112         1841 :         eInfo.egoEstimatedConflictExitTime = eInfo.egoEstimatedConflictEntryTime;
    1113         1841 :         eInfo.foeEstimatedConflictExitTime = eInfo.egoEstimatedConflictEntryTime;
    1114              :     }
    1115              : 
    1116        13461 :     if (type != ENCOUNTER_TYPE_MERGING && type != ENCOUNTER_TYPE_CROSSING) {
    1117              :         // this call is issued in context of an ongoing conflict, therefore complete type is already known for the encounter
    1118              :         // (One of EGO_ENTERED_CONFLICT_AREA, FOE_ENTERED_CONFLICT_AREA, EGO_LEFT_CONFLICT_AREA, FOE_LEFT_CONFLICT_AREA, BOTH_ENTERED_CONFLICT_AREA)
    1119              :         // --> no need to specify incomplete encounter type
    1120              :         return;
    1121              :     }
    1122              : 
    1123              :     // For merging and crossing situation, the leader/follower relation not determined by classifyEncounter()
    1124              :     // This is done below based on the estimated conflict entry times
    1125         8035 :     if (eInfo.egoEstimatedConflictEntryTime == 0. && eInfo.foeEstimatedConflictEntryTime == 0. &&
    1126            0 :             eInfo.egoConflictExitDist >= 0 && eInfo.foeConflictExitDist >= 0) {
    1127            0 :         type = ENCOUNTER_TYPE_COLLISION;
    1128            0 :         WRITE_WARNINGF(TL("SSM device of vehicle '%' detected collision with vehicle '%' at time=%."), e->egoID, e->foeID, time2string(SIMSTEP));
    1129         8035 :     } else if (eInfo.egoEstimatedConflictEntryTime == INVALID_DOUBLE && eInfo.foeEstimatedConflictEntryTime == INVALID_DOUBLE) {
    1130          528 :         type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    1131         7507 :     } else if (eInfo.egoEstimatedConflictEntryTime < eInfo.foeEstimatedConflictEntryTime || eInfo.foeEstimatedConflictEntryTime == INVALID_DOUBLE) {
    1132              :         // ego is estimated first at conflict point
    1133              : #ifdef DEBUG_SSM
    1134              :         if (DEBUG_COND(e->ego))
    1135              :             std::cout << "    -> ego is estimated leader at conflict entry."
    1136              :                       << " egoConflictExitTime=" << (eInfo.egoEstimatedConflictExitTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.egoEstimatedConflictExitTime))
    1137              :                       << " egoEstimatedConflictEntryTime=" << (eInfo.egoEstimatedConflictEntryTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.egoEstimatedConflictEntryTime))
    1138              :                       << " foeEstimatedConflictEntryTime=" << (eInfo.foeEstimatedConflictEntryTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.foeEstimatedConflictEntryTime))
    1139              :                       << std::endl;
    1140              : #endif
    1141         5092 :         type = type == ENCOUNTER_TYPE_CROSSING ? ENCOUNTER_TYPE_CROSSING_LEADER : ENCOUNTER_TYPE_MERGING_LEADER;
    1142              :     } else {
    1143              :         // ego is estimated second at conflict point
    1144              : #ifdef DEBUG_SSM
    1145              :         if (DEBUG_COND(e->ego))
    1146              :             std::cout << "    -> foe is estimated leader at conflict entry."
    1147              :                       << " foeConflictExitTime=" << (eInfo.foeEstimatedConflictExitTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.foeEstimatedConflictExitTime))
    1148              :                       << " egoEstimatedConflictEntryTime=" << (eInfo.egoEstimatedConflictEntryTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.egoEstimatedConflictEntryTime))
    1149              :                       << " foeEstimatedConflictEntryTime=" << (eInfo.foeEstimatedConflictEntryTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.foeEstimatedConflictEntryTime))
    1150              :                       << std::endl;
    1151              : #endif
    1152         4020 :         type = type == ENCOUNTER_TYPE_CROSSING ? ENCOUNTER_TYPE_CROSSING_FOLLOWER : ENCOUNTER_TYPE_MERGING_FOLLOWER;
    1153              :     }
    1154              : 
    1155              : }
    1156              : 
    1157              : 
    1158              : 
    1159              : void
    1160      6016099 : MSDevice_SSM::computeSSMs(EncounterApproachInfo& eInfo) const {
    1161              : #ifdef DEBUG_SSM
    1162              :     if (DEBUG_COND(myHolderMS)) {
    1163              :         Encounter* e = eInfo.encounter;
    1164              :         std::cout << SIMTIME << " computeSSMs() for vehicles '"
    1165              :                   << e->ego->getID() << "' and '" << e->foe->getID()
    1166              :                   << "'" << std::endl;
    1167              :     }
    1168              : #endif
    1169              : 
    1170              :     const EncounterType& type = eInfo.type;
    1171              : 
    1172              :     if (type == ENCOUNTER_TYPE_CROSSING_FOLLOWER || type == ENCOUNTER_TYPE_CROSSING_LEADER
    1173              :             || type == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA || type == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
    1174      6016099 :             || type == ENCOUNTER_TYPE_MERGING_FOLLOWER || type == ENCOUNTER_TYPE_MERGING_LEADER
    1175              :             || type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER || type == ENCOUNTER_TYPE_FOLLOWING_LEADER
    1176              :             || type == ENCOUNTER_TYPE_ONCOMING) {
    1177      4635614 :         if (myComputeTTC || myComputeDRAC || myComputePPET || myComputeMDRAC) {
    1178      4633410 :             determineTTCandDRACandPPETandMDRAC(eInfo);
    1179              :         }
    1180      4635614 :         determinePET(eInfo);
    1181              :     } else if (type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
    1182         4911 :         determinePET(eInfo);
    1183              :     } else if (type == ENCOUNTER_TYPE_COLLISION) {
    1184              :         // TODO: handle collision
    1185              :     } else if (type == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA || type == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
    1186              :                || type == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA || type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
    1187              :         // No conflict measures apply for these states, which correspond to intermediate times between
    1188              :         // one vehicle leaving the conflict area and the arrival time for the other (difference corresponds to the PET)
    1189              :     } else if (type == ENCOUNTER_TYPE_ON_ADJACENT_LANES || type == ENCOUNTER_TYPE_MERGING_ADJACENT) {
    1190              :         // No conflict measures apply for this state
    1191              :     } else if (type == ENCOUNTER_TYPE_MERGING_PASSED || type == ENCOUNTER_TYPE_FOLLOWING_PASSED) {
    1192              :         // No conflict measures apply for this state
    1193              :     } else if (type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
    1194              :         // No conflict measures apply for this state
    1195              :     } else {
    1196            0 :         std::stringstream ss;
    1197            0 :         ss << "'" << type << "'";
    1198            0 :         WRITE_WARNING("Unknown or undetermined encounter type at computeSSMs(): " + ss.str());
    1199            0 :     }
    1200              : 
    1201              : #ifdef DEBUG_SSM
    1202              :     if (DEBUG_COND(myHolderMS)) {
    1203              :         Encounter* e = eInfo.encounter;
    1204              :         std::cout << "computeSSMs() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "':\n"
    1205              :                   << "  ttc=" << (eInfo.ttc == INVALID_DOUBLE ? "NA" : ::toString(eInfo.ttc))
    1206              :                   << ", drac=" << (eInfo.drac == INVALID_DOUBLE ? "NA" : ::toString(eInfo.drac))
    1207              :                   << ", pet=" << (eInfo.pet.second == INVALID_DOUBLE ? "NA" : ::toString(eInfo.pet.second))
    1208              :                   << std::endl;
    1209              :     }
    1210              : #endif
    1211      6016099 : }
    1212              : 
    1213              : 
    1214              : void
    1215      4640525 : MSDevice_SSM::determinePET(EncounterApproachInfo& eInfo) const {
    1216      4640525 :     Encounter* e = eInfo.encounter;
    1217      4640525 :     if (e->size() == 0) {
    1218              :         return;
    1219              :     }
    1220              :     const EncounterType& type = eInfo.type;
    1221              :     std::pair<double, double>& pet = eInfo.pet;
    1222              : 
    1223              : #ifdef DEBUG_SSM
    1224              :     if (DEBUG_COND(myHolderMS))
    1225              :         std::cout << SIMTIME << " determinePET() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "'"
    1226              :                   << "(type: " << encounterToString(static_cast<EncounterType>(e->typeSpan.back())) << ")" << std::endl;
    1227              : #endif
    1228              : 
    1229      4593891 :     if (type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER || type == ENCOUNTER_TYPE_FOLLOWING_LEADER) {
    1230              :         // For a following situation, the corresponding PET-value is merely the time-headway.
    1231              :         //       Determining these could be done by comparison of memorized gaps with memorized covered distances
    1232              :         //       Implementation is postponed. Tracing the time gaps (in contrast to crossing PET) corresponds to
    1233              :         //       a vector of values not a single value.
    1234              :         // pass
    1235        14640 :     } else if (type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
    1236         4911 :         EncounterType prevType = static_cast<EncounterType>(e->typeSpan.back());
    1237         4911 :         if (prevType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
    1238              : #ifdef DEBUG_SSM
    1239              :             if (DEBUG_COND(myHolderMS))
    1240              :                 std::cout << "PET for crossing encounter already calculated as " << e->PET.value
    1241              :                           << std::endl;
    1242              : #endif
    1243              :             // pet must have been calculated already
    1244              :             assert(e->PET.value != INVALID_DOUBLE);
    1245              :             return;
    1246              :         }
    1247              : 
    1248              :         // this situation should have emerged from one of the following
    1249              :         assert(prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
    1250              :                || prevType == ENCOUNTER_TYPE_CROSSING_LEADER
    1251              :                || prevType == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
    1252              :                || prevType == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
    1253              :                || prevType == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
    1254              :                || prevType == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
    1255              :                || prevType == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA);
    1256              : 
    1257              : 
    1258              : #ifdef DEBUG_SSM
    1259              :         if (DEBUG_COND(myHolderMS))
    1260              :             std::cout << "e->egoDistsToConflict.back() = " << e->egoDistsToConflict.back()
    1261              :                       << "\ne->egoConflictEntryTime = " << e->egoConflictEntryTime
    1262              :                       << "\ne->egoConflictExitTime = " << e->egoConflictExitTime
    1263              :                       << "\ne->foeDistsToConflict.back() = " << e->foeDistsToConflict.back()
    1264              :                       << "\ne->foeConflictEntryTime = " << e->foeConflictEntryTime
    1265              :                       << "\ne->foeConflictExitTime = " << e->foeConflictExitTime
    1266              :                       << std::endl;
    1267              : #endif
    1268              : 
    1269              :         // But both have passed the conflict area
    1270              :         assert(e->egoConflictEntryTime != INVALID_DOUBLE || e->foeConflictEntryTime != INVALID_DOUBLE);
    1271              : 
    1272              :         // Both have left the conflict region
    1273              :         // (Conflict may have started as one was already within the conflict area - thus the check for invalid entry times)
    1274          232 :         if (e->foeConflictEntryTime == INVALID_DOUBLE || (e->egoConflictEntryTime != INVALID_DOUBLE && e->egoConflictEntryTime > e->foeConflictExitTime)) {
    1275           78 :             pet.first = e->egoConflictEntryTime;
    1276           78 :             pet.second = e->egoConflictEntryTime - e->foeConflictExitTime;
    1277          154 :         } else if (e->egoConflictEntryTime == INVALID_DOUBLE || (e->egoConflictEntryTime != INVALID_DOUBLE && e->foeConflictEntryTime > e->egoConflictExitTime)) {
    1278          154 :             pet.first = e->foeConflictEntryTime;
    1279          154 :             pet.second = e->foeConflictEntryTime - e->egoConflictExitTime;
    1280              :         } else {
    1281              : #ifdef DEBUG_SSM
    1282              :             if (DEBUG_COND(myHolderMS))
    1283              :                 std::cout << "determinePET: Both passed conflict area in the same step. Assume collision"
    1284              :                           << std::endl;
    1285              : #endif
    1286            0 :             pet.first = e->egoConflictEntryTime;
    1287            0 :             pet.second = 0;
    1288              :         }
    1289              : 
    1290              :         // Reset entry and exit times two allow an eventual subsequent re-use
    1291          232 :         e->egoConflictEntryTime = INVALID_DOUBLE;
    1292          232 :         e->egoConflictExitTime = INVALID_DOUBLE;
    1293          232 :         e->foeConflictEntryTime = INVALID_DOUBLE;
    1294          232 :         e->foeConflictExitTime = INVALID_DOUBLE;
    1295              : 
    1296              : #ifdef DEBUG_SSM
    1297              :         if (DEBUG_COND(myHolderMS))
    1298              :             std::cout << "Calculated PET = " << pet.second << " (at t=" << pet.first << ")"
    1299              :                       << std::endl;
    1300              : #endif
    1301              :     } else {
    1302              :         // other cases (merging and pre-crossing situations) do not correspond to a PET calculation.
    1303              : #ifdef DEBUG_SSM
    1304              :         if (DEBUG_COND(myHolderMS))
    1305              :             std::cout << "PET unappropriate for merging and pre-crossing situations. No calculation performed."
    1306              :                       << std::endl;
    1307              : #endif
    1308              :         return;
    1309              :     }
    1310              : }
    1311              : 
    1312              : 
    1313              : void
    1314      4633410 : MSDevice_SSM::determineTTCandDRACandPPETandMDRAC(EncounterApproachInfo& eInfo) const {
    1315      4633410 :     Encounter* e = eInfo.encounter;
    1316              :     const EncounterType& type = eInfo.type;
    1317              :     double& ttc = eInfo.ttc;
    1318              :     double& drac = eInfo.drac;
    1319              :     double& ppet = eInfo.ppet;
    1320              :     double& mdrac = eInfo.mdrac;
    1321              : 
    1322              : #ifdef DEBUG_SSM
    1323              :     if (DEBUG_COND(myHolderMS))
    1324              :         std::cout << SIMTIME << " determineTTCandDRAC() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "' (type = " << eInfo.type << ")"
    1325              :                   << std::endl;
    1326              : #endif
    1327              : 
    1328              :     // Dependent on the actual encounter situation (eInfo.type) calculate the TTC.
    1329              :     // For merging and crossing, different cases occur when a collision during the merging / crossing process is predicted.
    1330      4633410 :     if (type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER) {
    1331      2313671 :         double gap = eInfo.egoConflictEntryDist;
    1332      2313671 :         if (myComputeTTC) {
    1333      2309951 :             ttc = computeTTC(gap, e->ego->getSpeed(), e->foe->getSpeed());
    1334              :         }
    1335      2313671 :         if (myComputeDRAC) {
    1336       597635 :             drac = computeDRAC(gap, e->ego->getSpeed(), e->foe->getSpeed());
    1337              :         }
    1338      2313671 :         if (myComputeMDRAC) {
    1339         8042 :             mdrac = computeMDRAC(gap, e->ego->getSpeed(), e->foe->getSpeed(), myMDRACPRT);
    1340              :         }
    1341              :     } else if (type == ENCOUNTER_TYPE_FOLLOWING_LEADER) {
    1342      2309877 :         double gap = eInfo.foeConflictEntryDist;
    1343      2309877 :         if (myComputeTTC) {
    1344      2309877 :             ttc = computeTTC(gap, e->foe->getSpeed(), e->ego->getSpeed());
    1345              :         }
    1346      2309877 :         if (myComputeDRAC) {
    1347       596221 :             drac = computeDRAC(gap, e->foe->getSpeed(), e->ego->getSpeed());
    1348              :         }
    1349      2309877 :         if (myComputeMDRAC) {
    1350         5328 :             mdrac = computeMDRAC(gap, e->foe->getSpeed(), e->ego->getSpeed(), myMDRACPRT);
    1351              :         }
    1352              :     } else if (type == ENCOUNTER_TYPE_ONCOMING) {
    1353         1841 :         if (myComputeTTC) {
    1354         1841 :             const double dv = e->ego->getSpeed() + e->foe->getSpeed();
    1355         1841 :             if (dv > 0) {
    1356         1841 :                 ttc = eInfo.egoConflictEntryDist / dv;
    1357              :             }
    1358              :         }
    1359              :     } else if (type == ENCOUNTER_TYPE_MERGING_FOLLOWER || type == ENCOUNTER_TYPE_MERGING_LEADER) {
    1360              :         // TODO: calculate more specifically whether a following situation in the merge conflict area
    1361              :         //       is predicted when assuming constant speeds or whether a side collision is predicted.
    1362              :         //       Currently, we ignore any conflict area before the actual merging point of the lanes.
    1363              : 
    1364              :         // linearly extrapolated arrival times at the conflict
    1365              :         // NOTE: These differ from the estimated times stored in eInfo
    1366         1605 :         double egoEntryTime = e->ego->getSpeed() > 0 ? eInfo.egoConflictEntryDist / e->ego->getSpeed() : INVALID_DOUBLE;
    1367         1605 :         double egoExitTime = e->ego->getSpeed() > 0 ? eInfo.egoConflictExitDist / e->ego->getSpeed() : INVALID_DOUBLE;
    1368         1605 :         double foeEntryTime = e->foe->getSpeed() > 0 ? eInfo.foeConflictEntryDist / e->foe->getSpeed() : INVALID_DOUBLE;
    1369         1605 :         double foeExitTime = e->foe->getSpeed() > 0 ? eInfo.foeConflictExitDist / e->foe->getSpeed() : INVALID_DOUBLE;
    1370              : 
    1371              : #ifdef DEBUG_SSM
    1372              :         if (DEBUG_COND(myHolderMS))
    1373              :             std::cout << "   Conflict times with constant speed extrapolation for merging situation:\n   "
    1374              :                       << " egoEntryTime=" << (egoEntryTime == INVALID_DOUBLE ? "NA" : ::toString(egoEntryTime))
    1375              :                       << ", egoExitTime=" << (egoExitTime == INVALID_DOUBLE ? "NA" : ::toString(egoExitTime))
    1376              :                       << ", foeEntryTime=" << (foeEntryTime == INVALID_DOUBLE ? "NA" : ::toString(foeEntryTime))
    1377              :                       << ", foeExitTime=" << (foeExitTime == INVALID_DOUBLE ? "NA" : ::toString(foeExitTime))
    1378              :                       << std::endl;
    1379              : #endif
    1380              : 
    1381              :         // based on that, we obtain
    1382         1605 :         if (egoEntryTime == INVALID_DOUBLE || foeEntryTime == INVALID_DOUBLE) {
    1383              :             // at least one vehicle is stopped
    1384          415 :             ttc = INVALID_DOUBLE;
    1385          415 :             drac = INVALID_DOUBLE;
    1386          415 :             mdrac = INVALID_DOUBLE;
    1387              : #ifdef DEBUG_SSM
    1388              :             if (DEBUG_COND(myHolderMS)) {
    1389              :                 std::cout << "    No TTC and DRAC computed as one vehicle is stopped." << std::endl;
    1390              :             }
    1391              : #endif
    1392          415 :             return;
    1393              :         }
    1394              :         double leaderEntryTime = MIN2(egoEntryTime, foeEntryTime);
    1395              :         double followerEntryTime = MAX2(egoEntryTime, foeEntryTime);
    1396         1190 :         double leaderExitTime = leaderEntryTime == egoEntryTime ? egoExitTime : foeExitTime;
    1397              :         //double followerExitTime = leaderEntryTime==egoEntryTime?foeExitTime:egoExitTime;
    1398         1190 :         double leaderSpeed = leaderEntryTime == egoEntryTime ? e->ego->getSpeed() : e->foe->getSpeed();
    1399         1190 :         double followerSpeed = leaderEntryTime == egoEntryTime ? e->foe->getSpeed() : e->ego->getSpeed();
    1400         1190 :         double leaderConflictDist = leaderEntryTime == egoEntryTime ? eInfo.egoConflictEntryDist : eInfo.foeConflictEntryDist;
    1401         1190 :         double followerConflictDist = leaderEntryTime == egoEntryTime ? eInfo.foeConflictEntryDist : eInfo.egoConflictEntryDist;
    1402         1190 :         double leaderLength = leaderEntryTime == egoEntryTime ? e->ego->getLength() : e->foe->getLength();
    1403         1190 :         if (myComputePPET && followerEntryTime != INVALID_DOUBLE && leaderEntryTime != INVALID_DOUBLE) {
    1404            0 :             ppet = followerEntryTime - leaderExitTime;
    1405              :             //std::cout << " debug1 ppet=" << ppet << "\n";
    1406              :         }
    1407         1190 :         if (leaderExitTime >= followerEntryTime) {
    1408              :             // collision would occur at merge area
    1409          266 :             if (myComputeTTC) {
    1410          266 :                 ttc = computeTTC(followerConflictDist, followerSpeed, 0.);
    1411              :             }
    1412              :             // TODO: Calculate more specific drac for merging case here (complete stop is not always necessary -> see calculation for crossing case)
    1413              :             //       Rather the
    1414          266 :             if (myComputeDRAC) {
    1415          266 :                 drac = computeDRAC(followerConflictDist, followerSpeed, 0.);
    1416              :             }
    1417          266 :             if (myComputeMDRAC) {
    1418            0 :                 mdrac = computeMDRAC(followerConflictDist, followerSpeed, 0., myMDRACPRT);
    1419              :             }
    1420              : 
    1421              : #ifdef DEBUG_SSM
    1422              :             if (DEBUG_COND(myHolderMS))
    1423              :                 std::cout << "    Extrapolation predicts collision *at* merge point with TTC=" << ttc
    1424              :                           << ", drac=" << drac << std::endl;
    1425              : #endif
    1426              : 
    1427              :         } else {
    1428              :             // -> No collision at the merge area
    1429          924 :             if (myComputeTTC) {
    1430              :                 // Check if after merge a collision would occur if speeds are hold constant.
    1431          924 :                 double gapAfterMerge = followerConflictDist - leaderExitTime * followerSpeed;
    1432              :                 assert(gapAfterMerge >= 0);
    1433              : 
    1434              :                 // ttc as for following situation (assumes no collision until leader merged)
    1435          924 :                 double ttcAfterMerge = computeTTC(gapAfterMerge, followerSpeed, leaderSpeed);
    1436          924 :                 ttc = ttcAfterMerge == INVALID_DOUBLE ? INVALID_DOUBLE : leaderExitTime + ttcAfterMerge;
    1437              :             }
    1438          924 :             if (myComputeDRAC) {
    1439              :                 // Intitial gap. (May be negative only if the leader speed is higher than the follower speed, i.e., dv < 0)
    1440          924 :                 double g0 = followerConflictDist - leaderConflictDist - leaderLength;
    1441          924 :                 if (g0 < 0) {
    1442              :                     // Speed difference must be positive if g0<0.
    1443              :                     assert(leaderSpeed - followerSpeed > 0);
    1444              :                     // no deceleration needed for dv>0 and gap after merge >= 0
    1445          740 :                     drac = INVALID_DOUBLE;
    1446              :                 } else {
    1447              :                     // compute drac as for a following situation
    1448          184 :                     drac = computeDRAC(g0, followerSpeed, leaderSpeed);
    1449              :                 }
    1450              :             }
    1451          924 :             if (myComputeMDRAC) {
    1452              :                 // Intitial gap. (May be negative only if the leader speed is higher than the follower speed, i.e., dv < 0)
    1453           96 :                 double g0 = followerConflictDist - leaderConflictDist - leaderLength;
    1454           96 :                 if (g0 < 0) {
    1455              :                     // Speed difference must be positive if g0<0.
    1456              :                     assert(leaderSpeed - followerSpeed > 0);
    1457              :                     // no deceleration needed for dv>0 and gap after merge >= 0
    1458           48 :                     mdrac = INVALID_DOUBLE;
    1459              :                 } else {
    1460              :                     // compute drac as for a following situation
    1461           48 :                     mdrac = computeMDRAC(g0, followerSpeed, leaderSpeed, myMDRACPRT);
    1462              :                 }
    1463              :             }
    1464              : #ifdef DEBUG_SSM
    1465              :             if (DEBUG_COND(myHolderMS)) {
    1466              :                 if (ttc == INVALID_DOUBLE) {
    1467              :                     // assert(dv >= 0);
    1468              :                     assert(drac == INVALID_DOUBLE || drac == 0.0);
    1469              :                     std::cout << "    Extrapolation does not predict any collision." << std::endl;
    1470              :                 } else {
    1471              :                     std::cout << "    Extrapolation predicts collision *after* merge point with TTC="
    1472              :                               << (ttc == INVALID_DOUBLE ? "NA" : ::toString(ttc))
    1473              :                               << ", drac=" << (drac == INVALID_DOUBLE ? "NA" : ::toString(drac)) << std::endl;
    1474              :                 }
    1475              :             }
    1476              : #endif
    1477              : 
    1478              :         }
    1479              : 
    1480              :     } else if (type == ENCOUNTER_TYPE_CROSSING_FOLLOWER
    1481              :                || type == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA) {
    1482         2134 :         if (myComputeDRAC) {
    1483         2134 :             drac = computeDRAC(eInfo);
    1484              :         }
    1485         2134 :         if (eInfo.egoEstimatedConflictEntryTime <= eInfo.foeEstimatedConflictExitTime && eInfo.egoEstimatedConflictEntryTime != INVALID_DOUBLE) {
    1486              :             // follower's predicted arrival at the crossing area is earlier than the leader's predicted exit -> collision predicted
    1487          206 :             double gap = eInfo.egoConflictEntryDist;
    1488          206 :             if (myComputeTTC) {
    1489          206 :                 ttc = computeTTC(gap, e->ego->getSpeed(), 0.);
    1490              :             }
    1491          206 :             if (myComputeMDRAC) {
    1492            6 :                 mdrac = computeMDRAC(gap, e->ego->getSpeed(), 0., myMDRACPRT);
    1493              :             }
    1494              :         } else {
    1495              :             // encounter is expected to happen without collision
    1496         1928 :             ttc = INVALID_DOUBLE;
    1497         1928 :             mdrac = INVALID_DOUBLE;
    1498              :         }
    1499         2134 :         if (myComputePPET) {
    1500          303 :             const double entryTime = eInfo.egoEstimatedConflictEntryTime;
    1501          303 :             const double exitTime = (e->foeConflictExitTime == INVALID_DOUBLE
    1502          303 :                                      ? eInfo.foeEstimatedConflictExitTime : e->foeConflictExitTime);
    1503          303 :             if (entryTime != INVALID_DOUBLE && exitTime != INVALID_DOUBLE) {
    1504           10 :                 ppet = entryTime - exitTime;
    1505              :             }
    1506              :             //std::cout << " debug2 ppet=" << ppet << "\n";
    1507              :         }
    1508              : 
    1509              :     } else if (type == ENCOUNTER_TYPE_CROSSING_LEADER
    1510              :                || type == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA) {
    1511         4282 :         if (myComputeDRAC) {
    1512         3754 :             drac = computeDRAC(eInfo);
    1513              :         }
    1514         4282 :         if (eInfo.foeEstimatedConflictEntryTime <= eInfo.egoEstimatedConflictExitTime && eInfo.foeEstimatedConflictEntryTime != INVALID_DOUBLE) {
    1515              :             // follower's predicted arrival at the crossing area is earlier than the leader's predicted exit -> collision predicted
    1516          363 :             double gap = eInfo.foeConflictEntryDist;
    1517          363 :             if (myComputeTTC) {
    1518          291 :                 ttc = computeTTC(gap, e->foe->getSpeed(), 0.);
    1519              :             }
    1520          363 :             if (myComputeMDRAC) {
    1521          136 :                 mdrac = computeMDRAC(gap, e->foe->getSpeed(), 0., myMDRACPRT);
    1522              :             }
    1523              :         } else {
    1524              :             // encounter is expected to happen without collision
    1525         3919 :             ttc = INVALID_DOUBLE;
    1526         3919 :             mdrac = INVALID_DOUBLE;
    1527              :         }
    1528         4282 :         if (myComputePPET) {
    1529         1208 :             const double entryTime = eInfo.foeEstimatedConflictEntryTime;
    1530         1208 :             const double exitTime = (e->egoConflictExitTime == INVALID_DOUBLE
    1531         1208 :                                      ? eInfo.egoEstimatedConflictExitTime : e->egoConflictExitTime);
    1532         1208 :             if (entryTime != INVALID_DOUBLE && exitTime != INVALID_DOUBLE) {
    1533          331 :                 ppet = entryTime - exitTime;
    1534              :             }
    1535              :             //std::cout << SIMTIME << " debug3 ppet=" << writeNA(ppet)
    1536              :             //    << " fecet=" << writeNA(eInfo.foeEstimatedConflictEntryTime)
    1537              :             //    << " ecet=" << writeNA(e->egoConflictExitTime)
    1538              :             //    << " eecec=" << writeNA(eInfo.egoEstimatedConflictExitTime)
    1539              :             //    << "\n";
    1540              :         }
    1541              :     } else {
    1542              : #ifdef DEBUG_SSM
    1543              :         if (DEBUG_COND(myHolderMS)) {
    1544              :             std::stringstream ss;
    1545              :             ss << "'" << type << "'";
    1546              :             WRITE_WARNING("Underspecified or unknown encounter type in MSDevice_SSM::determineTTCandDRAC(): " + ss.str());
    1547              :         }
    1548              : #endif
    1549              :     }
    1550              : 
    1551              : #ifdef DEBUG_SSM
    1552              :     if (DEBUG_COND(myHolderMS))
    1553              :         std::cout << "ttc=" << (ttc == INVALID_DOUBLE ? "NA" : ::toString(ttc)) << ", drac=" << (drac == INVALID_DOUBLE ? "NA" : ::toString(drac))
    1554              :                   << std::endl;
    1555              : #endif
    1556              : }
    1557              : 
    1558              : 
    1559              : double
    1560      4621515 : MSDevice_SSM::computeTTC(double gap, double followerSpeed, double leaderSpeed) const {
    1561              :     // TODO: in merging situations, the TTC may be lower than the one computed here for following situations
    1562              :     //  (currently only the cross section corresponding to the target lane's begin is considered)
    1563              :     // More specifically, the minimum has to be taken from the two if a collision at merge was predicted.
    1564              : #ifdef DEBUG_SSM
    1565              :     if (DEBUG_COND(myHolderMS))
    1566              :         std::cout << "computeTTC() with gap=" << gap << ", followerSpeed=" << followerSpeed << ", leaderSpeed=" << leaderSpeed
    1567              :                   << std::endl;
    1568              : #endif
    1569      4621515 :     if (gap <= 0.) {
    1570              :         return 0.;    // collision already happend
    1571              :     }
    1572      4621515 :     double dv = followerSpeed - leaderSpeed;
    1573      4621515 :     if (dv <= 0.) {
    1574              :         return INVALID_DOUBLE;    // no collision
    1575              :     }
    1576              : 
    1577      2626875 :     return gap / dv;
    1578              : }
    1579              : 
    1580              : 
    1581              : double
    1582      1195307 : MSDevice_SSM::computeDRAC(double gap, double followerSpeed, double leaderSpeed) {
    1583              : //#ifdef DEBUG_SSM_DRAC
    1584              : //    if (DEBUG_COND)
    1585              : //    std::cout << "computeDRAC() with gap=" << gap << ", followerSpeed=" << followerSpeed << ", leaderSpeed=" << leaderSpeed
    1586              : //              << std::endl;
    1587              : //#endif
    1588      1195307 :     if (gap <= 0.) {
    1589              :         return INVALID_DOUBLE;    // collision!
    1590              :     }
    1591      1195307 :     double dv = followerSpeed - leaderSpeed;
    1592      1195307 :     if (dv <= 0.) {
    1593              :         return 0.0;    // no need to break
    1594              :     }
    1595              :     assert(followerSpeed > 0.);
    1596       624246 :     return 0.5 * dv * dv / gap; // following Guido et al. (2011)
    1597              : }
    1598              : 
    1599              : double
    1600        13560 : MSDevice_SSM::computeMDRAC(double gap, double followerSpeed, double leaderSpeed, double prt) {
    1601              : //#ifdef DEBUG_SSM_DRAC
    1602              : //    if (DEBUG_COND)
    1603              : //    std::cout << "computeMDRAC() with gap=" << gap << ", followerSpeed=" << followerSpeed << ", leaderSpeed=" << leaderSpeed
    1604              : //              << std::endl;
    1605              : //#endif
    1606        13560 :     if (gap <= 0.) {
    1607              :         return INVALID_DOUBLE;    // collision!
    1608              :     }
    1609        13560 :     double dv = followerSpeed - leaderSpeed;
    1610        13560 :     if (dv <= 0.) {
    1611              :         return 0.0;    // no need to brake
    1612              :     }
    1613        11042 :     if (gap / dv == prt) {
    1614              :         return INVALID_DOUBLE;
    1615              :     }
    1616              :     assert(followerSpeed > 0.);
    1617        11042 :     return 0.5 * dv / (gap / dv - prt);
    1618              : }
    1619              : 
    1620              : 
    1621              : double
    1622         5888 : MSDevice_SSM::computeDRAC(const EncounterApproachInfo& eInfo) {
    1623              :     // Introduce concise variable names
    1624         5888 :     double dEntry1 = eInfo.egoConflictEntryDist;
    1625         5888 :     double dEntry2 = eInfo.foeConflictEntryDist;
    1626         5888 :     double dExit1 = eInfo.egoConflictExitDist;
    1627         5888 :     double dExit2 = eInfo.foeConflictExitDist;
    1628         5888 :     double v1 = eInfo.encounter->ego->getSpeed();
    1629         5888 :     double v2 = eInfo.encounter->foe->getSpeed();
    1630         5888 :     double tEntry1 = eInfo.egoEstimatedConflictEntryTime;
    1631         5888 :     double tEntry2 = eInfo.foeEstimatedConflictEntryTime;
    1632         5888 :     double tExit1 = eInfo.egoEstimatedConflictExitTime;
    1633         5888 :     double tExit2 = eInfo.foeEstimatedConflictExitTime;
    1634              : #ifdef DEBUG_SSM_DRAC
    1635              :     if (DEBUG_COND(eInfo.encounter->ego))
    1636              :         std::cout << SIMTIME << "computeDRAC() with"
    1637              :                   << "\ndEntry1=" << dEntry1 << ", dEntry2=" << dEntry2
    1638              :                   << ", dExit1=" << dExit1 << ", dExit2=" << dExit2
    1639              :                   << ",\nv1=" << v1 << ", v2=" << v2
    1640              :                   << "\ntEntry1=" << (tEntry1 == INVALID_DOUBLE ? "NA" : ::toString(tEntry1)) << ", tEntry2=" << (tEntry2 == INVALID_DOUBLE ? "NA" : ::toString(tEntry2))
    1641              :                   << ", tExit1=" << (tExit1 == INVALID_DOUBLE ? "NA" : ::toString(tExit1)) << ", tExit2=" << (tExit2 == INVALID_DOUBLE ? "NA" : ::toString(tExit2))
    1642              :                   << std::endl;
    1643              : #endif
    1644         5888 :     if (dExit1 <= 0. || dExit2 <= 0.) {
    1645              :         // At least one vehicle already left or is not about to enter conflict area at all => no breaking needed.
    1646              : #ifdef DEBUG_SSM_DRAC
    1647              :         if (DEBUG_COND(eInfo.encounter->ego)) {
    1648              :             std::cout << "One already left conflict area -> drac == 0." << std::endl;
    1649              :         }
    1650              : #endif
    1651              :         return 0.;
    1652              :     }
    1653         5888 :     if (dEntry1 <= 0. && dEntry2 <= 0.) {
    1654              :         // collision... (both already entered conflict area but none left)
    1655              : #ifdef DEBUG_SSM_DRAC
    1656              :         if (DEBUG_COND(eInfo.encounter->ego)) {
    1657              :             std::cout << "Both entered conflict area but neither left. -> collision!" << std::endl;
    1658              :         }
    1659              : #endif
    1660              :         return INVALID_DOUBLE;
    1661              :     }
    1662              : 
    1663              :     double drac = std::numeric_limits<double>::max();
    1664         5888 :     if (dEntry1 > 0.) {
    1665              :         // vehicle 1 could break
    1666              : #ifdef DEBUG_SSM_DRAC
    1667              :         if (DEBUG_COND(eInfo.encounter->ego)) {
    1668              :             std::cout << "Ego could break..." << std::endl;
    1669              :         }
    1670              : #endif
    1671         5020 :         if (tExit2 != INVALID_DOUBLE) {
    1672              :             // Vehicle 2 is expected to leave conflict area at t2
    1673         3041 :             drac = MIN2(drac, 2 * (v1 - dEntry1 / tExit2) / tExit2);
    1674              : #ifdef DEBUG_SSM_DRAC
    1675              :             if (DEBUG_COND(eInfo.encounter->ego)) {
    1676              :                 std::cout << "  Foe expected to leave in " << tExit2 << "-> Ego needs drac=" << drac << std::endl;
    1677              :             }
    1678              : #endif
    1679              :         } else {
    1680              :             // Vehicle 2 is expected to stop on conflict area or earlier
    1681         1979 :             if (tEntry2 != INVALID_DOUBLE) {
    1682              :                 // ... on conflict area => veh1 has to stop before entry
    1683          112 :                 drac = MIN2(drac, computeDRAC(dEntry1, v1, 0));
    1684              : #ifdef DEBUG_SSM_DRAC
    1685              :                 if (DEBUG_COND(eInfo.encounter->ego)) {
    1686              :                     std::cout << "  Foe is expected stop on conflict area -> Ego needs drac=" << drac << std::endl;
    1687              :                 }
    1688              : #endif
    1689              :             } else {
    1690              :                 // ... before conflict area
    1691              : #ifdef DEBUG_SSM_DRAC
    1692              :                 if (DEBUG_COND(eInfo.encounter->ego)) {
    1693              :                     std::cout << "  Foe is expected stop before conflict area -> no drac computation for ego (will be done for foe if applicable)" << std::endl;
    1694              :                 }
    1695              : #endif
    1696              :             }
    1697              :         }
    1698              :     }
    1699              : 
    1700         5888 :     if (dEntry2 > 0.) {
    1701              :         // vehicle 2 could break
    1702              : #ifdef DEBUG_SSM_DRAC
    1703              :         if (DEBUG_COND(eInfo.encounter->ego)) {
    1704              :             std::cout << "Foe could break..." << std::endl;
    1705              :         }
    1706              : #endif
    1707         5530 :         if (tExit1 != INVALID_DOUBLE) {
    1708              :             // Vehicle 1 is expected to leave conflict area at t1
    1709              : #ifdef DEBUG_SSM_DRAC
    1710              :             if (DEBUG_COND(eInfo.encounter->ego)) {
    1711              :                 std::cout << "  Ego expected to leave in " << tExit1 << "-> Foe needs drac=" << (2 * (v2 - dEntry2 / tExit1) / tExit1) << std::endl;
    1712              :             }
    1713              : #endif
    1714         3409 :             drac = MIN2(drac, 2 * (v2 - dEntry2 / tExit1) / tExit1);
    1715              :         } else {
    1716              :             // Vehicle 1 is expected to stop on conflict area or earlier
    1717         2121 :             if (tEntry1 != INVALID_DOUBLE) {
    1718              :                 // ... on conflict area => veh2 has to stop before entry
    1719              : #ifdef DEBUG_SSM_DRAC
    1720              :                 if (DEBUG_COND(eInfo.encounter->ego)) {
    1721              :                     std::cout << "  Ego is expected stop on conflict area -> Foe needs drac=" << computeDRAC(dEntry2, v2, 0) << std::endl;
    1722              :                 }
    1723              : #endif
    1724          889 :                 drac = MIN2(drac, computeDRAC(dEntry2, v2, 0));
    1725              :             } else {
    1726              :                 // ... before conflict area
    1727              : #ifdef DEBUG_SSM_DRAC
    1728              :                 if (DEBUG_COND(eInfo.encounter->ego)) {
    1729              :                     std::cout << "  Ego is expected stop before conflict area -> no drac computation for foe (done for ego if applicable)" << std::endl;
    1730              :                 }
    1731              : #endif
    1732              :             }
    1733              :         }
    1734              :     }
    1735              : 
    1736         5888 :     return drac > 0 ? drac : INVALID_DOUBLE;
    1737              : }
    1738              : 
    1739              : void
    1740      6633981 : MSDevice_SSM::checkConflictEntryAndExit(EncounterApproachInfo& eInfo) {
    1741              :     // determine exact entry and exit times
    1742      6633981 :     Encounter* e = eInfo.encounter;
    1743              : 
    1744      6633981 :     const bool foePastConflictEntry = eInfo.foeConflictEntryDist < 0.0 && eInfo.foeConflictEntryDist != INVALID_DOUBLE;
    1745      6633981 :     const bool egoPastConflictEntry = eInfo.egoConflictEntryDist < 0.0 && eInfo.egoConflictEntryDist != INVALID_DOUBLE;
    1746      6633981 :     const bool foePastConflictExit = eInfo.foeConflictExitDist < 0.0 && eInfo.foeConflictExitDist != INVALID_DOUBLE;
    1747      6633981 :     const bool egoPastConflictExit = eInfo.egoConflictExitDist < 0.0 && eInfo.egoConflictExitDist != INVALID_DOUBLE;
    1748              : 
    1749              : #ifdef DEBUG_ENCOUNTER
    1750              :     if (DEBUG_COND_ENCOUNTER(e)) {
    1751              :         std::cout << SIMTIME << " checkConflictEntryAndExit() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "'"
    1752              :                   << "  foeEntryDist=" << eInfo.foeConflictEntryDist
    1753              :                   << "  egoEntryDist=" << eInfo.egoConflictEntryDist
    1754              :                   << "  foeExitDist=" << eInfo.foeConflictExitDist
    1755              :                   << "  egoExitDist=" << eInfo.egoConflictExitDist
    1756              :                   << "\n";
    1757              :     }
    1758              : #endif
    1759              : 
    1760              : 
    1761      6633981 :     if (e->size() == 0) {
    1762              :         // This is a new conflict (or a conflict that was considered earlier
    1763              :         // but disregarded due to being 'over')
    1764              : 
    1765       666892 :         if (egoPastConflictExit) {
    1766            0 :             if (foePastConflictExit) {
    1767            0 :                 eInfo.type = ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA;
    1768            0 :             } else if (foePastConflictEntry) {
    1769            0 :                 eInfo.type = ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA;
    1770              :             } else {
    1771            0 :                 eInfo.type = ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA;
    1772              :             }
    1773       666892 :         } else if (foePastConflictExit) {
    1774            0 :             if (egoPastConflictEntry) {
    1775            0 :                 eInfo.type = ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA;
    1776              :             } else {
    1777            0 :                 eInfo.type = ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA;
    1778              :             }
    1779              :         } else {
    1780              :             // No one left conflict area
    1781       666892 :             if (egoPastConflictEntry) {
    1782           10 :                 if (foePastConflictEntry) {
    1783            0 :                     eInfo.type = ENCOUNTER_TYPE_COLLISION;
    1784              :                 } else {
    1785           10 :                     eInfo.type = ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA;
    1786              :                 }
    1787       666882 :             } else if (foePastConflictEntry) {
    1788            0 :                 eInfo.type = ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA;
    1789              :             }
    1790              :             // else: both before conflict, keep current type
    1791              :         }
    1792       666892 :         return;
    1793              :     }
    1794              : 
    1795              :     // Distances to conflict area boundaries in previous step
    1796      5967089 :     double prevEgoConflictEntryDist = eInfo.egoConflictEntryDist + e->ego->getLastStepDist();
    1797      5967089 :     double prevFoeConflictEntryDist = eInfo.foeConflictEntryDist + e->foe->getLastStepDist();
    1798      5967089 :     double prevEgoConflictExitDist = prevEgoConflictEntryDist + eInfo.egoConflictAreaLength + e->ego->getLength();
    1799      5967089 :     double prevFoeConflictExitDist = prevFoeConflictEntryDist + eInfo.foeConflictAreaLength + e->foe->getLength();
    1800      5967089 :     EncounterType prevType = e->currentType;
    1801              : 
    1802              : //#ifdef DEBUG_ENCOUNTER
    1803              : //    if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    1804              : //        std::cout << "\nEgo's prev distance to conflict entry: " << prevEgoConflictEntryDist
    1805              : //                  << "\nEgo's prev distance to conflict exit:  " << prevEgoConflictExitDist
    1806              : //                  << "\nFoe's prev distance to conflict entry: " << prevFoeConflictEntryDist
    1807              : //                  << "\nFoe's prev distance to conflict exit:  " << prevFoeConflictExitDist
    1808              : //                  << std::endl;
    1809              : //#endif
    1810              : 
    1811              :     // Check if ego entered in last step
    1812      5967089 :     if (e->egoConflictEntryTime == INVALID_DOUBLE && egoPastConflictEntry && prevEgoConflictEntryDist >= 0) {
    1813              :         // ego must have entered the conflict in the last step. Determine exact entry time
    1814          236 :         e->egoConflictEntryTime = SIMTIME - TS + MSCFModel::passingTime(-prevEgoConflictEntryDist, 0., -eInfo.egoConflictEntryDist, e->ego->getPreviousSpeed(), e->ego->getSpeed());
    1815              : #ifdef DEBUG_ENCOUNTER
    1816              :         if (DEBUG_COND_ENCOUNTER(e)) {
    1817              :             std::cout << "    ego entered conflict area at t=" << e->egoConflictEntryTime << std::endl;
    1818              :         }
    1819              : #endif
    1820              :         // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
    1821          236 :         if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
    1822          236 :                 || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
    1823          154 :             eInfo.type = ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA;
    1824              :         }
    1825              :     }
    1826              : 
    1827              :     // Check if foe entered in last step
    1828      5967089 :     if (e->foeConflictEntryTime == INVALID_DOUBLE && foePastConflictEntry && prevFoeConflictEntryDist >= 0) {
    1829              :         // foe must have entered the conflict in the last step. Determine exact entry time
    1830          252 :         e->foeConflictEntryTime = SIMTIME - TS + MSCFModel::passingTime(-prevFoeConflictEntryDist, 0., -eInfo.foeConflictEntryDist, e->foe->getPreviousSpeed(), e->foe->getSpeed());
    1831              : #ifdef DEBUG_ENCOUNTER
    1832              :         if (DEBUG_COND_ENCOUNTER(e)) {
    1833              :             std::cout << "    foe entered conflict area at t=" << e->foeConflictEntryTime << std::endl;
    1834              :         }
    1835              : #endif
    1836              :         // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
    1837          252 :         if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
    1838          252 :                 || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
    1839           94 :             eInfo.type = ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA;
    1840              :         }
    1841              :     }
    1842              : 
    1843              :     // Check if ego left conflict area
    1844      5967089 :     if (e->egoConflictExitTime == INVALID_DOUBLE && eInfo.egoConflictExitDist < 0 && prevEgoConflictExitDist >= 0) {
    1845              :         // ego must have left the conflict area in the last step. Determine exact exit time
    1846          242 :         e->egoConflictExitTime = SIMTIME - TS + MSCFModel::passingTime(-prevEgoConflictExitDist, 0., -eInfo.egoConflictExitDist, e->ego->getPreviousSpeed(), e->ego->getSpeed());
    1847              :         // Add cross section to calculate PET for foe
    1848              : //        e->foePETCrossSections.push_back(std::make_pair(eInfo.foeConflictEntryCrossSection, e->egoConflictExitTime));
    1849              : #ifdef DEBUG_ENCOUNTER
    1850              :         if (DEBUG_COND_ENCOUNTER(e)) {
    1851              :             std::cout << "    ego left conflict area at t=" << e->egoConflictExitTime << std::endl;
    1852              :         }
    1853              : #endif
    1854              :         // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
    1855          242 :         if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
    1856          242 :                 || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
    1857            0 :             eInfo.type = ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA;
    1858              :         }
    1859              :     }
    1860              : 
    1861              :     // Check if foe left conflict area
    1862      5967089 :     if (e->foeConflictExitTime == INVALID_DOUBLE && eInfo.foeConflictExitDist < 0 && prevFoeConflictExitDist >= 0) {
    1863              :         // foe must have left the conflict area in the last step. Determine exact exit time
    1864          244 :         e->foeConflictExitTime = SIMTIME - TS + MSCFModel::passingTime(-prevFoeConflictExitDist, 0., -eInfo.foeConflictExitDist, e->foe->getPreviousSpeed(), e->foe->getSpeed());
    1865              :         // Add cross section to calculate PET for ego
    1866              : //        e->egoPETCrossSections.push_back(std::make_pair(eInfo.egoConflictEntryCrossSection, e->foeConflictExitTime));
    1867              : #ifdef DEBUG_ENCOUNTER
    1868              :         if (DEBUG_COND_ENCOUNTER(e)) {
    1869              :             std::cout << "    foe left conflict area at t=" << e->foeConflictExitTime << std::endl;
    1870              :         }
    1871              : #endif
    1872              :         // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
    1873          244 :         if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
    1874          244 :                 || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
    1875           12 :             eInfo.type = ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA;
    1876              :         }
    1877              :     }
    1878              : }
    1879              : 
    1880              : 
    1881              : void
    1882      1010243 : MSDevice_SSM::updatePassedEncounter(Encounter* e, FoeInfo* foeInfo, EncounterApproachInfo& eInfo) {
    1883              : 
    1884              : #ifdef DEBUG_ENCOUNTER
    1885              :     if (DEBUG_COND_ENCOUNTER(e)) {
    1886              :         std::cout << SIMTIME << " updatePassedEncounter() for vehicles '" << e->egoID << "' and '" << e->foeID << "'\n";
    1887              :     }
    1888              : #endif
    1889              : 
    1890      1010243 :     if (foeInfo == nullptr) {
    1891              :         // the foe is out of the device's range, proceed counting down the remaining extra time to trace
    1892       385846 :         e->countDownExtraTime(TS);
    1893              : #ifdef DEBUG_ENCOUNTER
    1894              :         if (DEBUG_COND_ENCOUNTER(e)) std::cout << "    Foe is out of range. Counting down extra time."
    1895              :                                                    << " Remaining seconds before closing encounter: " << e->getRemainingExtraTime() << std::endl;
    1896              : #endif
    1897              : 
    1898              :     } else {
    1899              :         // reset the remaining extra time (foe could have re-entered the device range after beginning extra time countdown already)
    1900       624397 :         e->resetExtraTime(myExtraTime);
    1901              :     }
    1902              : 
    1903              :     // Check, whether this was really a potential conflict at some time:
    1904              :     // Search through typeSpan for a type other than no conflict
    1905      1010243 :     EncounterType lastPotentialConflictType = e->typeSpan.size() > 0 ? static_cast<EncounterType>(e->typeSpan.back()) : ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    1906              : 
    1907              :     if (lastPotentialConflictType == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
    1908              :         // This encounter was no conflict in the last step -> remains so
    1909              : #ifdef DEBUG_ENCOUNTER
    1910              :         if (DEBUG_COND_ENCOUNTER(e)) {
    1911              :             std::cout << "    This encounter wasn't classified as a potential conflict lately.\n";
    1912              :         }
    1913              : #endif
    1914       620858 :         if (foeInfo == nullptr) {
    1915              :             // Encounter was either never a potential conflict and foe is out of range
    1916              :             // or the foe has left the network
    1917              :             // -> no use in further tracing this encounter
    1918              : #ifdef DEBUG_SSM
    1919              :             if (DEBUG_COND(myHolderMS)) {
    1920              :                 std::cout << "  Requesting encounter closure because foeInfo==nullptr" << std::endl;
    1921              :             }
    1922              : #endif
    1923         2728 :             e->closingRequested = true;
    1924              : #ifdef DEBUG_ENCOUNTER
    1925              :             if (DEBUG_COND_ENCOUNTER(e)) {
    1926              :                 std::cout << "    Closing encounter.\n";
    1927              :             }
    1928              : #endif
    1929         2728 :             eInfo.type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    1930              :         }
    1931              :     } else if (lastPotentialConflictType == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER
    1932              :                || lastPotentialConflictType == ENCOUNTER_TYPE_FOLLOWING_LEADER
    1933              :                || lastPotentialConflictType == ENCOUNTER_TYPE_FOLLOWING_PASSED) {
    1934              :         // if a following situation leads to a no-conflict situation this encounter switches no-conflict, since no further computations (PET) are needed.
    1935       374801 :         eInfo.type = ENCOUNTER_TYPE_FOLLOWING_PASSED;
    1936              : #ifdef DEBUG_ENCOUNTER
    1937              :         if (DEBUG_COND_ENCOUNTER(e)) {
    1938              :             std::cout << "    Encounter was previously classified as a follow/lead situation.\n";
    1939              :         }
    1940              : #endif
    1941              :     } else if (lastPotentialConflictType == ENCOUNTER_TYPE_MERGING_FOLLOWER
    1942              :                || lastPotentialConflictType == ENCOUNTER_TYPE_MERGING_LEADER
    1943              :                || lastPotentialConflictType == ENCOUNTER_TYPE_MERGING_PASSED) {
    1944              :         // if a merging situation leads to a no-conflict situation the leader was either removed from the net (we disregard special treatment)
    1945              :         // or route- or lane-changes removed the conflict.
    1946            0 :         eInfo.type = ENCOUNTER_TYPE_MERGING_PASSED;
    1947              : #ifdef DEBUG_ENCOUNTER
    1948              :         if (DEBUG_COND_ENCOUNTER(e)) {
    1949              :             std::cout << "    Encounter was previously classified as a merging situation.\n";
    1950              :         }
    1951              : #endif
    1952              :     }
    1953      1010243 :     if (lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
    1954              :             || lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_LEADER
    1955              :             || lastPotentialConflictType == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
    1956      1010243 :             || lastPotentialConflictType == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
    1957      1010243 :             || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
    1958              :             || lastPotentialConflictType == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
    1959      1006601 :             || lastPotentialConflictType == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
    1960      1003124 :             || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA
    1961      1003124 :             || lastPotentialConflictType == ENCOUNTER_TYPE_COLLISION) {
    1962              :         // Encounter has been a crossing situation.
    1963              : 
    1964              : #ifdef DEBUG_ENCOUNTER
    1965              :         if (DEBUG_COND_ENCOUNTER(e)) {
    1966              :             std::cout << "    Encounter was previously classified as a crossing situation of type " << lastPotentialConflictType << ".\n";
    1967              :         }
    1968              : #endif
    1969              :         // For passed encounters, the xxxConflictAreaLength variables are not determined before -> we use the stored values.
    1970              : 
    1971              :         // TODO: This could also more precisely be calculated wrt the angle of the crossing *at the conflict point*
    1972        11798 :         if (eInfo.egoConflictAreaLength == INVALID_DOUBLE) {
    1973        11798 :             eInfo.egoConflictAreaLength = e->foe->getWidth();
    1974              :         }
    1975        11798 :         if (eInfo.foeConflictAreaLength == INVALID_DOUBLE) {
    1976        11798 :             eInfo.foeConflictAreaLength = e->ego->getWidth();
    1977              :         }
    1978              : 
    1979        11798 :         eInfo.egoConflictEntryDist = e->egoDistsToConflict.back() - e->ego->getLastStepDist();
    1980        11798 :         eInfo.egoConflictExitDist = eInfo.egoConflictEntryDist + eInfo.egoConflictAreaLength + e->ego->getLength();
    1981        11798 :         eInfo.foeConflictEntryDist = e->foeDistsToConflict.back() - e->foe->getLastStepDist();
    1982        11798 :         eInfo.foeConflictExitDist = eInfo.foeConflictEntryDist + eInfo.foeConflictAreaLength + e->foe->getLength();
    1983              : 
    1984              : #ifdef DEBUG_ENCOUNTER
    1985              :         if (DEBUG_COND_ENCOUNTER(e))
    1986              :             std::cout << "    egoConflictEntryDist = " << eInfo.egoConflictEntryDist
    1987              :                       << ", egoConflictExitDist = " << eInfo.egoConflictExitDist
    1988              :                       << "\n    foeConflictEntryDist = " << eInfo.foeConflictEntryDist
    1989              :                       << ", foeConflictExitDist = " << eInfo.foeConflictExitDist
    1990              :                       << std::endl;
    1991              : #endif
    1992              : 
    1993              :         // Determine actual encounter type
    1994        11798 :         bool egoEnteredConflict = eInfo.egoConflictEntryDist < 0.;
    1995        11798 :         bool foeEnteredConflict = eInfo.foeConflictEntryDist < 0.;
    1996        11798 :         bool egoLeftConflict = eInfo.egoConflictExitDist < 0.;
    1997        11798 :         bool foeLeftConflict = eInfo.foeConflictExitDist < 0.;
    1998        11798 :         if ((!egoEnteredConflict) && !foeEnteredConflict) {
    1999              :             // XXX: do we need to recompute the follow/lead order, here?
    2000              :             assert(lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
    2001              :                    || lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_LEADER);
    2002           24 :             eInfo.type = lastPotentialConflictType;
    2003        11774 :         } else if (egoEnteredConflict && !foeEnteredConflict) {
    2004         3170 :             eInfo.type = ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA;
    2005         8604 :         } else if ((!egoEnteredConflict) && foeEnteredConflict) {
    2006         1513 :             eInfo.type = ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA;
    2007              :         } else { // (egoEnteredConflict && foeEnteredConflict) {
    2008         7091 :             eInfo.type = ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA;
    2009              :         }
    2010              : 
    2011        11798 :         if ((!egoLeftConflict) && !foeLeftConflict) {
    2012         1224 :             if (eInfo.type == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA) {
    2013            0 :                 eInfo.type = ENCOUNTER_TYPE_COLLISION;
    2014              :             }
    2015        10574 :         } else if (egoLeftConflict && !foeLeftConflict) {
    2016         3678 :             if (eInfo.type != ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA) {
    2017         2278 :                 eInfo.type = ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA;
    2018              :             }
    2019         6896 :         } else if ((!egoLeftConflict) && foeLeftConflict) {
    2020         1985 :             if (eInfo.type != ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA) {
    2021         1205 :                 eInfo.type = ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA;
    2022              :             }
    2023              :         } else {
    2024         4911 :             eInfo.type = ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA;
    2025              :             // It should not occur that both leave the conflict at the same step
    2026              :             assert(lastPotentialConflictType == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
    2027              :                    || lastPotentialConflictType == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
    2028              :                    || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
    2029              :                    || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA);
    2030              :         }
    2031              : 
    2032              :         // TODO: adjust the conflict distances according to lateral movement for single ENTERED-cases
    2033              : 
    2034              : #ifdef DEBUG_ENCOUNTER
    2035              :         if (DEBUG_COND_ENCOUNTER(e)) {
    2036              :             std::cout << "    Updated classification: " << eInfo.type << "\n";
    2037              :         }
    2038              : #endif
    2039              :     }
    2040      1010243 : }
    2041              : 
    2042              : 
    2043              : MSDevice_SSM::EncounterType
    2044      6633981 : MSDevice_SSM::classifyEncounter(const FoeInfo* foeInfo, EncounterApproachInfo& eInfo)  const {
    2045              : #ifdef DEBUG_ENCOUNTER
    2046              :     if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2047              :         std::cout << "classifyEncounter() called.\n";
    2048              :     }
    2049              : #endif
    2050      6633981 :     if (foeInfo == nullptr) {
    2051              :         // foeInfo == 0 signalizes, that no corresponding foe info was returned by findSurroundingVehicles(),
    2052              :         // i.e. the foe is actually out of range (This may also mean that it has left the network)
    2053              :         return ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2054              :     }
    2055      6248135 :     const Encounter* e = eInfo.encounter;
    2056              : 
    2057              :     // previous classification (if encounter was not just created)
    2058      6248135 :     EncounterType prevType = e->typeSpan.size() > 0 ? static_cast<EncounterType>(e->typeSpan.back()) : ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2059              :     if (e->typeSpan.size() > 0
    2060      6248135 :             && (prevType == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
    2061              :                 || prevType == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
    2062              :                 || prevType == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
    2063              :                 || prevType == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
    2064      5581243 :                 || prevType == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA)) {
    2065              :         // This is an ongoing crossing situation with at least one of the vehicles not
    2066              :         // having passed the conflict area.
    2067              :         // -> Merely trace the change of distances to the conflict entry / exit
    2068              :         // -> Derefer this to updatePassedEncounter, where this is done anyhow.
    2069              : #ifdef DEBUG_ENCOUNTER
    2070              :         if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2071              :             std::cout << "    Ongoing crossing conflict will be traced by passedEncounter().\n";
    2072              :         }
    2073              : #endif
    2074              :         return prevType;
    2075              :     }
    2076              : 
    2077              : 
    2078              :     // Ego's current Lane
    2079      6244550 :     const MSLane* egoLane = e->ego->getLane();
    2080              :     // Foe's current Lane
    2081      6244550 :     const MSLane* foeLane = e->foe->getLane();
    2082              : 
    2083              :     // Ego's conflict lane is memorized in foeInfo
    2084      6244550 :     const MSLane* egoConflictLane = foeInfo->egoConflictLane;
    2085      6244550 :     double egoDistToConflictLane = foeInfo->egoDistToConflictLane;
    2086              :     // Find conflicting lane and the distance to its entry link for the foe
    2087              :     double foeDistToConflictLane;
    2088      6244550 :     const MSLane* foeConflictLane = findFoeConflictLane(e->foe, foeInfo->egoConflictLane, foeDistToConflictLane);
    2089              : 
    2090              : #ifdef DEBUG_ENCOUNTER
    2091              :     if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2092              :         std::cout << "  egoConflictLane='" << (egoConflictLane == 0 ? "NULL" : egoConflictLane->getID()) << "'\n"
    2093              :                   << "  foeConflictLane='" << (foeConflictLane == 0 ? "NULL" : foeConflictLane->getID()) << "'\n"
    2094              :                   << "  egoDistToConflictLane=" << egoDistToConflictLane
    2095              :                   << "  foeDistToConflictLane=" << foeDistToConflictLane
    2096              :                   << std::endl;
    2097              : #endif
    2098              : 
    2099              :     // Treat different cases for foeConflictLane and egoConflictLane (internal or non-internal / equal to egoLane or to foeLane),
    2100              :     // and thereby determine encounterType and the ego/foeEncounterDistance.
    2101              :     // The encounter distance has a different meaning for different types of encounters:
    2102              :     // 1) For rear-end conflicts (lead/follow situations) the follower's encounter distance is the distance to the actual back position of the leader. The leaders's distance is undefined.
    2103              :     // 2) For merging encounters the encounter distance is the distance until the begin of the common target edge/lane.
    2104              :     //    (XXX: Perhaps this should be adjusted to include the entry point to the region where a simultaneous occupancy of
    2105              :     //          both merging lanes could imply a collision)
    2106              :     // 3) For crossing encounters the encounter distances is the distance until the entry point to the conflicting lane.
    2107              : 
    2108              :     EncounterType type;
    2109              : 
    2110      6244550 :     if (foeConflictLane == nullptr) {
    2111              :         // foe vehicle is not on course towards the ego's route (see findFoeConflictLane)
    2112              :         type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2113              : #ifdef DEBUG_ENCOUNTER
    2114              :         if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2115              :             std::cout << "-> Encounter type: No conflict.\n";
    2116              :         }
    2117              : #endif
    2118      6041071 :     } else if (!egoConflictLane->isInternal()) {
    2119              :         // The conflict lane is non-internal, therefore we either have no potential conflict or a lead/follow situation (i.e., no crossing or merging)
    2120      5455277 :         if (egoConflictLane == egoLane) {
    2121      5190942 :             const bool egoOpposite = e->ego->getLaneChangeModel().isOpposite();
    2122      5190942 :             const bool foeOpposite = e->foe->getLaneChangeModel().isOpposite();
    2123              : #ifdef DEBUG_ENCOUNTER
    2124              :             if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2125              :                 std::cout << "-> ego " << e->ego->getID() << " isOpposite " << egoOpposite << "  foe " << e->foe->getID() << " isOpposite " << foeOpposite << " .\n";
    2126              :             }
    2127              : #endif
    2128              :             // The conflict point is on the ego's current lane.
    2129      5190942 :             if (foeLane == egoLane) {
    2130              :                 // Foe is on the same non-internal lane
    2131      4026647 :                 if (!egoOpposite && !foeOpposite) {
    2132      4003511 :                     if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
    2133              :                         type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2134      1998778 :                         eInfo.foeConflictEntryDist = e->ego->getBackPositionOnLane() - e->foe->getPositionOnLane();
    2135              :                     } else {
    2136              :                         type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
    2137      2004733 :                         eInfo.egoConflictEntryDist = e->foe->getBackPositionOnLane() - e->ego->getPositionOnLane();
    2138              :                     }
    2139              : #ifdef DEBUG_ENCOUNTER
    2140              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2141              :                         std::cout << "-> Encounter type: Lead/follow-situation on non-internal lane '" << egoLane->getID() << "'\n";
    2142              :                     }
    2143              : #endif
    2144        23136 :                 } else if (egoOpposite && foeOpposite) {
    2145        13596 :                     if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
    2146              :                         type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2147         6798 :                         eInfo.foeConflictEntryDist = -(e->ego->getBackPositionOnLane() - e->foe->getPositionOnLane());
    2148              :                     } else {
    2149              :                         type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
    2150         6798 :                         eInfo.egoConflictEntryDist = -(e->foe->getBackPositionOnLane() - e->ego->getPositionOnLane());
    2151              :                     }
    2152              : #ifdef DEBUG_ENCOUNTER
    2153              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2154              :                         std::cout << "-> Encounter type: Lead/follow-situation  while both are driving in the opposite direction on non-internal lane '" << egoLane->getID() << "'\n";
    2155              :                     }
    2156              : #endif
    2157              :                 } else {
    2158              :                     type = ENCOUNTER_TYPE_ONCOMING;
    2159         9540 :                     const double gap = e->ego->getPositionOnLane() - e->foe->getPositionOnLane();
    2160         9540 :                     if (egoOpposite) {
    2161         4770 :                         if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
    2162          908 :                             eInfo.egoConflictEntryDist = gap;
    2163          908 :                             eInfo.foeConflictEntryDist = gap;
    2164              :                         } else {
    2165              :                             type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2166              :                         }
    2167              :                     } else {
    2168         4770 :                         if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
    2169          908 :                             eInfo.egoConflictEntryDist = -gap;
    2170          908 :                             eInfo.foeConflictEntryDist = -gap;
    2171              :                         } else {
    2172              :                             type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2173              :                         }
    2174              :                     }
    2175              : #ifdef DEBUG_ENCOUNTER
    2176              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2177              :                         std::cout << "-> Encounter type: oncoming on non-internal lane '" << egoLane->getID() << "'\n";
    2178              :                     }
    2179              : #endif
    2180              : 
    2181              :                 }
    2182      1164295 :             } else if (&(foeLane->getEdge()) == &(egoLane->getEdge())) {
    2183              :                 // Foe is on the same non-internal edge but not on the same lane. Treat this as no conflict for now
    2184              :                 // XXX: this disregards conflicts for vehicles on adjacent lanes
    2185       900700 :                 if (foeOpposite != egoOpposite) {
    2186              :                     type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2187              :                 } else {
    2188              :                     type = ENCOUNTER_TYPE_ON_ADJACENT_LANES;
    2189              :                 }
    2190              : #ifdef DEBUG_ENCOUNTER
    2191              :                 if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2192              :                     std::cout << "-> Encounter type: " << type << std::endl;
    2193              :                 }
    2194              : #endif
    2195              :             } else {
    2196              : 
    2197       263595 :                 if (!egoOpposite && !foeOpposite) {
    2198              : 
    2199              :                     assert(&(egoLane->getEdge()) == &(foeConflictLane->getEdge()));
    2200              :                     assert(egoDistToConflictLane <= 0);
    2201              :                     // Foe must be on a route leading into the ego's edge
    2202       263588 :                     if (foeConflictLane == egoLane) {
    2203              :                         type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2204       238506 :                         eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
    2205              : 
    2206              : #ifdef DEBUG_ENCOUNTER
    2207              :                         if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2208              :                             std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
    2209              :                                       << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
    2210              :                                       << " (gap = " << eInfo.foeConflictEntryDist << ")\n";
    2211              : #endif
    2212              :                     } else {
    2213              :                         // Foe's route leads to an adjacent lane of the current lane of the ego
    2214              :                         type = ENCOUNTER_TYPE_ON_ADJACENT_LANES;
    2215              : #ifdef DEBUG_ENCOUNTER
    2216              :                         if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2217              :                             std::cout << "-> Encounter type: " << type << std::endl;
    2218              :                         }
    2219              : #endif
    2220              :                     }
    2221              : 
    2222            7 :                 } else if (egoOpposite && foeOpposite) {
    2223              :                     // XXX determine follower relationship by searching for the foe lane in the opposites of ego bestlanes
    2224              :                     type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2225              :                     /*
    2226              :                     if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
    2227              :                     type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2228              :                     eInfo.foeConflictEntryDist = -(e->ego->getBackPositionOnLane() - e->foe->getPositionOnLane());
    2229              :                     } else {
    2230              :                     type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
    2231              :                     eInfo.egoConflictEntryDist = -(e->foe->getBackPositionOnLane() - e->ego->getPositionOnLane());
    2232              :                     }
    2233              :                     */
    2234              : #ifdef DEBUG_ENCOUNTER
    2235              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2236              :                         std::cout << "-> Encounter type: Lead/follow-situation  while both are driving in the opposite direction on non-internal lane '" << egoLane->getID() << "'\n";
    2237              :                     }
    2238              : #endif
    2239              :                 } else {
    2240              :                     type = ENCOUNTER_TYPE_ONCOMING;
    2241              :                     // XXX determine distance by searching for the foe lane in the opposites of ego bestlanes
    2242              :                     /*
    2243              :                     const double gap = e->ego->getPositionOnLane() - e->foe->getPositionOnLane();
    2244              :                     if (egoOpposite) {
    2245              :                     if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
    2246              :                     eInfo.egoConflictEntryDist = gap;
    2247              :                     eInfo.foeConflictEntryDist = gap;
    2248              :                     } else {
    2249              :                     type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2250              :                     }
    2251              :                     } else {
    2252              :                     if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
    2253              :                     eInfo.egoConflictEntryDist = -gap;
    2254              :                     eInfo.foeConflictEntryDist = -gap;
    2255              :                     } else {
    2256              :                     type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2257              :                     }
    2258              :                     }
    2259              :                     */
    2260              : #ifdef DEBUG_ENCOUNTER
    2261              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2262              :                         std::cout << "-> Encounter type: oncoming on non-internal lane '" << egoLane->getID() << "'\n";
    2263              :                     }
    2264              : #endif
    2265              : 
    2266              :                 }
    2267              :             }
    2268              :         } else {
    2269              :             // The egoConflictLane is a non-internal lane which is not the ego's current lane. Thus it must lie ahead of the ego vehicle and
    2270              :             // is located on the foe's current edge see findSurroundingVehicles()
    2271              :             // (otherwise the foe would have had to enter the ego's route along a junction and the corresponding
    2272              :             // conflict lane would be internal)
    2273              :             assert(&(foeLane->getEdge()) == &(egoConflictLane->getEdge()));
    2274              :             assert(foeDistToConflictLane <= 0);
    2275       264335 :             if (foeLane == egoConflictLane) {
    2276              :                 type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
    2277       239261 :                 eInfo.egoConflictEntryDist = egoDistToConflictLane + e->foe->getBackPositionOnLane();
    2278              : #ifdef DEBUG_ENCOUNTER
    2279              :                 if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2280              :                     std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
    2281              :                               << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
    2282              :                               << " (gap = " << eInfo.egoConflictEntryDist << ", case1)\n";
    2283              : #endif
    2284              :             } else {
    2285              :                 // Ego's route leads to an adjacent lane of the current lane of the foe
    2286              :                 type = ENCOUNTER_TYPE_ON_ADJACENT_LANES;
    2287              : #ifdef DEBUG_ENCOUNTER
    2288              :                 if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2289              :                     std::cout << "-> Encounter type: " << type << std::endl;
    2290              :                 }
    2291              : #endif
    2292              :             }
    2293              :         }
    2294              :     } else {
    2295              :         // egoConflictLane is internal, i.e., lies on a junction. Besides the lead/follow situation (which may stretch over different lanes of a connection),
    2296              :         // merging or crossing of the conflict lanes is possible.
    2297              :         assert(foeConflictLane->isInternal());
    2298       585794 :         const MSLink* egoEntryLink = egoConflictLane->getEntryLink();
    2299       585794 :         const MSLink* foeEntryLink = foeConflictLane->getEntryLink();
    2300       585794 :         const bool egoOpposite = e->ego->getLaneChangeModel().isOpposite();
    2301       585794 :         const bool foeOpposite = e->foe->getLaneChangeModel().isOpposite();
    2302              : 
    2303       585794 :         if (((!egoOpposite && foeOpposite) || (egoOpposite && !foeOpposite)) && egoConflictLane == foeConflictLane) {
    2304              :             // oncoming on junction
    2305              : #ifdef DEBUG_ENCOUNTER
    2306              :             if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2307              : 
    2308              :                 std::cout << "-> egoConflictLane: " << egoConflictLane->getID() << " foeConflictLane: " << foeConflictLane->getID() << " egoOpposite " << egoOpposite << " foeOpposite " << foeOpposite << std::endl;
    2309              :             }
    2310              : #endif
    2311              :             // The conflict point is on the ego's current lane.
    2312           25 :             if (foeLane == egoLane) {
    2313              :                 // Foe is on the same non-internal lane
    2314            0 :                 if (!egoOpposite && !foeOpposite) {
    2315            0 :                     if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
    2316              :                         type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2317            0 :                         eInfo.foeConflictEntryDist = e->ego->getBackPositionOnLane() - e->foe->getPositionOnLane();
    2318              :                     } else {
    2319              :                         type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
    2320            0 :                         eInfo.egoConflictEntryDist = e->foe->getBackPositionOnLane() - e->ego->getPositionOnLane();
    2321              :                     }
    2322              : #ifdef DEBUG_ENCOUNTER
    2323              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2324              :                         std::cout << "-> Encounter type: Lead/follow-situation on non-internal lane '" << egoLane->getID() << "'\n";
    2325              :                     }
    2326              : #endif
    2327            0 :                 } else if (egoOpposite && foeOpposite) {
    2328            0 :                     if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
    2329              :                         type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2330            0 :                         eInfo.foeConflictEntryDist = -(e->ego->getBackPositionOnLane() - e->foe->getPositionOnLane());
    2331              :                     } else {
    2332              :                         type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
    2333            0 :                         eInfo.egoConflictEntryDist = -(e->foe->getBackPositionOnLane() - e->ego->getPositionOnLane());
    2334              :                     }
    2335              : #ifdef DEBUG_ENCOUNTER
    2336              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2337              :                         std::cout << "-> Encounter type: Lead/follow-situation  while both are driving in the opposite direction on non-internal lane '" << egoLane->getID() << "'\n";
    2338              :                     }
    2339              : #endif
    2340              :                 } else {
    2341              :                     type = ENCOUNTER_TYPE_ONCOMING;
    2342            0 :                     const double gap = e->ego->getPositionOnLane() - e->foe->getPositionOnLane();
    2343            0 :                     if (egoOpposite) {
    2344            0 :                         if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
    2345            0 :                             eInfo.egoConflictEntryDist = gap;
    2346            0 :                             eInfo.foeConflictEntryDist = gap;
    2347              :                         } else {
    2348              :                             type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2349              :                         }
    2350              :                     } else {
    2351            0 :                         if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
    2352            0 :                             eInfo.egoConflictEntryDist = -gap;
    2353            0 :                             eInfo.foeConflictEntryDist = -gap;
    2354              :                         } else {
    2355              :                             type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2356              :                         }
    2357              :                     }
    2358              : #ifdef DEBUG_ENCOUNTER
    2359              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2360              :                         std::cout << "-> Encounter type: oncoming on non-internal lane '" << egoLane->getID() << "'\n";
    2361              :                     }
    2362              : #endif
    2363              : 
    2364              :                 }
    2365           25 :             } else if (&(foeLane->getEdge()) == &(egoLane->getEdge())) {
    2366              :                 // Foe is on the same non-internal edge but not on the same lane. Treat this as no conflict for now
    2367              :                 // XXX: this disregards conflicts for vehicles on adjacent lanes
    2368              :                 if (foeOpposite != egoOpposite) {
    2369              :                     type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2370              :                 } else {
    2371              :                     type = ENCOUNTER_TYPE_ON_ADJACENT_LANES;
    2372              :                 }
    2373              : #ifdef DEBUG_ENCOUNTER
    2374              :                 if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2375              :                     std::cout << "-> Encounter type: " << type << std::endl;
    2376              :                 }
    2377              : #endif
    2378              :             } else {
    2379           25 :                 if (!egoOpposite && !foeOpposite) {
    2380              :                     assert(&(egoLane->getEdge()) == &(foeConflictLane->getEdge()));
    2381              :                     assert(egoDistToConflictLane <= 0);
    2382              :                     // Foe must be on a route leading into the ego's edge
    2383            0 :                     if (foeConflictLane == egoLane) {
    2384              :                         type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2385            0 :                         eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
    2386              : 
    2387              : #ifdef DEBUG_ENCOUNTER
    2388              :                         if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2389              :                             std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
    2390              :                                       << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
    2391              :                                       << " (gap = " << eInfo.foeConflictEntryDist << ")\n";
    2392              : #endif
    2393              :                     } else {
    2394              :                         // Foe's route leads to an adjacent lane of the current lane of the ego
    2395              :                         type = ENCOUNTER_TYPE_ON_ADJACENT_LANES;
    2396              : #ifdef DEBUG_ENCOUNTER
    2397              :                         if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2398              :                             std::cout << "-> Encounter type: " << type << std::endl;
    2399              :                         }
    2400              : #endif
    2401              :                     }
    2402           25 :                 } else if (egoOpposite && foeOpposite) {
    2403              :                     // XXX determine follower relationship by searching for the foe lane in the opposites of ego bestlanes
    2404              :                     type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2405              :                     /*
    2406              :                     if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
    2407              :                     type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2408              :                     eInfo.foeConflictEntryDist = -(e->ego->getBackPositionOnLane() - e->foe->getPositionOnLane());
    2409              :                     } else {
    2410              :                     type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
    2411              :                     eInfo.egoConflictEntryDist = -(e->foe->getBackPositionOnLane() - e->ego->getPositionOnLane());
    2412              :                     }
    2413              :                     */
    2414              : #ifdef DEBUG_ENCOUNTER
    2415              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2416              :                         std::cout << "-> Encounter type: Lead/follow-situation  while both are driving in the opposite direction on non-internal lane '" << egoLane->getID() << "'\n";
    2417              :                     }
    2418              : #endif
    2419              :                 } else {
    2420              :                     type = ENCOUNTER_TYPE_ONCOMING;
    2421              :                     // XXX determine distance by searching for the foe lane in the opposites of ego bestlanes
    2422              :                     /*
    2423              :                     const double gap = e->ego->getPositionOnLane() - e->foe->getPositionOnLane();
    2424              :                     if (egoOpposite) {
    2425              :                     if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
    2426              :                     eInfo.egoConflictEntryDist = gap;
    2427              :                     eInfo.foeConflictEntryDist = gap;
    2428              :                     } else {
    2429              :                     type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2430              :                     }
    2431              :                     } else {
    2432              :                     if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
    2433              :                     eInfo.egoConflictEntryDist = -gap;
    2434              :                     eInfo.foeConflictEntryDist = -gap;
    2435              :                     } else {
    2436              :                     type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2437              :                     }
    2438              :                     }
    2439              :                     */
    2440              : #ifdef DEBUG_ENCOUNTER
    2441              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2442              :                         std::cout << "-> Encounter type: oncoming on non-internal lane '" << egoLane->getID() << "'\n";
    2443              :                     }
    2444              : #endif
    2445              :                 }
    2446              :             }
    2447       585769 :         } else if (&(egoEntryLink->getViaLane()->getEdge()) == &(foeEntryLink->getViaLane()->getEdge())) {
    2448       169867 :             if (egoEntryLink != foeEntryLink) {
    2449              :                 // XXX: this disregards conflicts for vehicles on adjacent internal lanes
    2450              :                 type = ENCOUNTER_TYPE_ON_ADJACENT_LANES;
    2451              : #ifdef DEBUG_ENCOUNTER
    2452              :                 if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2453              :                     std::cout << "-> Encounter type: " << type << std::endl;
    2454              :                 }
    2455              : #endif
    2456              :             } else {
    2457              :                 // Lead / follow situation on connection
    2458       130169 :                 if (egoLane == egoConflictLane && foeLane != foeConflictLane) {
    2459              :                     // ego on junction, foe not yet
    2460              :                     type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2461        65309 :                     eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
    2462        65309 :                     if (e->ego->getLane()->getIncomingLanes()[0].lane->isInternal()) {
    2463           24 :                         eInfo.foeConflictEntryDist += e->ego->getLane()->getIncomingLanes()[0].lane->getLength();
    2464              :                     }
    2465              : #ifdef DEBUG_ENCOUNTER
    2466              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2467              :                         std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
    2468              :                                   << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
    2469              :                                   << " (gap = " << eInfo.foeConflictEntryDist << ")\n";
    2470              : #endif
    2471        64860 :                 } else if (egoLane != egoConflictLane && foeLane == foeConflictLane) {
    2472              :                     // foe on junction, ego not yet
    2473              :                     type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
    2474        63902 :                     eInfo.egoConflictEntryDist = egoDistToConflictLane + e->foe->getBackPositionOnLane();
    2475        63902 :                     if (e->foe->getLane()->getIncomingLanes()[0].lane->isInternal()) {
    2476            0 :                         eInfo.egoConflictEntryDist += e->foe->getLane()->getIncomingLanes()[0].lane->getLength();
    2477              :                     }
    2478              : #ifdef DEBUG_ENCOUNTER
    2479              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2480              :                         std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
    2481              :                                   << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
    2482              :                                   << " (gap = " << eInfo.egoConflictEntryDist << ", case2)\n";
    2483              : #endif
    2484          958 :                 } else if (e->ego->getLaneChangeModel().isOpposite() || e->foe->getLaneChangeModel().isOpposite()) {
    2485              :                     type = ENCOUNTER_TYPE_MERGING;
    2486            0 :                     eInfo.foeConflictEntryDist = foeDistToConflictLane;
    2487            0 :                     eInfo.egoConflictEntryDist = egoDistToConflictLane;
    2488              : #ifdef DEBUG_ENCOUNTER
    2489              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2490              :                         std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' merges with foe '"
    2491              :                                   << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
    2492              :                                   << " (gap = " << eInfo.egoConflictEntryDist << ", case5)\n";
    2493              : #endif
    2494              : 
    2495              :                 } else {
    2496              :                     // Both must be already on the junction in a lead / follow situation on a connection
    2497              :                     // (since they approach via the same link, findSurroundingVehicles() would have determined a
    2498              :                     // different conflictLane if both are not on the junction)
    2499          958 :                     if (egoLane != egoConflictLane || foeLane != foeConflictLane) {
    2500            0 :                         WRITE_WARNINGF(TL("Cannot classify SSM encounter between ego vehicle % and foe vehicle % at time=%\n"), e->ego->getID(), e->foe->getID(), SIMTIME);
    2501            0 :                         return ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2502              :                     }
    2503          958 :                     if (egoLane == foeLane) {
    2504              :                         // both on the same internal lane
    2505          958 :                         if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
    2506              :                             type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2507          479 :                             eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
    2508              : #ifdef DEBUG_ENCOUNTER
    2509              :                             if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2510              :                                 std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
    2511              :                                           << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
    2512              :                                           << " (gap = " << eInfo.foeConflictEntryDist << ")"
    2513              :                                           << std::endl;
    2514              : #endif
    2515              :                         } else {
    2516              :                             type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
    2517          479 :                             eInfo.egoConflictEntryDist = egoDistToConflictLane + e->foe->getBackPositionOnLane();
    2518              : #ifdef DEBUG_ENCOUNTER
    2519              :                             if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2520              :                                 std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
    2521              :                                           << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
    2522              :                                           << " (gap = " << eInfo.egoConflictEntryDist << ", case3)"
    2523              :                                           << std::endl;
    2524              : #endif
    2525              :                         }
    2526              :                     } else {
    2527              :                         // ego and foe on distinct, consecutive internal lanes
    2528              : #ifdef DEBUG_ENCOUNTER
    2529              :                         if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2530              :                             std::cout << "    Lead/follow situation on consecutive internal lanes." << std::endl;
    2531              :                         }
    2532              : #endif
    2533              :                         MSLane* lane = egoEntryLink->getViaLane();
    2534              :                         while (true) {
    2535              :                             // Find first of egoLane and foeLane while crossing the junction (this dertermines who's the follower)
    2536              :                             // Then set the conflict lane to the lane of the leader and adapt the follower's distance to conflict
    2537            0 :                             if (egoLane == lane) {
    2538              :                                 // ego is follower
    2539              :                                 type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
    2540              :                                 // adapt conflict dist
    2541            0 :                                 eInfo.egoConflictEntryDist = egoDistToConflictLane;
    2542            0 :                                 while (lane != foeLane) {
    2543            0 :                                     eInfo.egoConflictEntryDist += lane->getLength();
    2544            0 :                                     lane = lane->getLinkCont()[0]->getViaLane();
    2545              :                                     assert(lane != 0);
    2546              :                                 }
    2547            0 :                                 eInfo.egoConflictEntryDist += e->foe->getBackPositionOnLane();
    2548              :                                 egoConflictLane = lane;
    2549              : #ifdef DEBUG_ENCOUNTER
    2550              :                                 if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2551              :                                     std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
    2552              :                                               << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
    2553              :                                               << " (gap = " << eInfo.egoConflictEntryDist << ", case4)"
    2554              :                                               << std::endl;
    2555              : #endif
    2556            0 :                                 break;
    2557            0 :                             } else if (foeLane == lane) {
    2558              :                                 // ego is leader
    2559              :                                 type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
    2560              :                                 // adapt conflict dist
    2561            0 :                                 eInfo.foeConflictEntryDist = foeDistToConflictLane;
    2562            0 :                                 while (lane != egoLane) {
    2563            0 :                                     eInfo.foeConflictEntryDist += lane->getLength();
    2564            0 :                                     lane = lane->getLinkCont()[0]->getViaLane();
    2565              :                                     assert(lane != 0);
    2566              :                                 }
    2567            0 :                                 eInfo.foeConflictEntryDist += e->ego->getBackPositionOnLane();
    2568              :                                 foeConflictLane = lane;
    2569              : #ifdef DEBUG_ENCOUNTER
    2570              :                                 if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2571              :                                     std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
    2572              :                                               << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
    2573              :                                               << " (gap = " << eInfo.foeConflictEntryDist << ")"
    2574              :                                               << std::endl;
    2575              : #endif
    2576            0 :                                 break;
    2577              :                             }
    2578            0 :                             lane = lane->getLinkCont()[0]->getViaLane();
    2579              :                             assert(lane != 0);
    2580              :                         }
    2581              :                     }
    2582              : #ifdef DEBUG_ENCOUNTER
    2583              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2584              :                         std::cout << "-> Encounter type: Lead/follow-situation on connection from '" << egoEntryLink->getLaneBefore()->getID()
    2585              :                                   << "' to '" << egoEntryLink->getLane()->getID() << "'" << std::endl;
    2586              : #endif
    2587              :                 }
    2588              :             }
    2589              :         } else {
    2590              :             // Entry links to junctions lead to different internal edges.
    2591              :             // There are three possibilities, either the edges cross, merge or have no conflict
    2592              :             const std::vector<MSLink*>& egoFoeLinks = egoEntryLink->getFoeLinks();
    2593              :             const std::vector<MSLink*>& foeFoeLinks = foeEntryLink->getFoeLinks();
    2594              :             // Determine whether ego and foe links are foes
    2595       415902 :             bool crossOrMerge = (find(egoFoeLinks.begin(), egoFoeLinks.end(), foeEntryLink) != egoFoeLinks.end()
    2596       415902 :                                  || std::find(foeFoeLinks.begin(), foeFoeLinks.end(), egoEntryLink) != foeFoeLinks.end());
    2597              :             if (!crossOrMerge) {
    2598              :                 //                if (&(foeEntryLink->getLane()->getEdge()) == &(egoEntryLink->getLane()->getEdge())) {
    2599              :                 //                    // XXX: the situation of merging into adjacent lanes is disregarded for now <- the alleged situation appears to imply crossOrMerge!!!
    2600              :                 //                    type = ENCOUNTER_TYPE_MERGING_ADJACENT;
    2601              :                 //#ifdef DEBUG_SSM
    2602              :                 //                    std::cout << "-> Encounter type: No conflict (adjacent lanes)." << std::endl;
    2603              :                 //#endif
    2604              :                 //                } else {
    2605              :                 type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
    2606              : #ifdef DEBUG_ENCOUNTER
    2607              :                 if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2608              :                     std::cout << "-> Encounter type: No conflict.\n";
    2609              :                 }
    2610              : #endif
    2611              :                 //                }
    2612         8519 :             } else if (&(foeEntryLink->getLane()->getEdge()) == &(egoEntryLink->getLane()->getEdge())) {
    2613         2617 :                 if (foeEntryLink->getLane() == egoEntryLink->getLane()) {
    2614              :                     type = ENCOUNTER_TYPE_MERGING;
    2615              :                     assert(egoConflictLane->isInternal());
    2616              :                     assert(foeConflictLane->isInternal());
    2617         2133 :                     eInfo.egoConflictEntryDist = egoDistToConflictLane + egoEntryLink->getInternalLengthsAfter();
    2618         2133 :                     eInfo.foeConflictEntryDist = foeDistToConflictLane + foeEntryLink->getInternalLengthsAfter();
    2619              : 
    2620         2133 :                     MSLink* egoEntryLinkSucc = egoEntryLink->getViaLane()->getLinkCont().front();
    2621         2133 :                     if (egoEntryLinkSucc->isInternalJunctionLink() && e->ego->getLane() == egoEntryLinkSucc->getViaLane()) {
    2622              :                         // ego is already past the internal junction
    2623           48 :                         eInfo.egoConflictEntryDist -= egoEntryLink->getViaLane()->getLength();
    2624           48 :                         eInfo.egoConflictExitDist -= egoEntryLink->getViaLane()->getLength();
    2625              :                     }
    2626         2133 :                     MSLink* foeEntryLinkSucc = foeEntryLink->getViaLane()->getLinkCont().front();
    2627         2133 :                     if (foeEntryLinkSucc->isInternalJunctionLink() && e->foe->getLane() == foeEntryLinkSucc->getViaLane()) {
    2628              :                         // foe is already past the internal junction
    2629           48 :                         eInfo.foeConflictEntryDist -= foeEntryLink->getViaLane()->getLength();
    2630           48 :                         eInfo.foeConflictExitDist -= foeEntryLink->getViaLane()->getLength();
    2631              :                     }
    2632              : 
    2633              : #ifdef DEBUG_ENCOUNTER
    2634              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
    2635              :                         std::cout << "-> Encounter type: Merging situation of ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' and foe '"
    2636              :                                   << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
    2637              :                                   << "\nDistances to merge-point: ego: " << eInfo.egoConflictEntryDist << ", foe: " << eInfo.foeConflictEntryDist
    2638              :                                   << std::endl;
    2639              : #endif
    2640              :                 } else {
    2641              :                     // Links leading to the same edge but different lanes. XXX: Disregards conflicts on adjacent lanes
    2642              :                     type = ENCOUNTER_TYPE_MERGING_ADJACENT;
    2643              : #ifdef DEBUG_ENCOUNTER
    2644              :                     if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2645              :                         std::cout << "-> Encounter type: No conflict: " << type << std::endl;
    2646              :                     }
    2647              : #endif
    2648              :                 }
    2649              :             } else {
    2650              :                 type = ENCOUNTER_TYPE_CROSSING;
    2651              : 
    2652              :                 assert(egoConflictLane->isInternal());
    2653              :                 assert(foeConflictLane->getEdge().getToJunction() == egoConflictLane->getEdge().getToJunction());
    2654              : 
    2655              :                 // If the conflict lanes are internal, they may not correspond to the
    2656              :                 // actually crossing parts of the corresponding connections.
    2657              :                 // Adjust the conflict lanes accordingly.
    2658              :                 // set back both to the first parts of the corresponding connections
    2659         5902 :                 double offset = 0.;
    2660         5902 :                 egoConflictLane = egoConflictLane->getFirstInternalInConnection(offset);
    2661         5902 :                 egoDistToConflictLane -= offset;
    2662         5902 :                 foeConflictLane = foeConflictLane->getFirstInternalInConnection(offset);
    2663         5902 :                 foeDistToConflictLane -= offset;
    2664              :                 // find the distances to the conflict from the junction entry for both vehicles
    2665              :                 // Here we also determine the real crossing lanes (before the conflict lane is the first lane of the connection)
    2666              :                 // for the ego
    2667              :                 double egoDistToConflictFromJunctionEntry = INVALID_DOUBLE;
    2668         6336 :                 while (foeConflictLane != nullptr && foeConflictLane->isInternal()) {
    2669         6336 :                     egoDistToConflictFromJunctionEntry = egoEntryLink->getLengthsBeforeCrossing(foeConflictLane);
    2670         6336 :                     if (egoDistToConflictFromJunctionEntry != INVALID_DOUBLE) {
    2671              :                         // found correct foeConflictLane
    2672         5902 :                         egoDistToConflictFromJunctionEntry += 0.5 * (foeConflictLane->getWidth() - e->foe->getVehicleType().getWidth());
    2673         5902 :                         break;
    2674              :                     }
    2675          434 :                     if (!foeConflictLane->getCanonicalSuccessorLane()->isInternal()) {
    2676              :                         // intersection has wierd geometry and the intersection was found
    2677              :                         egoDistToConflictFromJunctionEntry = 0;
    2678            0 :                         WRITE_WARNINGF(TL("Cannot compute SSM due to bad internal lane geometry at junction '%'. Crossing point between traffic from links % and % not found."),
    2679              :                                        egoEntryLink->getJunction()->getID(),
    2680              :                                        egoEntryLink->getIndex(),
    2681              :                                        foeEntryLink->getIndex());
    2682            0 :                         break;
    2683              :                     }
    2684          434 :                     foeConflictLane = foeConflictLane->getCanonicalSuccessorLane();
    2685              :                     assert(foeConflictLane != nullptr && foeConflictLane->isInternal()); // this loop should be ended by the break! Otherwise the lanes do not cross, which should be the case here.
    2686              :                 }
    2687              :                 assert(egoDistToConflictFromJunctionEntry != INVALID_DOUBLE);
    2688              : 
    2689              :                 // for the foe
    2690              :                 double foeDistToConflictFromJunctionEntry = INVALID_DOUBLE;
    2691              :                 foeDistToConflictFromJunctionEntry = INVALID_DOUBLE;
    2692         6702 :                 while (egoConflictLane != nullptr && egoConflictLane->isInternal()) {
    2693         6702 :                     foeDistToConflictFromJunctionEntry = foeEntryLink->getLengthsBeforeCrossing(egoConflictLane);
    2694         6702 :                     if (foeDistToConflictFromJunctionEntry != INVALID_DOUBLE) {
    2695              :                         // found correct egoConflictLane
    2696         5902 :                         foeDistToConflictFromJunctionEntry += 0.5 * (egoConflictLane->getWidth() - e->ego->getVehicleType().getWidth());
    2697         5902 :                         break;
    2698              :                     }
    2699          800 :                     if (!egoConflictLane->getCanonicalSuccessorLane()->isInternal()) {
    2700              :                         // intersection has wierd geometry and the intersection was found
    2701              :                         foeDistToConflictFromJunctionEntry = 0;
    2702            0 :                         WRITE_WARNINGF(TL("Cannot compute SSM due to bad internal lane geometry at junction '%'. Crossing point between traffic from links % and % not found."),
    2703              :                                        foeEntryLink->getJunction()->getID(),
    2704              :                                        foeEntryLink->getIndex(),
    2705              :                                        egoEntryLink->getIndex());
    2706            0 :                         break;
    2707              :                     }
    2708          800 :                     egoConflictLane = egoConflictLane->getCanonicalSuccessorLane();
    2709              :                     assert(egoConflictLane != nullptr && egoConflictLane->isInternal()); // this loop should be ended by the break! Otherwise the lanes do not cross, which should be the case here.
    2710              :                 }
    2711              :                 assert(foeDistToConflictFromJunctionEntry != INVALID_DOUBLE);
    2712              : 
    2713              :                 // store conflict entry information in eInfo
    2714              : 
    2715              :                 //                // TO-DO: equip these with exit times to store relevant PET sections in encounter
    2716              :                 //                eInfo.egoConflictEntryCrossSection = std::make_pair(egoConflictLane, egoDistToConflictFromJunctionEntry - egoInternalLaneLengthsBeforeCrossing);
    2717              :                 //                eInfo.foeConflictEntryCrossSection = std::make_pair(foeConflictLane, foeDistToConflictFromJunctionEntry - foeInternalLaneLengthsBeforeCrossing);
    2718              : 
    2719              :                 // Take into account the lateral position for the exact determination of the conflict point
    2720              :                 // whether lateral position increases or decreases conflict distance depends on lane angles at conflict
    2721              :                 // -> conflictLaneOrientation in {-1,+1}
    2722              :                 // First, measure the angle between the two connection lines (straight lines from junction entry point to junction exit point)
    2723         5902 :                 Position egoEntryPos = egoEntryLink->getViaLane()->getShape().front();
    2724         5902 :                 Position egoExitPos = egoEntryLink->getCorrespondingExitLink()->getInternalLaneBefore()->getShape().back();
    2725         5902 :                 PositionVector egoConnectionLine(egoEntryPos, egoExitPos);
    2726         5902 :                 Position foeEntryPos = foeEntryLink->getViaLane()->getShape().front();
    2727         5902 :                 Position foeExitPos = foeEntryLink->getCorrespondingExitLink()->getInternalLaneBefore()->getShape().back();
    2728         5902 :                 PositionVector foeConnectionLine(foeEntryPos, foeExitPos);
    2729         5902 :                 double angle = std::fmod(egoConnectionLine.rotationAtOffset(0.) - foeConnectionLine.rotationAtOffset(0.), (2 * M_PI));
    2730         5902 :                 if (angle < 0) {
    2731         4000 :                     angle += 2 * M_PI;
    2732              :                 }
    2733              :                 assert(angle >= 0);
    2734              :                 assert(angle <= 2 * M_PI);
    2735         5902 :                 if (angle > M_PI) {
    2736         4000 :                     angle -= 2 * M_PI;
    2737              :                 }
    2738              :                 assert(angle >= -M_PI);
    2739              :                 assert(angle <= M_PI);
    2740              :                 // Determine orientation of the connection lines. (Positive values mean that the ego vehicle approaches from the foe's left side.)
    2741         5902 :                 double crossingOrientation = (angle < 0) - (angle > 0);
    2742              : 
    2743              :                 // Adjust conflict dist to lateral positions
    2744              :                 // TODO: This could more precisely be calculated wrt the angle of the crossing *at the conflict point*
    2745         5902 :                 egoDistToConflictFromJunctionEntry -= crossingOrientation * e->foe->getLateralPositionOnLane();
    2746         5902 :                 foeDistToConflictFromJunctionEntry += crossingOrientation * e->ego->getLateralPositionOnLane();
    2747              : 
    2748              :                 // Complete entry distances
    2749         5902 :                 eInfo.egoConflictEntryDist = egoDistToConflictLane + egoDistToConflictFromJunctionEntry;
    2750         5902 :                 eInfo.foeConflictEntryDist = foeDistToConflictLane + foeDistToConflictFromJunctionEntry;
    2751              : 
    2752              : 
    2753              :                 // TODO: This could also more precisely be calculated wrt the angle of the crossing *at the conflict point*
    2754         5902 :                 eInfo.egoConflictAreaLength = e->foe->getWidth();
    2755         5902 :                 eInfo.foeConflictAreaLength = e->ego->getWidth();
    2756              : 
    2757              :                 // resulting exit distances
    2758         5902 :                 eInfo.egoConflictExitDist = eInfo.egoConflictEntryDist + eInfo.egoConflictAreaLength + e->ego->getLength();
    2759         5902 :                 eInfo.foeConflictExitDist = eInfo.foeConflictEntryDist + eInfo.foeConflictAreaLength + e->foe->getLength();
    2760              : 
    2761              : #ifdef DEBUG_ENCOUNTER
    2762              :                 if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
    2763              :                     std::cout << "    Determined exact conflict distances for crossing conflict."
    2764              :                               << "\n    crossingOrientation=" << crossingOrientation
    2765              :                               << ", egoCrossingAngle=" << egoConnectionLine.rotationAtOffset(0.)
    2766              :                               << ", foeCrossingAngle=" << foeConnectionLine.rotationAtOffset(0.)
    2767              :                               << ", relativeAngle=" << angle
    2768              :                               << " (foe from " << (crossingOrientation > 0 ? "right)" : "left)")
    2769              :                               << "\n    resulting offset for conflict entry distance:"
    2770              :                               << "\n     ego=" << crossingOrientation* e->foe->getLateralPositionOnLane()
    2771              :                               << ", foe=" << crossingOrientation* e->ego->getLateralPositionOnLane()
    2772              :                               << "\n    distToConflictLane:"
    2773              :                               << "\n     ego=" << egoDistToConflictLane
    2774              :                               << ", foe=" << foeDistToConflictLane
    2775              :                               << "\n    distToConflictFromJunctionEntry:"
    2776              :                               << "\n     ego=" << egoDistToConflictFromJunctionEntry
    2777              :                               << ", foe=" << foeDistToConflictFromJunctionEntry
    2778              :                               << "\n    resulting entry distances:"
    2779              :                               << "\n     ego=" << eInfo.egoConflictEntryDist
    2780              :                               << ", foe=" << eInfo.foeConflictEntryDist
    2781              :                               << "\n    resulting exit distances:"
    2782              :                               << "\n     ego=" << eInfo.egoConflictExitDist
    2783              :                               << ", foe=" << eInfo.foeConflictExitDist
    2784              :                               << std::endl;
    2785              : 
    2786              :                     std::cout << "real egoConflictLane: '" << (egoConflictLane == 0 ? "NULL" : egoConflictLane->getID()) << "'\n"
    2787              :                               << "real foeConflictLane: '" << (foeConflictLane == 0 ? "NULL" : foeConflictLane->getID()) << "'\n"
    2788              :                               << "-> Encounter type: Crossing situation of ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' and foe '"
    2789              :                               << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
    2790              :                               << "\nDistances to crossing-point: ego: " << eInfo.egoConflictEntryDist << ", foe: " << eInfo.foeConflictEntryDist
    2791              :                               << std::endl;
    2792              :                 }
    2793              : #endif
    2794         5902 :             }
    2795              :         }
    2796              :     }
    2797              :     return type;
    2798              : }
    2799              : 
    2800              : 
    2801              : 
    2802              : const MSLane*
    2803      6244550 : MSDevice_SSM::findFoeConflictLane(const MSVehicle* foe, const MSLane* egoConflictLane, double& distToConflictLane) const {
    2804              : 
    2805              : #ifdef DEBUG_SSM
    2806              :     if (DEBUG_COND(myHolderMS))
    2807              :         std::cout << SIMTIME << " findFoeConflictLane() for foe '"
    2808              :                   << foe->getID() << "' on lane '" << foe->getLane()->getID()
    2809              :                   << "' (with egoConflictLane=" << (egoConflictLane == 0 ? "NULL" : egoConflictLane->getID())
    2810              :                   << ")\nfoeBestLanes: " << ::toString(foe->getBestLanesContinuation())
    2811              :                   << std::endl;
    2812              : #endif
    2813      6244550 :     if (foe->getLaneChangeModel().isOpposite()) {
    2814              :         // distinguish three cases
    2815              :         // 1) foe is driving in the same direction as ego and ego is driving in lane direction -> ENCOUNTER_TYPE_ON_ADJACENT_LANES
    2816              :         // 2) foe is driving in the same direction as ego and ego is also driving in the opposite direction -> ENCOUNTER_TYPE_FOLLOWING
    2817              :         // 3) foe is driving in the opposite direction as ego and both are driving way from each other -> ENCOUNTER_TYPE_NOCONFLICT_AHEAD
    2818              :         // 3) foe is driving in the opposite direction as ego and both are driving towards each other -> ENCOUNTER_TYPE_ONCOMING
    2819              : #ifdef DEBUG_SSM_OPPOSITE
    2820              : #endif
    2821        23330 :         auto egoIt = std::find(myHolder.getCurrentRouteEdge(), myHolder.getRoute().end(), foe->getEdge());
    2822        23330 :         if (egoIt != myHolder.getRoute().end()) {
    2823              :             // same direction, foe is leader
    2824        17416 :             if (myHolderMS->getLaneChangeModel().isOpposite()) {
    2825        13603 :                 if (egoConflictLane->isInternal() && !foe->getLane()->isInternal()) {
    2826              :                     // lead/follow situation resolved elsewhere
    2827              :                     return nullptr;
    2828              :                 }
    2829        13603 :                 return foe->getLane();
    2830              :             } else {
    2831              :                 // adjacent
    2832              :                 return nullptr;
    2833              :             }
    2834              :         }
    2835         5914 :         auto foeIt = std::find(foe->getCurrentRouteEdge(), foe->getRoute().end(), myHolder.getEdge());
    2836         5914 :         if (foeIt != foe->getRoute().end()) {
    2837              :             // same direction, ego is leader
    2838            0 :             if (myHolderMS->getLaneChangeModel().isOpposite()) {
    2839              :                 return egoConflictLane;
    2840              :             } else {
    2841              :                 // adjacent
    2842              :                 return nullptr;
    2843              :             }
    2844              :         }
    2845         5914 :         auto egoIt2 = std::find(myHolder.getCurrentRouteEdge(), myHolder.getRoute().end(), foe->getEdge()->getOppositeEdge());
    2846         5914 :         if (egoIt2 != myHolder.getRoute().end()) {
    2847              :             // opposite direction, driving towards each other
    2848              :             return egoConflictLane;
    2849              :         } else {
    2850              :             // opposite direction, driving away from each other
    2851              :             return nullptr;
    2852              :         }
    2853              :     }
    2854              : 
    2855      6221220 :     const MSLane* foeLane = foe->getLane();
    2856      6221220 :     std::vector<MSLane*>::const_iterator laneIter = foe->getBestLanesContinuation().begin();
    2857      6221220 :     std::vector<MSLane*>::const_iterator foeBestLanesEnd = foe->getBestLanesContinuation().end();
    2858              :     assert(foeLane->isInternal() || *laneIter == foeLane);
    2859      6221220 :     distToConflictLane = -foe->getPositionOnLane();
    2860              : 
    2861              :     // Potential conflict lies on junction if egoConflictLane is internal
    2862      6221220 :     const MSJunction* conflictJunction = egoConflictLane->isInternal() ? egoConflictLane->getEdge().getToJunction() : nullptr;
    2863              : #ifdef DEBUG_SSM
    2864              :     if (DEBUG_COND(myHolderMS))
    2865              :         if (conflictJunction != 0) {
    2866              :             std::cout << "Potential conflict on junction '" << conflictJunction->getID()
    2867              :                       << std::endl;
    2868              :         }
    2869              : #endif
    2870      6221220 :     if (foeLane->isInternal() && foeLane->getEdge().getToJunction() == conflictJunction) {
    2871              :         // foe is already on the conflict junction
    2872        86665 :         if (egoConflictLane != nullptr && egoConflictLane->isInternal() && egoConflictLane->getLinkCont()[0]->getViaLane() == foeLane) {
    2873            0 :             distToConflictLane += egoConflictLane->getLength();
    2874              :         }
    2875        86665 :         return foeLane;
    2876              :     }
    2877              : 
    2878              :     // Foe is not on the conflict junction
    2879              : 
    2880              :     // Leading internal lanes in bestlanes are resembled as a single NULL-pointer skip them
    2881      6134555 :     if (*laneIter == nullptr) {
    2882       168004 :         while (foeLane != nullptr && foeLane->isInternal()) {
    2883        84028 :             distToConflictLane += foeLane->getLength();
    2884        84028 :             foeLane = foeLane->getLinkCont()[0]->getViaLane();
    2885              :         }
    2886              :         ++laneIter;
    2887              :         assert(laneIter == foeBestLanesEnd || *laneIter != 0);
    2888              :     }
    2889              : 
    2890              :     // Look for the junction downstream along foeBestLanes
    2891      6668624 :     while (laneIter != foeBestLanesEnd && distToConflictLane <= myRange) {
    2892              :         // Eventual internal lanes were skipped
    2893              :         assert(*laneIter == foeLane || foeLane == 0);
    2894      6500237 :         foeLane = *laneIter;
    2895              :         assert(!foeLane->isInternal());
    2896      6500237 :         if (&foeLane->getEdge() == &egoConflictLane->getEdge()) {
    2897              : #ifdef DEBUG_SSM
    2898              :             if (DEBUG_COND(myHolderMS)) {
    2899              :                 std::cout << "Found conflict lane for foe: '" << foeLane->getID() << "'" << std::endl;
    2900              :             }
    2901              : #endif
    2902              :             // found the potential conflict edge along foeBestLanes
    2903      5435791 :             return foeLane;
    2904              :         }
    2905              :         // No conflict on foeLane
    2906      1064446 :         distToConflictLane += foeLane->getLength();
    2907              : 
    2908              :         // set laneIter to next non internal lane along foeBestLanes
    2909              :         ++laneIter;
    2910      1064446 :         if (laneIter == foeBestLanesEnd) {
    2911              :             return nullptr;
    2912              :         }
    2913      1033174 :         MSLane* const nextNonInternalLane = *laneIter;
    2914      1033174 :         const MSLink* const link = foeLane->getLinkTo(nextNonInternalLane);
    2915      1033174 :         if (link == nullptr) {
    2916              :             // encountered incomplete route
    2917              :             return nullptr;
    2918              :         }
    2919              :         // Set foeLane to first internal lane on the next junction
    2920              :         foeLane = link->getViaLane();
    2921              :         assert(foeLane == 0 || foeLane->isInternal());
    2922      1033174 :         if (foeLane == nullptr) {
    2923              :             foeLane = nextNonInternalLane;
    2924            0 :             continue;
    2925              :         }
    2926      1033174 :         if (foeLane->getEdge().getToJunction() == conflictJunction) {
    2927              :             assert(foeLane != 0);
    2928              : #ifdef DEBUG_SSM
    2929              :             if (DEBUG_COND(myHolderMS)) {
    2930              :                 std::cout << "Found conflict lane for foe: '" << foeLane->getID() << "'" << std::endl;
    2931              :             }
    2932              : #endif
    2933              :             // found egoConflictLane, resp. the conflict junction, along foeBestLanes
    2934       499105 :             return foeLane;
    2935              :         }
    2936              :         // No conflict on junction
    2937       534069 :         distToConflictLane += link->getInternalLengthsAfter();
    2938              :         foeLane = nextNonInternalLane;
    2939              :     }
    2940              :     // Didn't find conflicting lane on foeBestLanes within range.
    2941              :     return nullptr;
    2942              : }
    2943              : 
    2944              : void
    2945      2242129 : MSDevice_SSM::flushConflicts(bool flushAll) {
    2946              : #ifdef DEBUG_SSM
    2947              :     if (DEBUG_COND(myHolderMS)) {
    2948              :         std::cout << "\n" << SIMTIME << " Device '" << getID() << "' flushConflicts past=" << myPastConflicts.size()
    2949              :                   << " oldestActive=" << (myOldestActiveEncounterBegin == INVALID_DOUBLE ? -1 : myOldestActiveEncounterBegin)
    2950              :                   << " topBegin=" << (myPastConflicts.size() > 0 ? myPastConflicts.top()->begin : -1)
    2951              :                   << "\n";
    2952              :     }
    2953              : #endif
    2954      4488063 :     while (!myPastConflicts.empty()) {
    2955        44210 :         Encounter* top = myPastConflicts.top();
    2956        44210 :         if (flushAll || top->begin <= myOldestActiveEncounterBegin) {
    2957              :             bool write = true;
    2958         3805 :             if (myFilterConflictTypes) {
    2959              :                 std::vector<int> foundTypes;
    2960            8 :                 std::set<int> encounterTypes(top->typeSpan.begin(), top->typeSpan.end());
    2961            8 :                 std::set_intersection(
    2962              :                     myDroppedConflictTypes.begin(), myDroppedConflictTypes.end(),
    2963              :                     encounterTypes.begin(), encounterTypes.end(),
    2964              :                     std::back_inserter(foundTypes));
    2965              :                 write = foundTypes.size() == 0;
    2966            8 :             }
    2967            8 :             if (write) {
    2968         3801 :                 writeOutConflict(top);
    2969              :             }
    2970              :             myPastConflicts.pop();
    2971         3805 :             delete top;
    2972              :         } else {
    2973              :             break;
    2974              :         }
    2975              :     }
    2976      2242129 : }
    2977              : 
    2978              : void
    2979         3868 : MSDevice_SSM::flushGlobalMeasures() {
    2980         3868 :     std::string egoID = myHolderMS->getID();
    2981              : #ifdef DEBUG_SSM
    2982              :     if (DEBUG_COND(myHolderMS))
    2983              :         std::cout << SIMTIME << " flushGlobalMeasures() of vehicle '"
    2984              :                   << egoID << "'"
    2985              :                   << "'\ntoGeo=" << myUseGeoCoords << std::endl;
    2986              : #endif
    2987         3868 :     if (myComputeBR || myComputeSGAP || myComputeTGAP) {
    2988          460 :         myOutputFile->openTag("globalMeasures");
    2989          460 :         myOutputFile->writeAttr("ego", egoID);
    2990         1380 :         myOutputFile->openTag("timeSpan").writeAttr("values", myGlobalMeasuresTimeSpan).closeTag();
    2991          460 :         if (myWritePositions) {
    2992           12 :             myOutputFile->openTag("positions").writeAttr("values", myGlobalMeasuresPositions).closeTag();
    2993              :         }
    2994          460 :         if (myWriteLanesPositions) {
    2995           24 :             myOutputFile->openTag("lane").writeAttr("values", myGlobalMeasuresLaneIDs).closeTag();
    2996           24 :             myOutputFile->openTag("lanePosition").writeAttr("values", myGlobalMeasuresLanesPositions).closeTag();
    2997              :         }
    2998          460 :         if (myComputeBR) {
    2999         1380 :             myOutputFile->openTag("BRSpan").writeAttr("values", myBRspan).closeTag();
    3000              : 
    3001          460 :             if (myMaxBR.second != 0.0) {
    3002          384 :                 if (myUseGeoCoords) {
    3003            4 :                     toGeo(myMaxBR.first.second);
    3004              :                 }
    3005         1920 :                 myOutputFile->openTag("maxBR").writeAttr("time", myMaxBR.first.first).writeAttr("position", makeStringWithNAs(myMaxBR.first.second)).writeAttr("value", myMaxBR.second).closeTag();
    3006              :             }
    3007              :         }
    3008              : 
    3009          460 :         if (myComputeSGAP) {
    3010         1344 :             myOutputFile->openTag("SGAPSpan").writeAttr("values", makeStringWithNAs(mySGAPspan, INVALID_DOUBLE)).closeTag();
    3011          448 :             if (myMinSGAP.second != "") {
    3012          176 :                 if (myUseGeoCoords) {
    3013            4 :                     toGeo(myMinSGAP.first.first.second);
    3014              :                 }
    3015          352 :                 myOutputFile->openTag("minSGAP").writeAttr("time", myMinSGAP.first.first.first)
    3016          352 :                 .writeAttr("position", makeStringWithNAs(myMinSGAP.first.first.second))
    3017          352 :                 .writeAttr("value", myMinSGAP.first.second)
    3018          528 :                 .writeAttr("leader", myMinSGAP.second).closeTag();
    3019              :             }
    3020              :         }
    3021              : 
    3022          460 :         if (myComputeTGAP) {
    3023         1344 :             myOutputFile->openTag("TGAPSpan").writeAttr("values", makeStringWithNAs(myTGAPspan, INVALID_DOUBLE)).closeTag();
    3024          448 :             if (myMinTGAP.second != "") {
    3025          168 :                 if (myUseGeoCoords) {
    3026            4 :                     toGeo(myMinTGAP.first.first.second);
    3027              :                 }
    3028          336 :                 myOutputFile->openTag("minTGAP").writeAttr("time", myMinTGAP.first.first.first)
    3029          336 :                 .writeAttr("position", makeStringWithNAs(myMinTGAP.first.first.second))
    3030          336 :                 .writeAttr("value", myMinTGAP.first.second)
    3031          504 :                 .writeAttr("leader", myMinTGAP.second).closeTag();
    3032              :             }
    3033              :         }
    3034              :         // close globalMeasures
    3035          920 :         myOutputFile->closeTag();
    3036              :     }
    3037         3868 : }
    3038              : 
    3039              : void
    3040         2902 : MSDevice_SSM::toGeo(Position& x) {
    3041         2902 :     GeoConvHelper::getFinal().cartesian2geo(x);
    3042         2902 : }
    3043              : 
    3044              : void
    3045           12 : MSDevice_SSM::toGeo(PositionVector& xv) {
    3046          828 :     for (Position& x : xv) {
    3047              :         if (x != Position::INVALID) {
    3048          688 :             toGeo(x);
    3049              :         }
    3050              :     }
    3051           12 : }
    3052              : 
    3053              : void
    3054         3801 : MSDevice_SSM::writeOutConflict(Encounter* e) {
    3055              : #ifdef DEBUG_SSM
    3056              :     if (DEBUG_COND(myHolderMS))
    3057              :         std::cout << SIMTIME << " writeOutConflict() of vehicles '"
    3058              :                   << e->egoID << "' and '" << e->foeID
    3059              :                   << "'\ntoGeo=" << myUseGeoCoords << std::endl;
    3060              : #endif
    3061         3801 :     myOutputFile->openTag("conflict");
    3062        11403 :     myOutputFile->writeAttr("begin", e->begin).writeAttr("end", e->end);
    3063        11403 :     myOutputFile->writeAttr("ego", e->egoID).writeAttr("foe", e->foeID);
    3064              : 
    3065         3801 :     if (mySaveTrajectories) {
    3066          420 :         myOutputFile->openTag("timeSpan").writeAttr("values", e->timeSpan).closeTag();
    3067          420 :         myOutputFile->openTag("typeSpan").writeAttr("values", e->typeSpan).closeTag();
    3068              : 
    3069              :         // Some useful snippets for that (from MSFCDExport.cpp):
    3070          140 :         if (myUseGeoCoords) {
    3071            4 :             toGeo(e->egoTrajectory.x);
    3072            4 :             toGeo(e->foeTrajectory.x);
    3073            4 :             toGeo(e->conflictPointSpan);
    3074              :         }
    3075              : 
    3076          420 :         myOutputFile->openTag("egoPosition").writeAttr("values", ::toString(e->egoTrajectory.x, myUseGeoCoords ? gPrecisionGeo : gPrecision)).closeTag();
    3077          140 :         if (myWriteLanesPositions) {
    3078           12 :             myOutputFile->openTag("egoLane").writeAttr("values", ::toString(e->egoTrajectory.lane)).closeTag();
    3079           12 :             myOutputFile->openTag("egoLanePosition").writeAttr("values", ::toString(e->egoTrajectory.lanePos)).closeTag();
    3080              :         }
    3081          420 :         myOutputFile->openTag("egoVelocity").writeAttr("values", ::toString(e->egoTrajectory.v)).closeTag();
    3082              : 
    3083          420 :         myOutputFile->openTag("foePosition").writeAttr("values", ::toString(e->foeTrajectory.x, myUseGeoCoords ? gPrecisionGeo : gPrecision)).closeTag();
    3084          140 :         if (myWriteLanesPositions) {
    3085           12 :             myOutputFile->openTag("foeLane").writeAttr("values", ::toString(e->foeTrajectory.lane)).closeTag();
    3086           12 :             myOutputFile->openTag("foeLanePosition").writeAttr("values", ::toString(e->foeTrajectory.lanePos)).closeTag();
    3087              :         }
    3088          420 :         myOutputFile->openTag("foeVelocity").writeAttr("values", ::toString(e->foeTrajectory.v)).closeTag();
    3089              : 
    3090          420 :         myOutputFile->openTag("conflictPoint").writeAttr("values", makeStringWithNAs(e->conflictPointSpan)).closeTag();
    3091              :     }
    3092              : 
    3093         3801 :     if (myComputeTTC) {
    3094         3777 :         if (mySaveTrajectories) {
    3095          408 :             myOutputFile->openTag("TTCSpan").writeAttr("values", makeStringWithNAs(e->TTCspan, INVALID_DOUBLE)).closeTag();
    3096              :         }
    3097         3777 :         if (e->minTTC.time == INVALID_DOUBLE) {
    3098         1183 :             myOutputFile->openTag("minTTC").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").writeAttr("speed", "NA").closeTag();
    3099              :         } else {
    3100         3608 :             std::string time = ::toString(e->minTTC.time);
    3101         3608 :             std::string type = ::toString(int(e->minTTC.type));
    3102         3608 :             std::string value = ::toString(e->minTTC.value);
    3103         3608 :             std::string speed = ::toString(e->minTTC.speed);
    3104         3608 :             if (myUseGeoCoords) {
    3105         1110 :                 toGeo(e->minTTC.pos);
    3106              :             }
    3107         3608 :             std::string position = makeStringWithNAs(e->minTTC.pos);
    3108        25256 :             myOutputFile->openTag("minTTC").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).writeAttr("speed", speed).closeTag();
    3109              :         }
    3110              :     }
    3111         3801 :     if (myComputeDRAC) {
    3112         3751 :         if (mySaveTrajectories) {
    3113          420 :             myOutputFile->openTag("DRACSpan").writeAttr("values", makeStringWithNAs(e->DRACspan, {0.0, INVALID_DOUBLE})).closeTag();
    3114              :         }
    3115         3751 :         if (e->maxDRAC.time == INVALID_DOUBLE) {
    3116         1120 :             myOutputFile->openTag("maxDRAC").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").writeAttr("speed", "NA").closeTag();
    3117              :         } else {
    3118         3591 :             std::string time = ::toString(e->maxDRAC.time);
    3119         3591 :             std::string type = ::toString(int(e->maxDRAC.type));
    3120         3591 :             std::string value = ::toString(e->maxDRAC.value);
    3121         3591 :             std::string speed = ::toString(e->maxDRAC.speed);
    3122         3591 :             if (myUseGeoCoords) {
    3123         1092 :                 toGeo(e->maxDRAC.pos);
    3124              :             }
    3125         3591 :             std::string position = makeStringWithNAs(e->maxDRAC.pos);
    3126        25137 :             myOutputFile->openTag("maxDRAC").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).writeAttr("speed", speed).closeTag();
    3127              :         }
    3128              :     }
    3129         3801 :     if (myComputePET) {
    3130         1551 :         if (e->PET.time == INVALID_DOUBLE) {
    3131         9401 :             myOutputFile->openTag("PET").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").writeAttr("speed", "NA").closeTag();
    3132              :         } else {
    3133          208 :             std::string time = ::toString(e->PET.time);
    3134          208 :             std::string type = ::toString(int(e->PET.type));
    3135          208 :             std::string value = ::toString(e->PET.value);
    3136          208 :             std::string speed = ::toString(e->PET.speed);
    3137          208 :             if (myUseGeoCoords) {
    3138            0 :                 toGeo(e->PET.pos);
    3139              :             }
    3140          208 :             std::string position = ::toString(e->PET.pos, myUseGeoCoords ? gPrecisionGeo : gPrecision);
    3141         1456 :             myOutputFile->openTag("PET").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).writeAttr("speed", speed).closeTag();
    3142              :         }
    3143              :     }
    3144         3801 :     if (myComputePPET) {
    3145           81 :         if (mySaveTrajectories) {
    3146           12 :             myOutputFile->openTag("PPETSpan").writeAttr("values", makeStringWithNAs(e->PPETspan, INVALID_DOUBLE)).closeTag();
    3147              :         }
    3148           81 :         if (e->minPPET.time == INVALID_DOUBLE) {
    3149          336 :             myOutputFile->openTag("minPPET").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").writeAttr("speed", "NA").closeTag();
    3150              :         } else {
    3151           33 :             std::string time = ::toString(e->minPPET.time);
    3152           33 :             std::string type = ::toString(int(e->minPPET.type));
    3153           33 :             std::string value = ::toString(e->minPPET.value);
    3154           33 :             std::string speed = ::toString(e->minPPET.speed);
    3155           33 :             if (myUseGeoCoords) {
    3156            0 :                 toGeo(e->minPPET.pos);
    3157              :             }
    3158           33 :             std::string position = makeStringWithNAs(e->minPPET.pos);
    3159          231 :             myOutputFile->openTag("minPPET").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).writeAttr("speed", speed).closeTag();
    3160              :         }
    3161              :     }
    3162         3801 :     if (myComputeMDRAC) {
    3163          125 :         if (mySaveTrajectories) {
    3164           36 :             myOutputFile->openTag("MDRACSpan").writeAttr("values", makeStringWithNAs(e->MDRACspan, {0.0, INVALID_DOUBLE})).closeTag();
    3165              :         }
    3166          125 :         if (e->maxMDRAC.time == INVALID_DOUBLE) {
    3167           42 :             myOutputFile->openTag("maxMDRAC").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").writeAttr("speed", "NA").closeTag();
    3168              :         } else {
    3169          119 :             std::string time = ::toString(e->maxMDRAC.time);
    3170          119 :             std::string type = ::toString(int(e->maxMDRAC.type));
    3171          119 :             std::string value = ::toString(e->maxMDRAC.value);
    3172          119 :             std::string speed = ::toString(e->maxMDRAC.speed);
    3173          119 :             if (myUseGeoCoords) {
    3174            0 :                 toGeo(e->maxMDRAC.pos);
    3175              :             }
    3176          119 :             std::string position = makeStringWithNAs(e->maxMDRAC.pos);
    3177          833 :             myOutputFile->openTag("maxMDRAC").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).writeAttr("speed", speed).closeTag();
    3178              :         }
    3179              :     }
    3180         3801 :     myOutputFile->closeTag();
    3181         3801 : }
    3182              : 
    3183              : std::string
    3184         1036 : MSDevice_SSM::makeStringWithNAs(const std::vector<double>& v, double NA) {
    3185         1036 :     std::string res = "";
    3186       144686 :     for (std::vector<double>::const_iterator i = v.begin(); i != v.end(); ++i) {
    3187       287300 :         res += (i == v.begin() ? "" : " ") + (*i == NA ? "NA" : ::toString(*i));
    3188              :     }
    3189         1036 :     return res;
    3190              : }
    3191              : 
    3192              : std::string
    3193          152 : MSDevice_SSM::makeStringWithNAs(const std::vector<double>& v, const std::vector<double>& NAs) {
    3194          152 :     std::string res = "";
    3195        10948 :     for (std::vector<double>::const_iterator i = v.begin(); i != v.end(); ++i) {
    3196        21592 :         res += (i == v.begin() ? "" : " ") + (find(NAs.begin(), NAs.end(), *i) != NAs.end() ? "NA" : ::toString(*i));
    3197              :     }
    3198          152 :     return res;
    3199              : }
    3200              : 
    3201              : std::string
    3202          140 : MSDevice_SSM::makeStringWithNAs(const PositionVector& v) {
    3203          140 :     const int precision = myUseGeoCoords ? gPrecisionGeo : gPrecision;
    3204          140 :     std::string res = "";
    3205         9756 :     for (PositionVector::const_iterator i = v.begin(); i != v.end(); ++i) {
    3206        19232 :         res += (i == v.begin() ? "" : " ") + (*i == Position::INVALID ? "NA" : ::toString(*i, precision));
    3207              :     }
    3208          140 :     return res;
    3209              : }
    3210              : 
    3211              : std::string
    3212         8079 : MSDevice_SSM::makeStringWithNAs(const Position& p) {
    3213         8079 :     const int precision = myUseGeoCoords ? gPrecisionGeo : gPrecision;
    3214         8079 :     return p == Position::INVALID ? "NA" : toString(p, precision);
    3215              : }
    3216              : 
    3217              : 
    3218              : std::string
    3219            0 : MSDevice_SSM::writeNA(double v, double NA) {
    3220            0 :     return v == NA ? "NA" : toString(v);
    3221              : }
    3222              : 
    3223              : // ---------------------------------------------------------------------------
    3224              : // MSDevice_SSM-methods
    3225              : // ---------------------------------------------------------------------------
    3226         3868 : MSDevice_SSM::MSDevice_SSM(SUMOVehicle& holder, const std::string& id, std::string outputFilename, std::map<std::string, double> thresholds,
    3227              :                            bool trajectories, double range, double extraTime, bool useGeoCoords, bool writePositions, bool writeLanesPositions,
    3228         3868 :                            std::vector<int> conflictTypeFilter) :
    3229              :     MSVehicleDevice(holder, id),
    3230              :     myThresholds(thresholds),
    3231         3868 :     mySaveTrajectories(trajectories),
    3232         3868 :     myRange(range),
    3233         3868 :     myMDRACPRT(getMDRAC_PRT(holder)),
    3234         3868 :     myExtraTime(extraTime),
    3235         3868 :     myUseGeoCoords(useGeoCoords),
    3236         3868 :     myWritePositions(writePositions),
    3237         3868 :     myWriteLanesPositions(writeLanesPositions),
    3238         3868 :     myOldestActiveEncounterBegin(INVALID_DOUBLE),
    3239              :     myMaxBR(std::make_pair(-1, Position(0., 0.)), 0.0),
    3240              :     myMinSGAP(std::make_pair(std::make_pair(-1, Position(0., 0.)), std::numeric_limits<double>::max()), ""),
    3241         7736 :     myMinTGAP(std::make_pair(std::make_pair(-1, Position(0., 0.)), std::numeric_limits<double>::max()), "") {
    3242              :     // Take care! Holder is currently being constructed. Cast occurs before completion.
    3243         3868 :     myHolderMS = static_cast<MSVehicle*>(&holder);
    3244              : 
    3245         3868 :     myComputeTTC = myThresholds.find("TTC") != myThresholds.end();
    3246         3868 :     myComputeDRAC = myThresholds.find("DRAC") != myThresholds.end();
    3247         3868 :     myComputeMDRAC = myThresholds.find("MDRAC") != myThresholds.end();
    3248         3868 :     myComputePET = myThresholds.find("PET") != myThresholds.end();
    3249         3868 :     myComputePPET = myThresholds.find("PPET") != myThresholds.end();
    3250              : 
    3251         3868 :     myComputeBR = myThresholds.find("BR") != myThresholds.end();
    3252         3868 :     myComputeSGAP = myThresholds.find("SGAP") != myThresholds.end();
    3253         3868 :     myComputeTGAP = myThresholds.find("TGAP") != myThresholds.end();
    3254              : 
    3255         3868 :     myDroppedConflictTypes = conflictTypeFilter;
    3256         3868 :     myFilterConflictTypes = myDroppedConflictTypes.size() > 0;
    3257              : 
    3258         3868 :     myActiveEncounters = EncounterVector();
    3259         3868 :     myPastConflicts = EncounterQueue();
    3260              : 
    3261              :     // XXX: Who deletes the OutputDevice?
    3262         3868 :     myOutputFile = &OutputDevice::getDevice(outputFilename);
    3263              : //    TODO: make xsd, include header
    3264              : //    myOutputFile.writeXMLHeader("SSMLog", "SSMLog.xsd");
    3265              :     if (myCreatedOutputFiles.count(outputFilename) == 0) {
    3266          936 :         myOutputFile->writeXMLHeader("SSMLog", "");
    3267              :         myCreatedOutputFiles.insert(outputFilename);
    3268              :     }
    3269              :     // register at static instance container
    3270         3868 :     myInstances->insert(this);
    3271              : 
    3272              : #ifdef DEBUG_SSM
    3273              :     if (DEBUG_COND(myHolderMS)) {
    3274              :         std::vector<std::string> measures;
    3275              :         std::vector<double> threshVals;
    3276              :         for (std::map<std::string, double>::const_iterator i = myThresholds.begin(); i != myThresholds.end(); ++i) {
    3277              :             measures.push_back(i->first);
    3278              :             threshVals.push_back(i->second);
    3279              :         }
    3280              :         std::cout << "Initialized ssm device '" << id << "' with "
    3281              :                   << "myMeasures=" << joinToString(measures, " ")
    3282              :                   << ", myThresholds=" << joinToString(threshVals, " ")
    3283              :                   << ", mySaveTrajectories=" << mySaveTrajectories
    3284              :                   << ", myRange=" << myRange << ", output file=" << outputFilename << ", extra time=" << myExtraTime << ", useGeo=" << myUseGeoCoords << "\n";
    3285              :     }
    3286              : #endif
    3287         3868 : }
    3288              : 
    3289              : /// @brief Destructor.
    3290         7736 : MSDevice_SSM::~MSDevice_SSM() {
    3291              :     // Deleted in ~BaseVehicle()
    3292              :     // unregister from static instance container
    3293         3868 :     myInstances->erase(this);
    3294         3868 :     resetEncounters();
    3295         3868 :     flushConflicts(true);
    3296         3868 :     flushGlobalMeasures();
    3297        11604 : }
    3298              : 
    3299              : 
    3300              : bool
    3301        33843 : MSDevice_SSM::notifyEnter(SUMOTrafficObject& veh, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
    3302              :     assert(veh.isVehicle());
    3303              : #ifdef DEBUG_SSM_NOTIFICATIONS
    3304              :     MSBaseVehicle* v = (MSBaseVehicle*) &veh;
    3305              :     if (DEBUG_COND(v)) {
    3306              :         std::cout << SIMTIME << "device '" << getID() << "' notifyEnter: reason=" << reason << " currentEdge=" << v->getLane()->getEdge().getID() << "\n";
    3307              :     }
    3308              : #else
    3309              :     UNUSED_PARAMETER(veh);
    3310              :     UNUSED_PARAMETER(reason);
    3311              : #endif
    3312        33843 :     return true; // keep the device
    3313              : }
    3314              : 
    3315              : bool
    3316        33803 : MSDevice_SSM::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/,
    3317              :                           MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
    3318              :     assert(veh.isVehicle());
    3319              : #ifdef DEBUG_SSM_NOTIFICATIONS
    3320              :     MSBaseVehicle* v = (MSBaseVehicle*) &veh;
    3321              :     if (DEBUG_COND(v)) {
    3322              :         std::cout << SIMTIME << "device '" << getID() << "' notifyLeave: reason=" << reason << " currentEdge=" << v->getLane()->getEdge().getID() << "\n";
    3323              :     }
    3324              : #else
    3325              :     UNUSED_PARAMETER(veh);
    3326              :     UNUSED_PARAMETER(reason);
    3327              : #endif
    3328        33803 :     return true; // keep the device
    3329              : }
    3330              : 
    3331              : bool
    3332      1736545 : MSDevice_SSM::notifyMove(SUMOTrafficObject& veh, double /* oldPos */,
    3333              :                          double /* newPos */, double newSpeed) {
    3334              : #ifdef DEBUG_SSM_NOTIFICATIONS
    3335              :     MSBaseVehicle* v = (MSBaseVehicle*) &veh;
    3336              :     if (DEBUG_COND(v)) {
    3337              :         std::cout << SIMTIME << "device '" << getID() << "' notifyMove: newSpeed=" << newSpeed << "\n";
    3338              :     }
    3339              : #else
    3340              :     UNUSED_PARAMETER(veh);
    3341              :     UNUSED_PARAMETER(newSpeed);
    3342              : #endif
    3343      1736545 :     return true; // keep the device
    3344              : }
    3345              : 
    3346              : 
    3347              : void
    3348      1733333 : MSDevice_SSM::findSurroundingVehicles(const MSVehicle& veh, double range, FoeInfoMap& foeCollector) {
    3349      1733333 :     if (!veh.isOnRoad()) {
    3350            0 :         return;
    3351              :     }
    3352              : #ifdef DEBUG_SSM_SURROUNDING
    3353              : 
    3354              :     gDebugFlag3 = DEBUG_COND_FIND(veh);
    3355              :     if (gDebugFlag3) {
    3356              :         std::cout << SIMTIME << " Looking for surrounding vehicles for ego vehicle '" << veh.getID()
    3357              :                   << "' on edge '" << veh.getLane()->getEdge().getID()
    3358              :                   << "'."
    3359              :                   << "\nVehicle's best lanes = " << ::toString(veh.getBestLanesContinuation())
    3360              :                   << std::endl;
    3361              :     }
    3362              : #endif
    3363              : 
    3364              : 
    3365              :     // The requesting vehicle's current route
    3366              :     // XXX: Restriction to route scanning may have to be generalized to scanning of possible continuations when
    3367              :     //      considering situations involving sudden route changes. See also the definition of the EncounterTypes.
    3368              :     //      A second problem is that following situations on deviating routes may result in closing encounters
    3369              :     //      too early if a leading foe is not traced on its new lane. (see test 'foe_leader_deviating_routes')
    3370              : 
    3371              :     // If veh is on an internal edge, the edgeIter points towards the last edge before the junction
    3372              :     //ConstMSEdgeVector::const_iterator edgeIter = veh.getCurrentRouteEdge();
    3373              :     //assert(*edgeIter != 0);
    3374              : 
    3375              :     // Best continuation lanes for the ego vehicle
    3376      1733333 :     std::vector<MSLane*> egoBestLanes = veh.getBestLanesContinuation();
    3377              : 
    3378              :     // current lane in loop below
    3379      1733333 :     const MSLane* lane = veh.getLane();
    3380              :     const MSEdge* egoEdge = &(lane->getEdge());
    3381      1733333 :     const bool isOpposite = veh.getLaneChangeModel().isOpposite();
    3382              :     std::vector<MSLane*>::const_iterator laneIter = egoBestLanes.begin();
    3383              :     assert(lane->isInternal() || lane == *laneIter || isOpposite);
    3384              :     assert(lane != 0);
    3385      1733333 :     if (lane->isInternal() && egoBestLanes[0] != nullptr) { // outdated BestLanes, see #11336
    3386              :         return;
    3387              :     }
    3388              : 
    3389      1733333 :     if (isOpposite) {
    3390        57277 :         for (int i = 0; i < (int)egoBestLanes.size(); i++) {
    3391        39622 :             if (egoBestLanes[i] != nullptr && egoBestLanes[i]->getEdge().getOppositeEdge() != nullptr) {
    3392        28790 :                 egoBestLanes[i] = egoBestLanes[i]->getEdge().getOppositeEdge()->getLanes().back();
    3393              :             }
    3394              :         }
    3395              :     }
    3396              : 
    3397              :     // next non-internal lane on the route
    3398              :     const MSLane* nextNonInternalLane = nullptr;
    3399              : 
    3400              :     const MSEdge* edge; // current edge in loop below
    3401              : 
    3402              :     // Init pos with vehicle's current position. Below pos is set to zero to denote
    3403              :     // the beginning position of the currently considered edge
    3404      1733333 :     double pos = veh.getPositionOnLane();
    3405              :     // remainingDownstreamRange is the range minus the distance that is already scanned downstream along the vehicles route
    3406              :     double remainingDownstreamRange = range;
    3407              :     // distToConflictLane is the distance of the ego vehicle to the start of the currently considered potential conflict lane (can be negative for its current lane)
    3408      1733333 :     double distToConflictLane = isOpposite ? pos - veh.getLane()->getLength() : -pos;
    3409              : 
    3410              :     // remember already visited lanes (no matter whether internal or not) and junctions downstream along the route
    3411              :     std::set<const MSLane*> seenLanes;
    3412              :     std::set<const MSJunction*> routeJunctions;
    3413              : 
    3414              :     // Starting points for upstream scans to be executed after downstream scan is complete.
    3415              :     // Holds pairs (starting edge, starting position on edge)
    3416              :     std::vector<UpstreamScanStartInfo> upstreamScanStartPositions;
    3417              : 
    3418              : 
    3419              :     // if the current edge is internal, collect all vehicles from the junction and within upstream range (except on the vehicles own edge),
    3420              :     // this is analogous to the code treating junctions in the loop below. Note that the distance on the junction itself is not included into
    3421              :     // range, so vehicles farther away than range can be collected, too.
    3422      1733333 :     if (lane->isInternal()) {
    3423              :         edge = &(lane->getEdge());
    3424              : 
    3425              : #ifdef DEBUG_SSM_SURROUNDING
    3426              :         if (gDebugFlag3) {
    3427              :             std::cout << SIMTIME << " Vehicle '" << veh.getID() << "' is on internal edge " << edge->getID() << "'." << std::endl;
    3428              : //                  << "Previous edge of its route: '" << (*edgeIter)->getID() << "'" << std::endl;
    3429              :         }
    3430              : #endif
    3431              : 
    3432              :         assert(edge->getToJunction() == edge->getFromJunction());
    3433              : 
    3434        44355 :         const MSJunction* junction = edge->getToJunction();
    3435              :         // Collect vehicles on the junction
    3436        44355 :         getVehiclesOnJunction(junction, lane, distToConflictLane, lane, foeCollector, seenLanes);
    3437              :         routeJunctions.insert(junction);
    3438              : 
    3439              :         // Collect vehicles on incoming edges.
    3440              :         // Note that this includes the previous edge on the ego vehicle's route.
    3441              :         // (The distance on the current internal edge is ignored)
    3442              :         const ConstMSEdgeVector& incoming = junction->getIncoming();
    3443       343383 :         for (ConstMSEdgeVector::const_iterator ei = incoming.begin(); ei != incoming.end(); ++ei) {
    3444       299028 :             if ((*ei)->isInternal()) {
    3445       186713 :                 continue;
    3446              :             }
    3447              :             // Upstream range is taken from the vehicle's back
    3448       112315 :             upstreamScanStartPositions.push_back(UpstreamScanStartInfo(*ei, (*ei)->getLength(), range + veh.getLength(), distToConflictLane, lane));
    3449              :         }
    3450              : 
    3451              : //        // Take into account internal distance covered on the current lane
    3452              : //        (commented out, because upstream scanning disregards internal lanes on the last scanned junction
    3453              : //        -- this makes the scanning symmetric between leader and follower)
    3454              : //        remainingDownstreamRange -= lane->getLength() - pos;
    3455              : 
    3456              :         // Take into account non-internal lengths until next non-internal lane
    3457        44355 :         MSLink* link = lane->getLinkCont()[0];
    3458        44355 :         remainingDownstreamRange -= link->getInternalLengthsAfter();
    3459        44355 :         distToConflictLane += lane->getLength() + link->getInternalLengthsAfter();
    3460              : 
    3461              :         // The next non-internal lane
    3462              :         pos = 0.;
    3463        44355 :         lane = *(++laneIter);
    3464              :         edge = &lane->getEdge();
    3465              :     } else {
    3466              :         // Collect all vehicles in range behind ego vehicle
    3467              :         edge = &(lane->getEdge());
    3468      1688978 :         double edgeLength = edge->getLength();
    3469      1688978 :         double startScanPos = std::min(pos + remainingDownstreamRange, edgeLength);
    3470      3377956 :         upstreamScanStartPositions.push_back(UpstreamScanStartInfo(edge, startScanPos, std::max(0., startScanPos - pos + range + veh.getLength()), distToConflictLane, lane));
    3471              :     }
    3472              : 
    3473              :     assert(lane != 0);
    3474              :     assert(!lane->isInternal());
    3475              : 
    3476              :     // Advance downstream the ego vehicle's route for distance 'range'.
    3477              :     // Collect all vehicles on the traversed Edges and on incoming edges at junctions
    3478              :     // and starting points for upstream vehicle collection strated below after downstream scan.
    3479      1991412 :     while (remainingDownstreamRange > 0.) {
    3480              : 
    3481              : #ifdef DEBUG_SSM_SURROUNDING
    3482              :         if (gDebugFlag3) {
    3483              :             std::cout << SIMTIME << " Scanning downstream for vehicle '" << veh.getID() << "' on lane '" << veh.getLane()->getID() << "', position=" << pos << ".\n"
    3484              :                       << "Considering edge '" << edge->getID() << "' Remaining downstream range = " << remainingDownstreamRange
    3485              :                       << "\nbestLanes=" << ::toString(egoBestLanes) << "\n"
    3486              :                       << std::endl;
    3487              :         }
    3488              : #endif
    3489              :         assert(!edge->isInternal());
    3490              :         assert(!lane->isInternal());
    3491              :         assert(pos == 0 || lane == veh.getLane());
    3492      1951329 :         if (pos + remainingDownstreamRange < lane->getLength()) {
    3493              :             // scan range ends on this lane
    3494      1644523 :             if (edge->getID() != egoEdge->getID()) {
    3495       256032 :                 upstreamScanStartPositions.push_back(UpstreamScanStartInfo(edge, pos + remainingDownstreamRange, remainingDownstreamRange, distToConflictLane, lane));
    3496              :             }
    3497              :             // scanned required downstream range
    3498              :             break;
    3499              :         } else {
    3500              :             // Also need to scan area that reaches beyond the lane
    3501              :             // Collecting vehicles on non-internal edge ahead
    3502       306806 :             if (edge->getID() != egoEdge->getID()) {
    3503         6319 :                 upstreamScanStartPositions.push_back(UpstreamScanStartInfo(edge, edge->getLength(), edge->getLength() - pos, distToConflictLane, lane));
    3504              :             }
    3505              :             // account for scanned distance on lane
    3506       306806 :             remainingDownstreamRange -= lane->getLength() - pos;
    3507       306806 :             distToConflictLane += lane->getLength();
    3508              :             pos = 0.;
    3509              : 
    3510              :             // proceed to next non-internal lane
    3511              :             ++laneIter;
    3512              :             assert(laneIter == egoBestLanes.end() || *laneIter != 0);
    3513              : 
    3514              :             // If the vehicle's best lanes go on, collect vehicles on the upcoming junction
    3515       306806 :             if (laneIter != egoBestLanes.end()) {
    3516              :                 // Upcoming junction
    3517              :                 const MSJunction* junction;
    3518       259299 :                 if (isOpposite) {
    3519         1387 :                     junction = lane->getParallelOpposite()->getEdge().getToJunction();
    3520              :                 } else {
    3521       257912 :                     junction = lane->getEdge().getToJunction();
    3522              :                 }
    3523              : 
    3524              : 
    3525              :                 // Find connection for ego on the junction
    3526       259299 :                 nextNonInternalLane = *laneIter;
    3527       259299 :                 const MSLink* link = lane->getLinkTo(nextNonInternalLane);
    3528       259299 :                 if (isOpposite && link == nullptr) {
    3529         1387 :                     link = nextNonInternalLane->getLinkTo(lane);
    3530         1387 :                     if (link == nullptr) {
    3531          535 :                         link = lane->getParallelOpposite()->getLinkTo(nextNonInternalLane);
    3532              :                     }
    3533              :                 }
    3534       258447 :                 if (link == nullptr) {
    3535              :                     // disconnected route
    3536              :                     break;
    3537              :                 }
    3538              : 
    3539              :                 // First lane of the connection
    3540       258079 :                 lane = link->getViaLane();
    3541       258079 :                 if (lane == nullptr) {
    3542              :                     // link without internal lane
    3543           80 :                     lane = nextNonInternalLane;
    3544              :                     edge = &(lane->getEdge());
    3545           80 :                     if (seenLanes.count(lane) == 0) {
    3546              :                         seenLanes.insert(lane);
    3547           80 :                         continue;
    3548              :                     } else {
    3549              :                         break;
    3550              :                     }
    3551              :                 }
    3552              : 
    3553              :                 if (seenLanes.count(lane) == 0) {
    3554              :                     // Collect vehicles on the junction, if it wasn't considered already
    3555       257999 :                     getVehiclesOnJunction(junction, lane, distToConflictLane, lane, foeCollector, seenLanes);
    3556              :                     routeJunctions.insert(junction);
    3557              : 
    3558              :                     // Collect vehicles on incoming edges (except the last edge, where we already collected). Use full range.
    3559       257999 :                     if (isOpposite) {
    3560              :                         // look for vehicles that are also driving on the opposite side behind ego
    3561              :                         const ConstMSEdgeVector& outgoing = junction->getOutgoing();
    3562         9075 :                         for (ConstMSEdgeVector::const_iterator ei = outgoing.begin(); ei != outgoing.end(); ++ei) {
    3563         7688 :                             if (*ei == edge || (*ei)->isInternal()) {
    3564         6301 :                                 continue;
    3565              :                             }
    3566         1387 :                             upstreamScanStartPositions.push_back(UpstreamScanStartInfo(*ei, (*ei)->getLength(), range, distToConflictLane, lane));
    3567              :                         }
    3568              :                     } else {
    3569              :                         const ConstMSEdgeVector& incoming = junction->getIncoming();
    3570      1464327 :                         for (ConstMSEdgeVector::const_iterator ei = incoming.begin(); ei != incoming.end(); ++ei) {
    3571      1207715 :                             if (*ei == edge || (*ei)->isInternal()) {
    3572       926108 :                                 continue;
    3573              :                             }
    3574       281607 :                             upstreamScanStartPositions.push_back(UpstreamScanStartInfo(*ei, (*ei)->getLength(), range, distToConflictLane, lane));
    3575              :                         }
    3576              :                     }
    3577              : 
    3578              :                     // account for scanned distance on junction
    3579       257999 :                     double linkLength = link->getInternalLengthsAfter();
    3580       257999 :                     remainingDownstreamRange -= linkLength;
    3581       257999 :                     distToConflictLane += linkLength;
    3582              : #ifdef DEBUG_SSM_SURROUNDING
    3583              :                     if (gDebugFlag3) {
    3584              :                         std::cout << "    Downstream Scan for vehicle '" << veh.getID() << "' proceeded over junction '" << junction->getID()
    3585              :                                   << "',\n    linkLength=" << linkLength << ", remainingDownstreamRange=" << remainingDownstreamRange
    3586              :                                   << std::endl;
    3587              :                     }
    3588              : #endif
    3589              : 
    3590              :                     // update ego's lane to next non internal edge
    3591       257999 :                     lane = nextNonInternalLane;
    3592              :                     edge = &(lane->getEdge());
    3593              :                 } else {
    3594              : #ifdef DEBUG_SSM_SURROUNDING
    3595              :                     if (gDebugFlag3) {
    3596              :                         std::cout << "    Downstream Scan for vehicle '" << veh.getID() << "' stops at lane '" << lane->getID()
    3597              :                                   << "', which has already been scanned."
    3598              :                                   << std::endl;
    3599              :                     }
    3600              : #endif
    3601              :                     break;
    3602              :                 }
    3603              :             } else {
    3604              :                 // Further vehicle path unknown, break search
    3605              :                 break;
    3606              :             }
    3607              :         }
    3608              :     }
    3609              :     // add junction from the end of the route
    3610      1733333 :     routeJunctions.insert(lane->getEdge().getToJunction());
    3611              : 
    3612              : 
    3613              :     // Scan upstream branches from collected starting points
    3614      4079971 :     for (UpstreamScanStartInfo& i : upstreamScanStartPositions) {
    3615      2346638 :         getUpstreamVehicles(i, foeCollector, seenLanes, routeJunctions);
    3616              :     }
    3617              : 
    3618              : #ifdef DEBUG_SSM_SURROUNDING
    3619              :     if (gDebugFlag3) {
    3620              :         for (std::pair<const MSVehicle*, FoeInfo*> foeInfo : foeCollector) {
    3621              :             std::cout << "    foe " << foeInfo.first->getID() << " conflict at " << foeInfo.second->egoConflictLane->getID() << " egoDist " << foeInfo.second->egoDistToConflictLane << std::endl;
    3622              :         }
    3623              :     }
    3624              : #endif
    3625              : 
    3626              :     // remove ego vehicle
    3627              :     const auto& it = foeCollector.find(&veh);
    3628      1733333 :     if (it != foeCollector.end()) {
    3629      1733333 :         delete it->second;
    3630              :         foeCollector.erase(it);
    3631              :     }
    3632      1733333 :     gDebugFlag3 = false;
    3633      3466666 : }
    3634              : 
    3635              : 
    3636              : void
    3637      2883391 : MSDevice_SSM::getUpstreamVehicles(const UpstreamScanStartInfo& scanStart, FoeInfoMap& foeCollector, std::set<const MSLane*>& seenLanes, const std::set<const MSJunction*>& routeJunctions) {
    3638              : #ifdef DEBUG_SSM_SURROUNDING
    3639              :     if (gDebugFlag3) {
    3640              :         std::cout << SIMTIME << " getUpstreamVehicles() for edge '" << scanStart.edge->getID() << "'"
    3641              :                   << " egoConflictLane=" << scanStart.egoConflictLane->getID()
    3642              :                   << " pos = " << scanStart.pos << " range = " << scanStart.range
    3643              :                   << std::endl;
    3644              :     }
    3645              : #endif
    3646      2883391 :     if (scanStart.range <= 0) {
    3647              :         return;
    3648              :     }
    3649              : 
    3650              :     // Collect vehicles on the given edge with position in [pos-range,pos]
    3651      6758479 :     for (MSLane* lane : scanStart.edge->getLanes()) {
    3652      3879390 :         if (seenLanes.find(lane) != seenLanes.end()) {
    3653         4302 :             return;
    3654              :         }
    3655              : #ifdef DEBUG_SSM_SURROUNDING
    3656              :         int foundCount = 0;
    3657              : #endif
    3658     37841538 :         for (MSVehicle* const veh : lane->getVehiclesSecure()) {
    3659     33966450 :             if (foeCollector.find(veh) != foeCollector.end()) {
    3660              :                 // vehicle already recognized, earlier recognized conflict has priority
    3661            0 :                 continue;
    3662              :             }
    3663     33966450 :             if (veh->getPositionOnLane() - veh->getLength() <= scanStart.pos && veh->getPositionOnLane() >= scanStart.pos - scanStart.range) {
    3664              : #ifdef DEBUG_SSM_SURROUNDING
    3665              :                 if (gDebugFlag3) {
    3666              :                     std::cout << "\t" << veh->getID() << "\n";
    3667              :                 }
    3668              :                 foundCount++;
    3669              : #endif
    3670      7764199 :                 FoeInfo* c = new FoeInfo(); // c is deleted in updateEncounter()
    3671      7764199 :                 c->egoDistToConflictLane = scanStart.egoDistToConflictLane;
    3672      7764199 :                 c->egoConflictLane = scanStart.egoConflictLane;
    3673      7764199 :                 foeCollector[veh] = c;
    3674              :             }
    3675              :         }
    3676      3875088 :         lane->releaseVehicles();
    3677              : 
    3678              : #ifdef DEBUG_SSM_SURROUNDING
    3679              :         if (gDebugFlag3 && foundCount > 0) {
    3680              :             std::cout << "\t" << lane->getID() << ": Found " << foundCount << "\n";
    3681              :         }
    3682              : #endif
    3683              :         seenLanes.insert(lane);
    3684              :     }
    3685              : 
    3686              : #ifdef DEBUG_SSM_SURROUNDING
    3687              :     if (gDebugFlag3) {
    3688              :         std::cout << std::endl;
    3689              :     }
    3690              : #endif
    3691              : 
    3692              :     // TODO: Gather vehicles from opposite direction. This should happen in any case, where opposite direction overtaking is possible.
    3693              :     //       If it isn't it might still be nicer to trace oncoming vehicles for the resulting trajectories in the encounters
    3694              :     //    if (edge->hasOpposite...)
    3695              : 
    3696      2879089 :     if (scanStart.range <= scanStart.pos) {
    3697              :         return;
    3698              :     }
    3699              : 
    3700              :     // Here we have: range > pos, i.e. we proceed collecting vehicles on preceding edges
    3701       361203 :     double remainingRange = scanStart.range - scanStart.pos;
    3702              : 
    3703              :     // Junction representing the origin of 'edge'
    3704       361203 :     const MSJunction* junction = scanStart.edge->getFromJunction();
    3705              : 
    3706              :     // stop if upstream search reaches the ego route
    3707       361203 :     if (routeJunctions.find(junction) != routeJunctions.end()) {
    3708              :         return;
    3709              :     }
    3710              : 
    3711              :     // Collect vehicles from incoming edges of the junction
    3712              :     int incomingEdgeCount = 0;
    3713       337473 :     if (!scanStart.edge->isInternal()) {
    3714              :         // collect vehicles on preceding junction (for internal edges this is already done in caller,
    3715              :         // i.e. findSurroundingVehicles() or the recursive call from getUpstreamVehicles())
    3716              : 
    3717              :         // Collect vehicles on the junction, if it wasn't considered already
    3718              :         // run vehicle collection for all incoming connections
    3719      1593015 :         for (MSLane* const internalLane : junction->getInternalLanes()) {
    3720      1255542 :             if (internalLane->getEdge().getSuccessors()[0]->getID() == scanStart.edge->getID()) {
    3721       563266 :                 getVehiclesOnJunction(junction, internalLane, scanStart.egoDistToConflictLane, scanStart.egoConflictLane, foeCollector, seenLanes);
    3722       563266 :                 incomingEdgeCount++;
    3723              :             }
    3724       337473 :         }
    3725              :     }
    3726              :     // Collect vehicles from incoming edges from the junction representing the origin of 'edge'
    3727       337473 :     if (incomingEdgeCount > 0) {
    3728      1679997 :         for (const MSEdge* inEdge : junction->getIncoming()) {
    3729      1393791 :             if (inEdge->isInternal() || inEdge->isCrossing()) {
    3730       857038 :                 continue;
    3731              :             }
    3732              :             bool skip = false;
    3733      1381284 :             for (MSLane* const lane : inEdge->getLanes()) {
    3734       800433 :                 if (seenLanes.find(lane) != seenLanes.end()) {
    3735              :                     skip = true;
    3736              :                     break;
    3737              :                 }
    3738              :             }
    3739       596467 :             if (skip) {
    3740              : #ifdef DEBUG_SSM_SURROUNDING
    3741              :                 //if (gDebugFlag3) std::cout << "Scan skips already seen edge " << (*ei)->getID() << "\n";
    3742              : #endif
    3743        15616 :                 continue;
    3744              :             }
    3745              : 
    3746              :             // XXX the length may be wrong if there are parallel internal edges for different vClasses
    3747       580851 :             double distOnJunction = scanStart.edge->isInternal() ? 0. : inEdge->getInternalFollowingLengthTo(scanStart.edge, SVC_IGNORING);
    3748       580851 :             if (distOnJunction >= remainingRange) {
    3749              : #ifdef DEBUG_SSM_SURROUNDING
    3750              :                 //if (gDebugFlag3) std::cout << "Scan stops on junction (between " << inEdge->getID() << " and " << scanStart.edge->getID() << ") at rel. dist " << distOnJunction << "\n";
    3751              : #endif
    3752        44098 :                 continue;
    3753              :             }
    3754              :             // account for vehicles on the predecessor edge
    3755       536753 :             UpstreamScanStartInfo nextInfo(inEdge, inEdge->getLength(), remainingRange - distOnJunction, scanStart.egoDistToConflictLane, scanStart.egoConflictLane);
    3756       536753 :             getUpstreamVehicles(nextInfo, foeCollector, seenLanes, routeJunctions);
    3757              :         }
    3758              :     }
    3759              : }
    3760              : 
    3761              : 
    3762              : void
    3763       865620 : MSDevice_SSM::getVehiclesOnJunction(const MSJunction* junction, const MSLane* const egoJunctionLane, double egoDistToConflictLane, const MSLane* const egoConflictLane, FoeInfoMap& foeCollector, std::set<const MSLane*>& seenLanes) {
    3764              : #ifdef DEBUG_SSM_SURROUNDING
    3765              :     if (gDebugFlag3) {
    3766              :         std::cout << SIMTIME << " getVehiclesOnJunction() for junction '" << junction->getID()
    3767              :                   << "' egoJunctionLane=" << Named::getIDSecure(egoJunctionLane)
    3768              :                   << "\nFound vehicles:"
    3769              :                   << std::endl;
    3770              :     }
    3771              : #endif
    3772              :     // FoeInfo creation
    3773      1190780 :     auto collectFoeInfos = [&](const MSLane::VehCont & vehicles) {
    3774      1408429 :         for (MSVehicle* const veh : vehicles) {
    3775       435298 :             if (foeCollector.find(veh) != foeCollector.end()) {
    3776          400 :                 delete foeCollector[veh];
    3777              :             }
    3778       217649 :             FoeInfo* c = new FoeInfo();
    3779       217649 :             c->egoConflictLane = egoConflictLane;
    3780       217649 :             c->egoDistToConflictLane = egoDistToConflictLane;
    3781       217649 :             foeCollector[veh] = c;
    3782              : #ifdef DEBUG_SSM_SURROUNDING
    3783              :             if (gDebugFlag3) {
    3784              :                 std::cout << "\t" << veh->getID() << " egoConflictLane=" << Named::getIDSecure(egoConflictLane) << "\n";
    3785              :             }
    3786              : #endif
    3787              :         }
    3788       865620 :     };
    3789              : 
    3790              :     // stop condition
    3791       865620 :     if (seenLanes.find(egoJunctionLane) != seenLanes.end() || egoJunctionLane->getEdge().isCrossing()) {
    3792       133155 :         return;
    3793              :     }
    3794              : 
    3795      1146108 :     auto scanInternalLane = [&](const MSLane * lane) {
    3796      1146108 :         const MSLane::VehCont& vehicles = lane->getVehiclesSecure();
    3797              : 
    3798              :         // Add FoeInfos (XXX: for some situations, a vehicle may be collected twice. Then the later finding overwrites the earlier in foeCollector.
    3799              :         // This could lead to neglecting a conflict when determining foeConflictLane later.) -> TODO: test with twice intersecting routes
    3800      1146108 :         collectFoeInfos(vehicles);
    3801              : 
    3802      1146108 :         lane->releaseVehicles();
    3803              : 
    3804              :         // check additional internal link upstream in the same junction
    3805              :         // TODO: getEntryLink returns nullptr
    3806      1146108 :         if (lane->getCanonicalPredecessorLane()->isInternal()) {
    3807        44672 :             lane = lane->getCanonicalPredecessorLane();
    3808              : 
    3809              :             // This code must be modified, if more than two-piece internal lanes are allowed. Thus, assert:
    3810              :             assert(!lane->getEntryLink()->fromInternalLane());
    3811              : 
    3812              :             // collect vehicles
    3813        44672 :             const MSLane::VehCont& vehicles2 = lane->getVehiclesSecure();
    3814              :             // Add FoeInfos for the first internal lane
    3815        44672 :             collectFoeInfos(vehicles2);
    3816        44672 :             lane->releaseVehicles();
    3817              :         }
    3818              : 
    3819              : 
    3820              :         // If there is an internal continuation lane, also collect vehicles on that lane
    3821      1146108 :         if (lane->getLinkCont().size() > 1 && lane->getLinkCont()[0]->getViaLane() != nullptr) {
    3822              :             // There's a second internal lane of the connection
    3823              :             lane = lane->getLinkCont()[0]->getViaLane();
    3824              :             // This code must be modified, if more than two-piece internal lanes are allowed. Thus, assert:
    3825              :             assert(lane->getLinkCont().size() == 0 || lane->getLinkCont()[0]->getViaLane() == 0);
    3826              : 
    3827              :             // collect vehicles
    3828            0 :             const MSLane::VehCont& vehicles2 = lane->getVehiclesSecure();
    3829              :             // Add FoeInfos for the first internal lane
    3830            0 :             collectFoeInfos(vehicles2);
    3831            0 :             lane->releaseVehicles();
    3832              :         }
    3833              : 
    3834      1878573 :     };
    3835              : 
    3836              :     // Collect vehicles on conflicting lanes
    3837       732465 :     const MSLink* entryLink = egoJunctionLane->getEntryLink();
    3838       732465 :     if (entryLink->getFoeLanes().size() > 0) {
    3839              : 
    3840       274097 :         const std::vector<MSLane*> foeLanes = junction->getFoeInternalLanes(entryLink);
    3841       745510 :         for (MSLane* lane : foeLanes) {
    3842       471413 :             if (seenLanes.find(lane) != seenLanes.end()) {
    3843        57770 :                 continue;
    3844              :             }
    3845       413643 :             scanInternalLane(lane);
    3846              :             seenLanes.insert(lane);
    3847              :         }
    3848       274097 :     }
    3849       732465 :     scanInternalLane(egoJunctionLane);
    3850              : 
    3851              : #ifdef DEBUG_SSM_SURROUNDING
    3852              :     if (gDebugFlag3) {
    3853              :         std::cout << std::endl;
    3854              :     }
    3855              : #endif
    3856              : }
    3857              : 
    3858              : 
    3859              : 
    3860              : void
    3861         3828 : MSDevice_SSM::generateOutput(OutputDevice* /*tripinfoOut*/) const {
    3862              :     // This is called once at vehicle removal.
    3863              :     //       Also: flush myOutputFile? Or is this done automatically?
    3864              :     // myOutputFile->closeTag();
    3865         3828 : }
    3866              : 
    3867              : // ---------------------------------------------------------------------------
    3868              : // Static parameter load helpers
    3869              : // ---------------------------------------------------------------------------
    3870              : std::string
    3871         3868 : MSDevice_SSM::getOutputFilename(const SUMOVehicle& v, std::string deviceID) {
    3872         3868 :     OptionsCont& oc = OptionsCont::getOptions();
    3873         3868 :     std::string file = deviceID + ".xml";
    3874         7736 :     if (v.getParameter().hasParameter("device.ssm.file")) {
    3875              :         try {
    3876         1008 :             file = v.getParameter().getParameter("device.ssm.file", file);
    3877            0 :         } catch (...) {
    3878            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.measures'."), v.getParameter().getParameter("device.ssm.file", file));
    3879            0 :         }
    3880         7064 :     } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.file")) {
    3881              :         try {
    3882         6336 :             file = v.getVehicleType().getParameter().getParameter("device.ssm.file", file);
    3883            0 :         } catch (...) {
    3884            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.measures'."), v.getVehicleType().getParameter().getParameter("device.ssm.file", file));
    3885            0 :         }
    3886              :     } else {
    3887         4260 :         file = oc.getString("device.ssm.file") == "" ? file : oc.getString("device.ssm.file");
    3888         2840 :         if (oc.isDefault("device.ssm.file") && (myIssuedParameterWarnFlags & SSM_WARN_FILE) == 0) {
    3889          324 :             WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.file'. Using default of '%'."), v.getID(), file);
    3890          108 :             myIssuedParameterWarnFlags |= SSM_WARN_FILE;
    3891              :         }
    3892              :     }
    3893         7736 :     if (OptionsCont::getOptions().isSet("configuration-file")) {
    3894           48 :         file = FileHelpers::checkForRelativity(file, OptionsCont::getOptions().getString("configuration-file"));
    3895              :         try {
    3896           96 :             file = StringUtils::urlDecode(file);
    3897            0 :         } catch (NumberFormatException& e) {
    3898            0 :             WRITE_WARNING(toString(e.what()) + " when trying to decode filename '" + file + "'.");
    3899            0 :         }
    3900              :     }
    3901         3868 :     return file;
    3902              : }
    3903              : 
    3904              : bool
    3905         3868 : MSDevice_SSM::useGeoCoords(const SUMOVehicle& v) {
    3906         3868 :     OptionsCont& oc = OptionsCont::getOptions();
    3907         3868 :     bool useGeo = false;
    3908         7736 :     if (v.getParameter().hasParameter("device.ssm.geo")) {
    3909              :         try {
    3910           16 :             useGeo = StringUtils::toBool(v.getParameter().getParameter("device.ssm.geo", "no"));
    3911            0 :         } catch (...) {
    3912            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.geo'."), v.getParameter().getParameter("device.ssm.geo", "no"));
    3913            0 :         }
    3914         7720 :     } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.geo")) {
    3915              :         try {
    3916         4208 :             useGeo = StringUtils::toBool(v.getVehicleType().getParameter().getParameter("device.ssm.geo", "no"));
    3917            0 :         } catch (...) {
    3918            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.geo'."), v.getVehicleType().getParameter().getParameter("device.ssm.geo", "no"));
    3919            0 :         }
    3920              :     } else {
    3921         1756 :         useGeo = oc.getBool("device.ssm.geo");
    3922         3512 :         if (oc.isDefault("device.ssm.geo") && (myIssuedParameterWarnFlags & SSM_WARN_GEO) == 0) {
    3923         1329 :             WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.geo'. Using default of '%'."), v.getID(), toString(useGeo));
    3924          443 :             myIssuedParameterWarnFlags |= SSM_WARN_GEO;
    3925              :         }
    3926              :     }
    3927         3868 :     return useGeo;
    3928              : }
    3929              : 
    3930              : 
    3931              : bool
    3932         3868 : MSDevice_SSM::writePositions(const SUMOVehicle& v) {
    3933         3868 :     OptionsCont& oc = OptionsCont::getOptions();
    3934         3868 :     bool writePos = false;
    3935         7736 :     if (v.getParameter().hasParameter("device.ssm.write-positions")) {
    3936              :         try {
    3937            8 :             writePos = StringUtils::toBool(v.getParameter().getParameter("device.ssm.write-positions", "no"));
    3938            0 :         } catch (...) {
    3939            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.write-positions'."), v.getParameter().getParameter("device.ssm.write-positions", "no"));
    3940            0 :         }
    3941         7728 :     } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.write-positions")) {
    3942              :         try {
    3943            0 :             writePos = StringUtils::toBool(v.getVehicleType().getParameter().getParameter("device.ssm.write-positions", "no"));
    3944            0 :         } catch (...) {
    3945            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.write-positions'."), v.getVehicleType().getParameter().getParameter("device.ssm.write-positions", "no"));
    3946            0 :         }
    3947              :     } else {
    3948         3864 :         writePos = oc.getBool("device.ssm.write-positions");
    3949         7728 :         if (oc.isDefault("device.ssm.write-positions") && (myIssuedParameterWarnFlags & SSM_WARN_POS) == 0) {
    3950         1353 :             WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.write-positions'. Using default of '%'."), v.getID(), toString(writePos));
    3951          451 :             myIssuedParameterWarnFlags |= SSM_WARN_POS;
    3952              :         }
    3953              :     }
    3954         3868 :     return writePos;
    3955              : }
    3956              : 
    3957              : 
    3958              : bool
    3959         3868 : MSDevice_SSM::writeLanesPositions(const SUMOVehicle& v) {
    3960         3868 :     OptionsCont& oc = OptionsCont::getOptions();
    3961         3868 :     bool writeLanesPos = false;
    3962         7736 :     if (v.getParameter().hasParameter("device.ssm.write-lane-positions")) {
    3963              :         try {
    3964           16 :             writeLanesPos = StringUtils::toBool(v.getParameter().getParameter("device.ssm.write-lane-positions", "no"));
    3965            0 :         } catch (...) {
    3966            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.write-lane-positions'."), v.getParameter().getParameter("device.ssm.write-lane-positions", "no"));
    3967            0 :         }
    3968         7720 :     } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.write-lane-positions")) {
    3969              :         try {
    3970            0 :             writeLanesPos = StringUtils::toBool(v.getVehicleType().getParameter().getParameter("device.ssm.write-lane-positions", "no"));
    3971            0 :         } catch (...) {
    3972            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.write-lane-positions'."), v.getVehicleType().getParameter().getParameter("device.ssm.write-lane-positions", "no"));
    3973            0 :         }
    3974              :     } else {
    3975         3860 :         writeLanesPos = oc.getBool("device.ssm.write-lane-positions");
    3976         7720 :         if (oc.isDefault("device.ssm.write-lane-positions") && (myIssuedParameterWarnFlags & SSM_WARN_LANEPOS) == 0) {
    3977         1341 :             WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.write-positions'. Using default of '%'."), v.getID(), toString(writeLanesPos));
    3978          447 :             myIssuedParameterWarnFlags |= SSM_WARN_LANEPOS;
    3979              :         }
    3980              :     }
    3981         3868 :     return writeLanesPos;
    3982              : }
    3983              : 
    3984              : 
    3985              : bool
    3986         3868 : MSDevice_SSM::filterByConflictType(const SUMOVehicle& v, std::string deviceID, std::vector<int>& conflictTypes) {
    3987         3868 :     OptionsCont& oc = OptionsCont::getOptions();
    3988         3868 :     std::string typeString = "";
    3989         7736 :     if (v.getParameter().hasParameter("device.ssm.exclude-conflict-types")) {
    3990              :         try {
    3991            0 :             typeString = v.getParameter().getParameter("device.ssm.exclude-conflict-types", "");
    3992            0 :         } catch (...) {
    3993            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.conflict-order'."), v.getParameter().getParameter("device.ssm.exclude-conflict-types", ""));
    3994            0 :         }
    3995         7736 :     } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.exclude-conflict-types")) {
    3996              :         try {
    3997           16 :             typeString = v.getVehicleType().getParameter().getParameter("device.ssm.exclude-conflict-types", "");
    3998            0 :         } catch (...) {
    3999            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.conflict-order'."), v.getVehicleType().getParameter().getParameter("device.ssm.exclude-conflict-types", ""));
    4000            0 :         }
    4001              :     } else {
    4002         3860 :         typeString = oc.getString("device.ssm.exclude-conflict-types");
    4003         7720 :         if (oc.isDefault("device.ssm.exclude-conflict-types") && (myIssuedParameterWarnFlags & SSM_WARN_CONFLICTFILTER) == 0) {
    4004         1353 :             WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.exclude-conflict-types'. Using default of '%'."), v.getID(), typeString);
    4005          451 :             myIssuedParameterWarnFlags |= SSM_WARN_CONFLICTFILTER;
    4006              :         }
    4007              :     }
    4008              :     // Check retrieved conflict keys
    4009         7736 :     StringTokenizer st = StringTokenizer("");
    4010        11604 :     st = (typeString.find(",") != std::string::npos) ? StringTokenizer(typeString, ",") : StringTokenizer(typeString);
    4011         3868 :     std::vector<std::string> found = st.getVector();
    4012              :     std::set<int> confirmed;
    4013         3876 :     for (std::vector<std::string>::const_iterator i = found.begin(); i != found.end(); ++i) {
    4014            8 :         if (*i == "foe") {
    4015            0 :             confirmed.insert(FOE_ENCOUNTERTYPES.begin(), FOE_ENCOUNTERTYPES.end());
    4016            8 :         } else if (*i == "ego") {
    4017            8 :             confirmed.insert(EGO_ENCOUNTERTYPES.begin(), EGO_ENCOUNTERTYPES.end());
    4018            0 :         } else if (encounterToString(static_cast<EncounterType>(std::stoi(*i))) != "UNKNOWN") {
    4019            0 :             confirmed.insert(std::stoi(*i));
    4020              :         } else {
    4021              :             // Given identifier is unknown
    4022            0 :             WRITE_ERRORF(TL("SSM order filter '%' is not supported. Aborting construction of SSM device '%'."), *i, deviceID);
    4023              :             return false;
    4024              :         }
    4025              :     }
    4026         3868 :     conflictTypes.insert(conflictTypes.end(), confirmed.begin(), confirmed.end());
    4027         3868 :     return true;
    4028         3868 : }
    4029              : 
    4030              : 
    4031              : double
    4032         3868 : MSDevice_SSM::getDetectionRange(const SUMOVehicle& v) {
    4033         3868 :     OptionsCont& oc = OptionsCont::getOptions();
    4034         3868 :     double range = -INVALID_DOUBLE;
    4035         7736 :     if (v.getParameter().hasParameter("device.ssm.range")) {
    4036              :         try {
    4037          688 :             range = StringUtils::toDouble(v.getParameter().getParameter("device.ssm.range", ""));
    4038            0 :         } catch (...) {
    4039            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.range'."), v.getParameter().getParameter("device.ssm.range", ""));
    4040            0 :         }
    4041         7048 :     } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.range")) {
    4042              :         try {
    4043         4208 :             range = StringUtils::toDouble(v.getVehicleType().getParameter().getParameter("device.ssm.range", ""));
    4044            0 :         } catch (...) {
    4045            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.range'."), v.getVehicleType().getParameter().getParameter("device.ssm.range", ""));
    4046            0 :         }
    4047              :     } else {
    4048         1420 :         range = oc.getFloat("device.ssm.range");
    4049         2840 :         if (oc.isDefault("device.ssm.range") && (myIssuedParameterWarnFlags & SSM_WARN_RANGE) == 0) {
    4050          405 :             WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.range'. Using default of '%'."), v.getID(), toString(range));
    4051          135 :             myIssuedParameterWarnFlags |= SSM_WARN_RANGE;
    4052              :         }
    4053              :     }
    4054         3868 :     return range;
    4055              : }
    4056              : 
    4057              : 
    4058              : double
    4059         3868 : MSDevice_SSM::getMDRAC_PRT(const SUMOVehicle& v) {
    4060         3868 :     OptionsCont& oc = OptionsCont::getOptions();
    4061         3868 :     double prt = 1;
    4062         7736 :     if (v.getParameter().hasParameter("device.ssm.mdrac.prt")) {
    4063              :         try {
    4064            8 :             prt = StringUtils::toDouble(v.getParameter().getParameter("device.ssm.mdrac.prt", ""));
    4065            0 :         } catch (...) {
    4066            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.mdrac.prt'."), v.getParameter().getParameter("device.ssm.mdrac.prt", ""));
    4067            0 :         }
    4068         7728 :     } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.mdrac.prt")) {
    4069              :         try {
    4070            0 :             prt = StringUtils::toDouble(v.getVehicleType().getParameter().getParameter("device.ssm.mdrac.prt", ""));
    4071            0 :         } catch (...) {
    4072            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.mdrac.prt'."), v.getVehicleType().getParameter().getParameter("device.ssm.mdrac.prt", ""));
    4073            0 :         }
    4074              :     } else {
    4075         3864 :         prt = oc.getFloat("device.ssm.mdrac.prt");
    4076         7728 :         if (oc.isDefault("device.ssm.mdrac.prt") && (myIssuedParameterWarnFlags & SSM_WARN_MDRAC_PRT) == 0) {
    4077          960 :             WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.mdrac.prt'. Using default of '%'."), v.getID(), toString(prt));
    4078          320 :             myIssuedParameterWarnFlags |= SSM_WARN_MDRAC_PRT;
    4079              :         }
    4080              :     }
    4081         3868 :     return prt;
    4082              : }
    4083              : 
    4084              : 
    4085              : 
    4086              : 
    4087              : double
    4088         3868 : MSDevice_SSM::getExtraTime(const SUMOVehicle& v) {
    4089         3868 :     OptionsCont& oc = OptionsCont::getOptions();
    4090         3868 :     double extraTime = INVALID_DOUBLE;
    4091         7736 :     if (v.getParameter().hasParameter("device.ssm.extratime")) {
    4092              :         try {
    4093           16 :             extraTime = StringUtils::toDouble(v.getParameter().getParameter("device.ssm.extratime", ""));
    4094            0 :         } catch (...) {
    4095            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.extratime'."), v.getParameter().getParameter("device.ssm.extratime", ""));
    4096            0 :         }
    4097         7720 :     } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.extratime")) {
    4098              :         try {
    4099         4208 :             extraTime = StringUtils::toDouble(v.getVehicleType().getParameter().getParameter("device.ssm.extratime", ""));
    4100            0 :         } catch (...) {
    4101            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.extratime'."), v.getVehicleType().getParameter().getParameter("device.ssm.extratime", ""));
    4102            0 :         }
    4103              :     } else {
    4104         1756 :         extraTime = oc.getFloat("device.ssm.extratime");
    4105         3512 :         if (oc.isDefault("device.ssm.extratime") && (myIssuedParameterWarnFlags & SSM_WARN_EXTRATIME) == 0) {
    4106         1329 :             WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.extratime'. Using default of '%'."), v.getID(), toString(extraTime));
    4107          443 :             myIssuedParameterWarnFlags |= SSM_WARN_EXTRATIME;
    4108              :         }
    4109              :     }
    4110         3868 :     if (extraTime < 0.) {
    4111            0 :         extraTime = DEFAULT_EXTRA_TIME;
    4112            0 :         WRITE_WARNINGF(TL("Negative (or no) value encountered for vehicle parameter 'device.ssm.extratime' in vehicle '%' using default value % instead."), v.getID(), ::toString(extraTime));
    4113              :     }
    4114         3868 :     return extraTime;
    4115              : }
    4116              : 
    4117              : 
    4118              : bool
    4119         3868 : MSDevice_SSM::requestsTrajectories(const SUMOVehicle& v) {
    4120         3868 :     OptionsCont& oc = OptionsCont::getOptions();
    4121         3868 :     bool trajectories = false;
    4122         7736 :     if (v.getParameter().hasParameter("device.ssm.trajectories")) {
    4123              :         try {
    4124          704 :             trajectories = StringUtils::toBool(v.getParameter().getParameter("device.ssm.trajectories", "no"));
    4125            0 :         } catch (...) {
    4126            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.trajectories'."), v.getParameter().getParameter("device.ssm.trajectories", "no"));
    4127            0 :         }
    4128         7032 :     } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.trajectories")) {
    4129              :         try {
    4130         4208 :             trajectories = StringUtils::toBool(v.getVehicleType().getParameter().getParameter("device.ssm.trajectories", "no"));
    4131            0 :         } catch (...) {
    4132            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.trajectories'."), v.getVehicleType().getParameter().getParameter("device.ssm.trajectories", "no"));
    4133            0 :         }
    4134              :     } else {
    4135         1412 :         trajectories = oc.getBool("device.ssm.trajectories");
    4136         2824 :         if (oc.isDefault("device.ssm.trajectories") && (myIssuedParameterWarnFlags & SSM_WARN_TRAJECTORIES) == 0) {
    4137          405 :             WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.trajectories'. Using default of '%'."), v.getID(), toString(trajectories));
    4138          135 :             myIssuedParameterWarnFlags |= SSM_WARN_TRAJECTORIES;
    4139              :         }
    4140              :     }
    4141         3868 :     return trajectories;
    4142              : }
    4143              : 
    4144              : 
    4145              : bool
    4146         3876 : MSDevice_SSM::getMeasuresAndThresholds(const SUMOVehicle& v, std::string deviceID, std::map<std::string, double>& thresholds) {
    4147         3876 :     OptionsCont& oc = OptionsCont::getOptions();
    4148              : 
    4149              :     // Measures
    4150         3876 :     std::string measures_str = "";
    4151         7752 :     if (v.getParameter().hasParameter("device.ssm.measures")) {
    4152              :         try {
    4153          808 :             measures_str = v.getParameter().getParameter("device.ssm.measures", "");
    4154            0 :         } catch (...) {
    4155            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.measures'."), v.getParameter().getParameter("device.ssm.measures", ""));
    4156            0 :         }
    4157         6944 :     } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.measures")) {
    4158              :         try {
    4159         4224 :             measures_str = v.getVehicleType().getParameter().getParameter("device.ssm.measures", "");
    4160            0 :         } catch (...) {
    4161            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.measures'."), v.getVehicleType().getParameter().getParameter("device.ssm.measures", ""));
    4162            0 :         }
    4163              :     } else {
    4164         1360 :         measures_str = oc.getString("device.ssm.measures");
    4165         2720 :         if (oc.isDefault("device.ssm.measures") && (myIssuedParameterWarnFlags & SSM_WARN_MEASURES) == 0) {
    4166          156 :             WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.measures'. Using default of '%'."), v.getID(), measures_str);
    4167           52 :             myIssuedParameterWarnFlags |= SSM_WARN_MEASURES;
    4168              :         }
    4169              :     }
    4170              : 
    4171              :     // Check retrieved measures
    4172         3876 :     if (measures_str == "") {
    4173          192 :         WRITE_WARNINGF("No measures specified for ssm device of vehicle '%'. Registering all available SSMs.", v.getID());
    4174              :         measures_str = AVAILABLE_SSMS;
    4175              :     }
    4176         3876 :     StringTokenizer st = StringTokenizer(AVAILABLE_SSMS);
    4177         3876 :     std::vector<std::string> available = st.getVector();
    4178        11628 :     st = (measures_str.find(",") != std::string::npos) ? StringTokenizer(measures_str, ",") : StringTokenizer(measures_str);
    4179         3876 :     std::vector<std::string> measures = st.getVector();
    4180        14724 :     for (std::vector<std::string>::const_iterator i = measures.begin(); i != measures.end(); ++i) {
    4181        10848 :         if (std::find(available.begin(), available.end(), *i) == available.end()) {
    4182              :             // Given identifier is unknown
    4183            0 :             WRITE_ERRORF(TL("SSM identifier '%' is not supported. Aborting construction of SSM device '%'."), *i, deviceID);
    4184              :             return false;
    4185              :         }
    4186              :     }
    4187              : 
    4188              :     // Thresholds
    4189         3876 :     std::string thresholds_str = "";
    4190         7752 :     if (v.getParameter().hasParameter("device.ssm.thresholds")) {
    4191              :         try {
    4192          736 :             thresholds_str = v.getParameter().getParameter("device.ssm.thresholds", "");
    4193            0 :         } catch (...) {
    4194            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.thresholds'."), v.getParameter().getParameter("device.ssm.thresholds", ""));
    4195            0 :         }
    4196         7016 :     } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.thresholds")) {
    4197              :         try {
    4198         4208 :             thresholds_str = v.getVehicleType().getParameter().getParameter("device.ssm.thresholds", "");
    4199            0 :         } catch (...) {
    4200            0 :             WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.thresholds'."), v.getVehicleType().getParameter().getParameter("device.ssm.thresholds", ""));
    4201            0 :         }
    4202              :     } else {
    4203         1404 :         thresholds_str = oc.getString("device.ssm.thresholds");
    4204         2808 :         if (oc.isDefault("device.ssm.thresholds") && (myIssuedParameterWarnFlags & SSM_WARN_THRESHOLDS) == 0) {
    4205          300 :             WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.thresholds'. Using default of '%'."), v.getID(), thresholds_str);
    4206          100 :             myIssuedParameterWarnFlags |= SSM_WARN_THRESHOLDS;
    4207              :         }
    4208              :     }
    4209              : 
    4210              :     // Parse vector of doubles from threshold_str
    4211              :     int count = 0;
    4212         3876 :     if (thresholds_str != "") {
    4213         8280 :         st = (thresholds_str.find(",") != std::string::npos) ? StringTokenizer(thresholds_str, ",") : StringTokenizer(thresholds_str);
    4214        11896 :         while (count < (int)measures.size() && st.hasNext()) {
    4215         9136 :             double thresh = StringUtils::toDouble(st.next());
    4216         9136 :             thresholds.insert(std::make_pair(measures[count], thresh));
    4217         9136 :             ++count;
    4218              :         }
    4219         2760 :         if (thresholds.size() < measures.size() || st.hasNext()) {
    4220           24 :             WRITE_ERRORF(TL("Given list of thresholds ('%') is not of the same size as the list of measures ('%').\nPlease specify exactly one threshold for each measure."), thresholds_str, measures_str);
    4221            8 :             return false;
    4222              :         }
    4223              :     } else {
    4224              :         // assume default thresholds if none are given
    4225         2824 :         for (std::vector<std::string>::const_iterator i = measures.begin(); i != measures.end(); ++i) {
    4226         1708 :             if (*i == "TTC") {
    4227         1088 :                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_TTC));
    4228          620 :             } else if (*i == "DRAC") {
    4229           84 :                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_DRAC));
    4230          536 :             } else if (*i == "MDRAC") {
    4231           72 :                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_MDRAC));
    4232          464 :             } else if (*i == "PET") {
    4233          100 :                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_PET));
    4234          364 :             } else if (*i == "PPET") {
    4235           64 :                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_PPET));
    4236          300 :             } else if (*i == "BR") {
    4237          100 :                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_BR));
    4238          200 :             } else if (*i == "SGAP") {
    4239          100 :                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_SGAP));
    4240          100 :             } else if (*i == "TGAP") {
    4241          100 :                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_TGAP));
    4242              :             } else {
    4243            0 :                 WRITE_ERROR("Unknown SSM identifier '" + (*i) + "'. Aborting construction of ssm device."); // should never occur
    4244              :                 return false;
    4245              :             }
    4246              :         }
    4247              :     }
    4248              :     return true;
    4249         3876 : }
    4250              : 
    4251              : 
    4252              : std::string
    4253         2400 : MSDevice_SSM::getParameter(const std::string& key) const {
    4254         2400 :     if (key == "minTTC" && !myComputeTTC) {
    4255            0 :         throw InvalidArgument("Measure TTC is not tracked by ssm device");
    4256              :     }
    4257         2400 :     if (key == "maxDRAC" && !myComputeDRAC) {
    4258            0 :         throw InvalidArgument("Measure DRAC is not tracked by ssm device");
    4259              :     }
    4260         2400 :     if (key == "maxMDRAC" && !myComputeMDRAC) {
    4261            0 :         throw InvalidArgument("Measure MDRAC is not tracked by ssm device");
    4262              :     }
    4263         2400 :     if (key == "minPET" && !myComputePET) {
    4264            0 :         throw InvalidArgument("Measure PET is not tracked by ssm device");
    4265              :     }
    4266         2400 :     if (key == "minPPET" && !myComputePPET) {
    4267            0 :         throw InvalidArgument("Measure PPET is not tracked by ssm device");
    4268              :     }
    4269         1800 :     if (key == "minTTC" ||
    4270         1200 :             key == "maxDRAC" ||
    4271         1200 :             key == "maxMDRAC" ||
    4272         3000 :             key == "minPET" ||
    4273              :             key == "minPPET") {
    4274         2400 :         double value = INVALID_DOUBLE;
    4275              :         double minTTC = INVALID_DOUBLE;
    4276              :         double minPET = INVALID_DOUBLE;
    4277              :         double maxDRAC = -INVALID_DOUBLE;
    4278              :         double maxMDRAC = -INVALID_DOUBLE;
    4279              :         double minPPET = INVALID_DOUBLE;
    4280         4288 :         for (Encounter* e : myActiveEncounters) {
    4281         1888 :             minTTC = MIN2(minTTC, e->minTTC.value);
    4282         1888 :             minPET = MIN2(minPET, e->PET.value);
    4283         1888 :             maxDRAC = MAX2(maxDRAC, e->maxDRAC.value);
    4284         1888 :             maxMDRAC = MAX2(maxMDRAC, e->maxMDRAC.value);
    4285         1888 :             minPPET = MIN2(minPPET, e->minPPET.value);
    4286              :         }
    4287         2400 :         if (key == "minTTC") {
    4288          600 :             value = minTTC;
    4289         1800 :         } else if (key == "maxDRAC") {
    4290          600 :             value = maxDRAC;
    4291         1200 :         } else if (key == "maxMDRAC") {
    4292            0 :             value = maxMDRAC;
    4293         1200 :         } else if (key == "minPET") {
    4294          600 :             value = minPET;
    4295          600 :         } else if (key == "minPPET") {
    4296          600 :             value = minPPET;
    4297              :         }
    4298         2400 :         if (fabs(value) == INVALID_DOUBLE) {
    4299         1556 :             return "";
    4300              :         } else {
    4301          844 :             return toString(value);
    4302              :         }
    4303              :     }
    4304            0 :     throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'.");
    4305              : }
    4306              : 
    4307              : 
    4308              : void
    4309            0 : MSDevice_SSM::setParameter(const std::string& key, const std::string& value) {
    4310              :     double doubleValue;
    4311              :     try {
    4312            0 :         doubleValue = StringUtils::toDouble(value);
    4313            0 :     } catch (NumberFormatException&) {
    4314            0 :         throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'.");
    4315            0 :     }
    4316            0 :     if (false || key == "foo") {
    4317              :         UNUSED_PARAMETER(doubleValue);
    4318              :     } else {
    4319            0 :         throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'.");
    4320              :     }
    4321            0 : }
    4322              : 
    4323              : 
    4324              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1