LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSDriveWay.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 96.4 % 784 756
Test Date: 2024-11-22 15:46:21 Functions: 96.4 % 55 53

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-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    MSDriveWay.cpp
      15              : /// @author  Jakob Erdmann
      16              : /// @date    December 2021
      17              : ///
      18              : // A sequende of rail tracks (lanes) that may be used as a "set route" (Fahrstraße)
      19              : /****************************************************************************/
      20              : #include <config.h>
      21              : #include <cassert>
      22              : #include <utility>
      23              : 
      24              : #include <utils/xml/SUMOSAXAttributes.h>
      25              : #include <utils/common/StringUtils.h>
      26              : #include <microsim/MSStop.h>
      27              : #include <microsim/MSLane.h>
      28              : #include <microsim/MSEdge.h>
      29              : #include <microsim/MSLink.h>
      30              : #include <microsim/MSNet.h>
      31              : #include <microsim/MSVehicleControl.h>
      32              : #include <microsim/MSJunctionLogic.h>
      33              : #include <mesosim/MELoop.h>
      34              : #include "MSRailSignal.h"
      35              : #include "MSDriveWay.h"
      36              : #include "MSRailSignalControl.h"
      37              : 
      38              : // typical block length in germany on main lines is 3-5km on branch lines up to 7km
      39              : // special branches that are used by one train exclusively could also be up to 20km in length
      40              : // minimum block size in germany is 37.5m (LZB)
      41              : // larger countries (USA, Russia) might see blocks beyond 20km)
      42              : #define MAX_BLOCK_LENGTH 20000
      43              : #define MAX_SIGNAL_WARNINGS 10
      44              : 
      45              : #define DRIVEWAY_SANITY_CHECK
      46              : //#define SUBDRIVEWAY_WARN_NOCONFLICT
      47              : 
      48              : //#define DEBUG_BUILD_DRIVEWAY
      49              : //#define DEBUG_BUILD_SUBDRIVEWAY
      50              : //#define DEBUG_ADD_FOES
      51              : //#define DEBUG_BUILD_SIDINGS
      52              : //#define DEBUG_DRIVEWAY_BUILDROUTE
      53              : //#define DEBUG_CHECK_FLANKS
      54              : //#define DEBUG_SIGNALSTATE_PRIORITY
      55              : //#define DEBUG_SIGNALSTATE
      56              : //#define DEBUG_FIND_PROTECTION
      57              : //#define DEBUG_MOVEREMINDER
      58              : //#define DEBUG_MATCH
      59              : 
      60              : #define DEBUG_HELPER(obj) ((obj) != nullptr && (obj)->isSelected())
      61              : //#define DEBUG_HELPER(obj) ((obj)->getID() == "")
      62              : //#define DEBUG_HELPER(obj) (true)
      63              : 
      64              : //#define DEBUG_COND_DW (dw->myNumericalID == 5)
      65              : #define DEBUG_COND_DW (false)
      66              : 
      67              : // ===========================================================================
      68              : // static value definitions
      69              : // ===========================================================================
      70              : int MSDriveWay::myGlobalDriveWayIndex(0);
      71              : int MSDriveWay::myNumWarnings(0);
      72              : bool MSDriveWay::myWriteVehicles(false);
      73              : std::map<const MSLink*, std::vector<MSDriveWay*> > MSDriveWay::mySwitchDriveWays;
      74              : std::map<const MSEdge*, std::vector<MSDriveWay*> > MSDriveWay::myReversalDriveWays;
      75              : std::map<const MSEdge*, std::vector<MSDriveWay*>, ComparatorNumericalIdLess> MSDriveWay::myDepartureDriveways;
      76              : std::map<const MSJunction*, int> MSDriveWay::myDepartDrivewayIndex;
      77              : std::map<const MSEdge*, std::vector<MSDriveWay*> > MSDriveWay::myDepartureDrivewaysEnds;
      78              : std::map<const MSEdge*, std::vector<MSDriveWay*>, ComparatorNumericalIdLess> MSDriveWay::myEndingDriveways;
      79              : std::map<ConstMSEdgeVector, MSDriveWay*> MSDriveWay::myDriveWayRouteLookup;
      80              : std::map<std::string, MSDriveWay*> MSDriveWay::myDriveWayLookup;
      81              : 
      82              : // ---------------------------------------------------------------------------
      83              : // static initialisation methods
      84              : // ---------------------------------------------------------------------------
      85              : void
      86        42995 : MSDriveWay::init() {
      87        42995 :     myWriteVehicles = OptionsCont::getOptions().isSet("railsignal-vehicle-output");
      88        42995 : }
      89              : 
      90              : // ===========================================================================
      91              : // MSDriveWay method definitions
      92              : // ===========================================================================
      93              : 
      94              : 
      95        15661 : MSDriveWay::MSDriveWay(const MSLink* origin, const std::string& id, bool temporary) :
      96        31322 :     MSMoveReminder("DriveWay_" + (temporary ? "tmp" : id)),
      97              :     Named(id),
      98         7606 :     myNumericalID(temporary ? -1 : myGlobalDriveWayIndex++),
      99        15661 :     myOrigin(origin),
     100        15661 :     myMaxFlankLength(0),
     101        15661 :     myActive(nullptr),
     102        15661 :     myCoreSize(0),
     103        15661 :     myFoundSignal(false),
     104        15661 :     myFoundJump(false),
     105        15661 :     myTerminateRoute(false),
     106        15661 :     myAbortedBuild(false),
     107        15661 :     myBidiEnded(false),
     108        54589 :     myIsSubDriveway(false)
     109        15661 : {}
     110              : 
     111              : 
     112        23267 : MSDriveWay::~MSDriveWay() {
     113        17333 :     for (const MSDriveWay* sub : mySubDriveWays) {
     114         1672 :         delete sub;
     115              :     }
     116              :     mySubDriveWays.clear();
     117        70250 : }
     118              : 
     119              : void
     120        40275 : MSDriveWay::cleanup() {
     121        40275 :     myGlobalDriveWayIndex = 0;
     122        40275 :     myNumWarnings = 0;
     123        40275 :     myWriteVehicles = false;
     124              : 
     125        42763 :     for (auto item : myDepartureDriveways) {
     126         5079 :         for (MSDriveWay* dw : item.second) {
     127         2591 :             delete dw;
     128              :         }
     129              :     }
     130              :     MSDriveWay::mySwitchDriveWays.clear();
     131              :     MSDriveWay::myReversalDriveWays.clear();
     132              :     MSDriveWay::myDepartureDriveways.clear();
     133              :     MSDriveWay::myDepartDrivewayIndex.clear();
     134              :     MSDriveWay::myDepartureDrivewaysEnds.clear();
     135              :     MSDriveWay::myEndingDriveways.clear();
     136        40275 : }
     137              : 
     138              : void
     139          177 : MSDriveWay::clearState() {
     140          202 :     for (auto item : myEndingDriveways) {
     141           50 :         for (MSDriveWay* dw : item.second) {
     142              :             dw->myTrains.clear();
     143              :         }
     144              :     }
     145          177 : }
     146              : 
     147              : 
     148              : bool
     149        19165 : MSDriveWay::notifyEnter(SUMOTrafficObject& veh, Notification reason, const MSLane* enteredLane) {
     150              :     UNUSED_PARAMETER(reason);
     151              :     UNUSED_PARAMETER(enteredLane);
     152              : #ifdef DEBUG_MOVEREMINDER
     153              :     std::cout << SIMTIME << " notifyEnter " << getDescription() << " veh=" << veh.getID() << " lane=" << (MSGlobals::gUseMesoSim ? veh.getEdge()->getID() : enteredLane->getID()) << " reason=" << reason << "\n";
     154              : #endif
     155        38330 :     if (veh.isVehicle() && (enteredLane == myLane || (MSGlobals::gUseMesoSim && veh.getEdge() == &myLane->getEdge()))
     156        38330 :             && (reason == NOTIFICATION_DEPARTED || reason == NOTIFICATION_JUNCTION || reason == NOTIFICATION_PARKING)) {
     157        19110 :         SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     158        19110 :         MSRouteIterator firstIt = std::find(sveh.getCurrentRouteEdge(), sveh.getRoute().end(), myLane->getNextNormal());
     159        19104 :         if (myTrains.count(&sveh) == 0 && match(firstIt, sveh.getRoute().end())) {
     160        16338 :             myTrains.insert(&sveh);
     161        16338 :             if (myOrigin != nullptr) {
     162        10986 :                 MSRailSignalControl::getInstance().notifyApproach(myOrigin);
     163              :             }
     164        43564 :             for (const MSDriveWay* foe : myFoes) {
     165        27226 :                 if (foe->myOrigin != nullptr) {
     166        19663 :                     MSRailSignalControl::getInstance().notifyApproach(foe->myOrigin);
     167              :                 }
     168              :             }
     169        16338 :             if (myWriteVehicles) {
     170         1390 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, true, veh.getID(), reason));
     171              :             }
     172        16338 :             return true;
     173              :         }
     174              :     }
     175              :     return false;
     176              : }
     177              : 
     178              : 
     179              : bool
     180        78215 : MSDriveWay::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/, Notification reason, const MSLane* enteredLane) {
     181              :     UNUSED_PARAMETER(reason);
     182              :     UNUSED_PARAMETER(enteredLane);
     183              : #ifdef DEBUG_MOVEREMINDER
     184              :     std::cout << SIMTIME << " notifyLeave " << getDescription() << " veh=" << veh.getID() << " lane=" << Named::getIDSecure(enteredLane) << " reason=" << toString(reason) << "\n";
     185              : #endif
     186        78215 :     if (veh.isVehicle()) {
     187              :         // leaving network with departure, teleport etc
     188        78215 :         if (reason != MSMoveReminder::NOTIFICATION_JUNCTION && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     189         5128 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     190         5128 :             if (myWriteVehicles) {
     191          372 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     192              :             }
     193         5128 :             return false;
     194        73087 :         } else if (MSGlobals::gUseMesoSim && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     195              :             // notifyLeave is called before moving the route iterator
     196         6010 :             const MSLane* leftLane = (*(dynamic_cast<SUMOVehicle&>(veh).getCurrentRouteEdge()))->getLanes().front();
     197         6010 :             return notifyLeaveBack(veh, reason, leftLane);
     198              :         } else {
     199              :             return true;
     200              :         }
     201              :     } else {
     202              :         return false;
     203              :     }
     204              : }
     205              : 
     206              : 
     207              : bool
     208        59531 : MSDriveWay::notifyLeaveBack(SUMOTrafficObject& veh, Notification reason, const MSLane* leftLane) {
     209              :     UNUSED_PARAMETER(reason);
     210              :     UNUSED_PARAMETER(leftLane);
     211              : #ifdef DEBUG_MOVEREMINDER
     212              :     std::cout << SIMTIME << " notifyLeaveBack " << getDescription() << " veh=" << veh.getID() << " lane=" << Named::getIDSecure(leftLane) << " reason=" << toString(reason) << "\n";
     213              : #endif
     214        59531 :     if (veh.isVehicle()) {
     215        59531 :         if (leftLane == myForward.back() && (veh.getBackLane() != leftLane->getBidiLane() || MSGlobals::gUseMesoSim)) {
     216        11332 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     217        11332 :             if (myWriteVehicles) {
     218         1018 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     219              :             }
     220        11332 :             return false;
     221              :         } else {
     222        48199 :             return true;
     223              :         }
     224              :     } else {
     225              :         return false;
     226              :     }
     227              : }
     228              : 
     229              : 
     230              : bool
     231       447371 : MSDriveWay::reserve(const Approaching& closest, MSEdgeVector& occupied) {
     232       447371 :     if (foeDriveWayOccupied(true, closest.first, occupied)) {
     233              :         return false;
     234              :     }
     235       398225 :     for (MSLink* foeLink : myConflictLinks) {
     236       114925 :         if (hasLinkConflict(closest, foeLink)) {
     237              : #ifdef DEBUG_SIGNALSTATE
     238              :             if (gDebugFlag4 || DEBUG_HELPER(closest.first)) {
     239              :                 std::cout << getID() << " linkConflict with " << getTLLinkID(foeLink) << "\n";
     240              :             }
     241              : #endif
     242              :             return false;
     243              :         }
     244              :     }
     245       283300 :     myActive = closest.first;
     246       283300 :     return true;
     247              : }
     248              : 
     249              : 
     250              : bool
     251       114925 : MSDriveWay::hasLinkConflict(const Approaching& veh, const MSLink* foeLink) const {
     252              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     253              :     if (gDebugFlag4) {
     254              :         std::cout << "   checkLinkConflict foeLink=" << getTLLinkID(foeLink) << " ego=" << Named::getIDSecure(veh.first) << "\n";
     255              :     }
     256              : #endif
     257       114925 :     if (foeLink->getApproaching().size() > 0) {
     258        38612 :         Approaching foe = foeLink->getClosest();
     259              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     260              :         if (gDebugFlag4) {
     261              :             std::cout << "     approaching foe=" << foe.first->getID() << "\n";
     262              :         }
     263              : #endif
     264        38612 :         if (foe.first == veh.first) {
     265        38612 :             return false;
     266              :         }
     267              :         const MSTrafficLightLogic* foeTLL = foeLink->getTLLogic();
     268              :         assert(foeTLL != nullptr);
     269        34305 :         const MSRailSignal* constFoeRS = dynamic_cast<const MSRailSignal*>(foeTLL);
     270              :         MSRailSignal* foeRS = const_cast<MSRailSignal*>(constFoeRS);
     271        34305 :         if (foeRS != nullptr) {
     272        34305 :             const MSDriveWay& foeDriveWay = foeRS->retrieveDriveWayForVeh(foeLink->getTLIndex(), foe.first);
     273              :             MSEdgeVector occupied;
     274        53858 :             if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied) ||
     275        32148 :                     !foeRS->constraintsAllow(foe.first) ||
     276        24620 :                     !overlap(foeDriveWay) ||
     277        46330 :                     !isFoeOrSubFoe(&foeDriveWay) ||
     278         9789 :                     canUseSiding(veh.first, &foeDriveWay).first) {
     279              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     280              :                 if (gDebugFlag4) {
     281              :                     if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied)) {
     282              :                         std::cout << "     foe blocked\n";
     283              :                     } else if (!foeRS->constraintsAllow(foe.first)) {
     284              :                         std::cout << "     foe constrained\n";
     285              :                     } else if (!overlap(foeDriveWay)) {
     286              :                         std::cout << "     no overlap\n";
     287              :                     } else if (!isFoeOrSubFoe(&foeDriveWay)) {
     288              :                         std::cout << "     foeDW=" << foeDriveWay.getID() << " is not a foe to " << getID() << "\n";
     289              :                     } else if (canUseSiding(veh.first, &foeDriveWay).first) {
     290              :                         std::cout << "     use siding\n";
     291              :                     }
     292              :                 }
     293              : #endif
     294        24607 :                 return false;
     295              :             }
     296              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     297              :             if (gDebugFlag4) {
     298              :                 std::cout
     299              :                         << "  aSB=" << veh.second.arrivalSpeedBraking << " foeASB=" << foe.second.arrivalSpeedBraking
     300              :                         << "  aT=" << veh.second.arrivalTime << " foeAT=" << foe.second.arrivalTime
     301              :                         << "  aS=" << veh.first->getSpeed() << " foeS=" << foe.first->getSpeed()
     302              :                         << "  aD=" << veh.second.dist << " foeD=" << foe.second.dist
     303              :                         << "  aW=" << veh.first->getWaitingTime() << " foeW=" << foe.first->getWaitingTime()
     304              :                         << "  aN=" << veh.first->getNumericalID() << " foeN=" << foe.first->getNumericalID()
     305              :                         << "\n";
     306              :             }
     307              : #endif
     308         9698 :             const bool yield = mustYield(veh, foe);
     309         9698 :             if (MSRailSignal::storeVehicles()) {
     310          540 :                 MSRailSignal::rivalVehicles().push_back(foe.first);
     311          540 :                 if (yield) {
     312          315 :                     MSRailSignal::priorityVehicles().push_back(foe.first);
     313              :                 }
     314              :             }
     315         9698 :             return yield;
     316        34305 :         }
     317              :     }
     318              :     return false;
     319              : }
     320              : 
     321              : 
     322              : bool
     323        21071 : MSDriveWay::isFoeOrSubFoe(const MSDriveWay* foe) const {
     324        21071 :     if (std::find(myFoes.begin(), myFoes.end(), foe) != myFoes.end()) {
     325              :         return true;
     326              :     }
     327        15083 :     for (const MSDriveWay* sub : foe->mySubDriveWays) {
     328         9046 :         if (isFoeOrSubFoe(sub)) {
     329              :             return true;
     330              :         }
     331              :     }
     332              :     return false;
     333              : }
     334              : 
     335              : 
     336              : bool
     337         9698 : MSDriveWay::mustYield(const Approaching& veh, const Approaching& foe) {
     338         9698 :     if (foe.second.arrivalSpeedBraking == veh.second.arrivalSpeedBraking) {
     339         5428 :         if (foe.second.arrivalTime == veh.second.arrivalTime) {
     340          540 :             if (foe.first->getSpeed() == veh.first->getSpeed()) {
     341          522 :                 if (foe.second.dist == veh.second.dist) {
     342          490 :                     if (foe.first->getWaitingTime() == veh.first->getWaitingTime()) {
     343          472 :                         return foe.first->getNumericalID() < veh.first->getNumericalID();
     344              :                     } else {
     345           18 :                         return foe.first->getWaitingTime() > veh.first->getWaitingTime();
     346              :                     }
     347              :                 } else {
     348           32 :                     return foe.second.dist < veh.second.dist;
     349              :                 }
     350              :             } else {
     351           18 :                 return foe.first->getSpeed() > veh.first->getSpeed();
     352              :             }
     353              :         } else {
     354         4888 :             return foe.second.arrivalTime < veh.second.arrivalTime;
     355              :         }
     356              :     } else {
     357         4270 :         return foe.second.arrivalSpeedBraking > veh.second.arrivalSpeedBraking;
     358              :     }
     359              : }
     360              : 
     361              : 
     362              : bool
     363        16035 : MSDriveWay::conflictLaneOccupied(bool store, const SUMOVehicle* ego) const {
     364       204411 :     for (const MSLane* lane : myConflictLanes) {
     365              :         if (!lane->isEmpty()) {
     366         4410 :             std::string joinVehicle = "";
     367         4410 :             if (ego != nullptr && !MSGlobals::gUseMesoSim) {
     368            0 :                 const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
     369            0 :                 if (stop != nullptr) {
     370            0 :                     joinVehicle = stop->join;
     371              :                 }
     372              :             }
     373              : #ifdef DEBUG_SIGNALSTATE
     374              :             if (gDebugFlag4) {
     375              :                 std::cout << SIMTIME << " conflictLane " << lane->getID() << " occupied ego=" << Named::getIDSecure(ego) << " vehNumber=" << lane->getVehicleNumber() << "\n";
     376              :                 if (joinVehicle != "") {
     377              :                     std::cout << "  joinVehicle=" << joinVehicle << " occupant=" << toString(lane->getVehiclesSecure()) << "\n";
     378              :                     lane->releaseVehicles();
     379              :                 }
     380              :             }
     381              : #endif
     382         4410 :             if (lane->getVehicleNumberWithPartials() == 1) {
     383         4410 :                 MSVehicle* foe = lane->getLastAnyVehicle();
     384         4410 :                 if (joinVehicle != "") {
     385            0 :                     if (foe->getID() == joinVehicle && foe->isStopped()) {
     386              : #ifdef DEBUG_SIGNALSTATE
     387              :                         if (gDebugFlag4) {
     388              :                             std::cout << "    ignore join-target '" << joinVehicle << "\n";
     389              :                         }
     390              : #endif
     391            0 :                         continue;
     392              :                     }
     393              :                 }
     394         4410 :                 if (ego != nullptr) {
     395            0 :                     if (foe == ego && std::find(myForward.begin(), myForward.end(), lane) == myForward.end()) {
     396              : #ifdef DEBUG_SIGNALSTATE
     397              :                         if (gDebugFlag4) {
     398              :                             std::cout << "    ignore ego as oncoming '" << ego->getID() << "\n";
     399              :                         }
     400              : #endif
     401            0 :                         continue;
     402              :                     }
     403            0 :                     if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
     404              : #ifdef DEBUG_SIGNALSTATE
     405              :                         if (gDebugFlag4) {
     406              :                             std::cout << "    ignore " << foe->getID() << " for which ego is join-target\n";
     407              :                         }
     408              : #endif
     409            0 :                         continue;
     410              :                     }
     411              :                 }
     412              :             }
     413         4410 :             if (MSRailSignal::storeVehicles() && store) {
     414         4410 :                 MSRailSignal::blockingVehicles().push_back(lane->getLastAnyVehicle());
     415              :             }
     416              :             return true;
     417              :         }
     418              :     }
     419        11625 :     return false;
     420              : }
     421              : 
     422              : 
     423              : bool
     424      7782671 : MSDriveWay::foeDriveWayApproached() const {
     425     15642969 :     for (const MSDriveWay* foeDW : myFoes) {
     426     15584675 :         if (foeDW->myOrigin != nullptr && foeDW->myOrigin->getApproaching().size() > 0) {
     427              : #ifdef DEBUG_SIGNALSTATE
     428              :             if (gDebugFlag4) {
     429              :                 std::cout << SIMTIME << " foeLink=" << foeDW->myOrigin->getDescription() << " approachedBy=" << foeDW->myOrigin->getApproaching().begin()->first->getID() << "\n";
     430              :             }
     431              : #endif
     432              :             return true;
     433              :         }
     434              :     }
     435              :     return false;
     436              : }
     437              : 
     438              : 
     439              : bool
     440      8714008 : MSDriveWay::foeDriveWayOccupied(bool store, const SUMOVehicle* ego, MSEdgeVector& occupied) const {
     441     25375418 :     for (const MSDriveWay* foeDW : myFoes) {
     442     17290485 :         if (!foeDW->myTrains.empty()) {
     443              : #ifdef DEBUG_SIGNALSTATE
     444              :             if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
     445              :                 std::cout << SIMTIME << " " << getID() << " foeDriveWay " << foeDW->getID() << " occupied ego=" << Named::getIDSecure(ego) << " foeVeh=" << toString(foeDW->myTrains) << "\n";
     446              :             }
     447              : #endif
     448       651060 :             if (foeDW->myTrains.size() == 1) {
     449       650814 :                 SUMOVehicle* foe = *foeDW->myTrains.begin();
     450       650814 :                 if (foe == ego) {
     451              : #ifdef DEBUG_SIGNALSTATE
     452              :                     if (gDebugFlag4 || DEBUG_HELPER(ego)) {
     453              :                         std::cout << "    ignore ego as foe '" << Named::getIDSecure(ego) << "\n";
     454              :                     }
     455              : #endif
     456        22085 :                     continue;
     457              :                 }
     458       631942 :                 if (hasJoin(ego, foe)) {
     459           36 :                     continue;
     460              :                 }
     461              :             }
     462       632152 :             std::pair<bool, const MSDriveWay*> useSiding = canUseSiding(ego, foeDW);
     463              : #ifdef DEBUG_SIGNALSTATE
     464              :             if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
     465              :                 auto it = mySidings.find(foeDW);
     466              :                 int numSidings = 0;
     467              :                 if (it != mySidings.end()) {
     468              :                     numSidings = it->second.size();
     469              :                 }
     470              :                 std::cout << "  useSiding=" << useSiding.first << " sidingFoe=" << Named::getIDSecure(useSiding.second) << " numSidings=" << numSidings << "\n";
     471              :             }
     472              : #endif
     473       632152 :             if (useSiding.first) {
     474         3177 :                 continue;
     475              :             } else {
     476       628975 :                 if (MSRailSignal::storeVehicles() && store) {
     477          740 :                     for (SUMOVehicle* foe : foeDW->myTrains) {
     478          370 :                         MSRailSignal::blockingVehicles().push_back(foe);
     479              :                     }
     480          370 :                     MSRailSignal::blockingDriveWays().push_back(foeDW);
     481              :                 }
     482      1258428 :                 for (const SUMOVehicle* foe : foeDW->myTrains) {
     483       629453 :                     occupied.push_back(const_cast<MSEdge*>(foe->getEdge()));
     484       629453 :                     MSEdge* bidi = const_cast<MSEdge*>(foe->getEdge()->getBidiEdge());
     485       629453 :                     if (bidi != nullptr) {
     486       457619 :                         occupied.push_back(bidi);
     487              :                     }
     488              :                     /// @todo: if foe occupies more than one edge we should add all of them to the occupied vector
     489              :                 }
     490       211149 :                 if (ego != nullptr && MSGlobals::gTimeToTeleportRSDeadlock > 0
     491       797488 :                         && (ego->getWaitingTime() > ego->getVehicleType().getCarFollowModel().getStartupDelay() || !ego->isOnRoad())) {
     492              :                     // if there is an occupied siding, it becomes part of the waitRelation
     493        87432 :                     SUMOVehicle* foe = *(useSiding.second == nullptr ? foeDW : useSiding.second)->myTrains.begin();
     494        87432 :                     const MSRailSignal* rs = myOrigin != nullptr ? dynamic_cast<const MSRailSignal*>(myOrigin->getTLLogic()) : nullptr;
     495        87432 :                     MSRailSignalControl::getInstance().addWaitRelation(ego, rs, foe);
     496              :                 }
     497       628975 :                 return true;
     498              :             }
     499     16639425 :         } else if (foeDW != this && isDepartDriveway() && !foeDW->isDepartDriveway()) {
     500         8741 :             if (foeDW->myOrigin->getApproaching().size() > 0) {
     501         1416 :                 Approaching foeA = foeDW->myOrigin->getClosest();
     502         1416 :                 const SUMOVehicle* foe = foeA.first;
     503         1416 :                 if (foeA.second.dist < foe->getBrakeGap(true)) {
     504          134 :                     MSRouteIterator firstIt = std::find(foe->getCurrentRouteEdge(), foe->getRoute().end(), foeDW->myRoute.front());
     505          134 :                     if (firstIt != foe->getRoute().end()) {
     506          134 :                         if (foeDW->match(firstIt, foe->getRoute().end())) {
     507          100 :                             bool useSiding = canUseSiding(ego, foeDW).first;
     508              : #ifdef DEBUG_SIGNALSTATE
     509              :                             if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
     510              :                                 std::cout << SIMTIME << " " << getID() << " blocked by " << foeDW->getID() << " (approached by " << foe->getID() << ") useSiding=" << useSiding << "\n";
     511              :                             }
     512              : #endif
     513          100 :                             if (useSiding) {
     514              :                                 //std::cout << SIMTIME << " " << getID() << " ego=" << ego->getID() << " foeDW=" << foeDW->getID() << " myFoes=" << toString(myFoes) << "\n";
     515            0 :                                 continue;
     516              :                             } else {
     517          100 :                                 return true;
     518              :                             }
     519              :                         }
     520              :                     }
     521              :                 }
     522              :             }
     523              :         }
     524              :     }
     525      8085993 :     for (const std::set<const MSDriveWay*>& dlFoes : myDeadlocks) {
     526              :         bool allOccupied = true;
     527        10984 :         for (const MSDriveWay* dlFoe : dlFoes) {
     528         8292 :             if (dlFoe->myTrains.empty()) {
     529              :                 allOccupied = false;
     530              :                 //std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << "  deadlockCheck clear " << dlFoe->getID() << "\n";
     531              :                 break;
     532              :             }
     533              :         }
     534         3752 :         if (allOccupied) {
     535              : #ifdef DEBUG_SIGNALSTATE
     536              :             if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
     537              :                 std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " deadlockCheck " << joinNamedToString(dlFoes, " ") << "\n";
     538              :             }
     539              : #endif
     540         9148 :             for (const MSDriveWay* dlFoe : dlFoes) {
     541         6456 :                 MSRailSignal::blockingDriveWays().push_back(dlFoe);
     542              :             }
     543              :             return true;
     544              :         }
     545              :     }
     546              :     return false;
     547              : }
     548              : 
     549              : 
     550              : bool
     551       631942 : MSDriveWay::hasJoin(const SUMOVehicle* ego, const SUMOVehicle* foe) {
     552       631942 :     if (ego != nullptr && !MSGlobals::gUseMesoSim) {
     553       172690 :         std::string joinVehicle = "";
     554       172690 :         const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
     555       172690 :         if (stop != nullptr) {
     556        87278 :             joinVehicle = stop->join;
     557              :         }
     558       172690 :         if (joinVehicle == "" && !ego->hasDeparted() && ego->getStops().size() > 1) {
     559              :             // check one more stop
     560         5984 :             auto it = ego->getStops().begin();
     561              :             std::advance(it, 1);
     562         5984 :             joinVehicle = it->pars.join;
     563              :         }
     564       172690 :         if (joinVehicle != "") {
     565              : #ifdef DEBUG_SIGNALSTATE
     566              :             if (gDebugFlag4 || DEBUG_COND_DW) {
     567              :                 std::cout << "  joinVehicle=" << joinVehicle << "\n";
     568              :             }
     569              : #endif
     570          345 :             if (foe->getID() == joinVehicle && foe->isStopped()) {
     571              : #ifdef DEBUG_SIGNALSTATE
     572              :                 if (gDebugFlag4 || DEBUG_COND_DW) {
     573              :                     std::cout << "    ignore join-target '" << joinVehicle << "\n";
     574              :                 }
     575              : #endif
     576              :                 return true;
     577              :             }
     578              :         }
     579              : 
     580       172666 :         if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
     581              : #ifdef DEBUG_SIGNALSTATE
     582              :             if (gDebugFlag4 || DEBUG_COND_DW) {
     583              :                 std::cout << "    ignore " << foe->getID() << " for which ego is join-target\n";
     584              :             }
     585              : #endif
     586              :             return true;
     587              :         }
     588              :     }
     589              :     return false;
     590              : }
     591              : 
     592              : 
     593              : std::pair<bool, const MSDriveWay*>
     594       645167 : MSDriveWay::canUseSiding(const SUMOVehicle* ego, const MSDriveWay* foe, bool recurse) const {
     595              :     auto it = mySidings.find(foe);
     596       645167 :     if (it != mySidings.end()) {
     597        11726 :         for (auto siding : it->second) {
     598              :             // assume siding is usuable when computing state for unapproached signal (ego == nullptr)
     599        10934 :             if (ego == nullptr || siding.length >= ego->getLength()) {
     600              :                 // if the siding is already "reserved" by another vehicle we cannot use it here
     601         7583 :                 const MSEdge* sidingEnd = myRoute[siding.end];
     602        15272 :                 for (MSDriveWay* sidingApproach : myEndingDriveways[sidingEnd]) {
     603        11794 :                     if (!sidingApproach->myTrains.empty()) {
     604              :                         // possibly the foe vehicle can use the other part of the siding
     605         4315 :                         if (recurse) {
     606              :                             const SUMOVehicle* foeVeh = nullptr;
     607         3126 :                             if (!foe->myTrains.empty()) {
     608         3118 :                                 foeVeh = *foe->myTrains.begin();
     609            8 :                             } else if (foe->myOrigin != nullptr && foe->myOrigin->getApproaching().size() > 0) {
     610            8 :                                 foeVeh = foe->myOrigin->getClosest().first;
     611              :                             }
     612         3126 :                             if (foeVeh == nullptr) {
     613            0 :                                 WRITE_WARNINGF("Invalid call to canUseSiding dw=% foe=% ego=% time=%", getID(), foe->getID(), Named::getIDSecure(ego), time2string(SIMSTEP));
     614            0 :                                 continue;
     615              :                             }
     616         3126 :                             if (foe->canUseSiding(foeVeh, this, false).first) {
     617          210 :                                 continue;
     618              :                             }
     619              :                         }
     620              :                         // possibly the foe vehicle
     621              :                         // @todo: in principle it might still be possible to continue if vehicle that approaches the siding can safely leave the situation
     622              : #ifdef DEBUG_SIGNALSTATE
     623              :                         if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
     624              :                             std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
     625              :                                       << " foeVeh=" << toString(foe->myTrains)
     626              :                                       << " sidingEnd=" << sidingEnd->getID() << " sidingApproach=" << sidingApproach->getID() << " approaching=" << toString(sidingApproach->myTrains) << "\n";
     627              :                         }
     628              : #endif
     629         4105 :                         return std::make_pair(false, sidingApproach);
     630              :                     }
     631              :                 }
     632              :                 //std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
     633              :                 //    << " foeVeh=" << toString(foe->myTrains)
     634              :                 //    << " sidingEnd=" << sidingEnd->getID() << "usable\n";
     635         3478 :                 return std::make_pair(true, nullptr);
     636              :             }
     637              :         }
     638              :     }
     639       637584 :     return std::make_pair(false, nullptr);
     640              : }
     641              : 
     642              : bool
     643        15593 : MSDriveWay::overlap(const MSDriveWay& other) const {
     644        35021 :     for (int i = 0; i < myCoreSize; i++) {
     645       141814 :         for (int j = 0; j < other.myCoreSize; j++) {
     646       122386 :             const MSEdge* edge = myRoute[i];
     647       122386 :             const MSEdge* edge2 = other.myRoute[j];
     648              :             if (edge->getToJunction() == edge2->getToJunction()
     649       122386 :                     || edge->getToJunction() == edge2->getFromJunction()) {
     650              :                 // XXX might be rail_crossing with parallel tracks
     651              :                 return true;
     652              :             }
     653              :         }
     654              :     }
     655              :     return false;
     656              : }
     657              : 
     658              : 
     659              : bool
     660        19012 : MSDriveWay::flankConflict(const MSDriveWay& other) const {
     661        48132 :     for (const MSLane* lane : myForward) {
     662       182734 :         for (const MSLane* lane2 : other.myForward) {
     663       149095 :             if (lane == lane2) {
     664              :                 return true;
     665              :             }
     666              :         }
     667       283949 :         for (const MSLane* lane2 : other.myBidi) {
     668       252662 :             if (lane == lane2) {
     669         4403 :                 if (bidiBlockedBy(other)) {
     670              :                     // it's only a deadlock if both trains block symmetrically
     671              :                     return true;
     672              :                 }
     673              :             }
     674              :         }
     675       257845 :         for (const MSLane* lane2 : other.myBidiExtended) {
     676       228725 :             if (lane == lane2) {
     677         7583 :                 if (bidiBlockedBy(other)) {
     678              :                     // it's only a deadlock if both trains block symmetrically
     679              :                     return true;
     680              :                 }
     681              :             }
     682              :         }
     683              :     }
     684              :     return false;
     685              : }
     686              : 
     687              : 
     688              : bool
     689        12119 : MSDriveWay::crossingConflict(const MSDriveWay& other) const {
     690        34627 :     for (const MSLane* lane : myForward) {
     691       116307 :         for (const MSLane* lane2 : other.myForward) {
     692        93799 :             if (lane->isNormal() && lane2->isNormal() && lane->getEdge().getToJunction() == lane2->getEdge().getToJunction()) {
     693              :                 return true;
     694              :             }
     695              :         }
     696              :     }
     697              :     return false;
     698              : }
     699              : 
     700              : 
     701              : bool
     702        12258 : MSDriveWay::bidiBlockedBy(const MSDriveWay& other) const {
     703        85416 :     for (const MSLane* lane : myBidi) {
     704       302144 :         for (const MSLane* lane2 : other.myForward) {
     705       228986 :             if (lane == lane2) {
     706              :                 return true;
     707              :             }
     708              :         }
     709              :     }
     710        42023 :     for (const MSLane* lane : myBidiExtended) {
     711       145617 :         for (const MSLane* lane2 : other.myForward) {
     712       113092 :             if (lane == lane2) {
     713         1831 :                 if (overlap(other)) {
     714              :                     return true;
     715              :                 }
     716              :             }
     717              :         }
     718              :     }
     719              :     return false;
     720              : }
     721              : 
     722              : 
     723              : bool
     724         6566 : MSDriveWay::bidiBlockedByEnd(const MSDriveWay& other) const {
     725         6566 :     const MSLane* end = other.myForward.back();
     726        46019 :     for (const MSLane* lane : myBidi) {
     727        41958 :         if (lane == end) {
     728              :             return true;
     729              :         }
     730              :     }
     731        17592 :     for (const MSLane* lane : myBidiExtended) {
     732        14698 :         if (lane == end) {
     733         1167 :             if (overlap(other)) {
     734              :                 return true;
     735              :             }
     736              :         }
     737              :     }
     738              :     return false;
     739              : }
     740              : 
     741              : bool
     742         1012 : MSDriveWay::forwardRouteConflict(std::set<const MSEdge*> forward, const MSDriveWay& other, bool secondCheck) {
     743              :     int i = 0;
     744         4135 :     for (const MSEdge* edge2 : other.myRoute) {
     745         4057 :         if (i == other.myCoreSize) {
     746              :             return false;
     747              :         }
     748         4057 :         i++;
     749         4057 :         if (edge2 == myForward.front()->getNextNormal() && !secondCheck) {
     750              :             // foe should not pass from behind through our own forward section
     751              :             return false;
     752              :         }
     753              :         if (forward.count(edge2->getBidiEdge()) != 0) {
     754              :             return true;
     755              :         }
     756              :     }
     757              :     return false;
     758              : }
     759              : 
     760              : void
     761         5790 : MSDriveWay::writeBlocks(OutputDevice& od) const {
     762        10029 :     od.openTag(myIsSubDriveway ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
     763         5790 :     od.writeAttr(SUMO_ATTR_ID, myID);
     764         5790 :     od.writeAttr(SUMO_ATTR_VEHICLE, myFirstVehicle);
     765         5790 :     od.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
     766         5790 :     if (myCoreSize != (int)myRoute.size()) {
     767            0 :         od.writeAttr("core", myCoreSize);
     768              :     }
     769         5790 :     od.openTag("forward");
     770         5790 :     od.writeAttr(SUMO_ATTR_LANES, toString(myForward));
     771         5790 :     od.closeTag();
     772         5790 :     if (!myIsSubDriveway) {
     773         4239 :         od.openTag("bidi");
     774         8478 :         od.writeAttr(SUMO_ATTR_LANES, toString(myBidi));
     775         4239 :         if (myBidiExtended.size() > 0) {
     776         1027 :             od.lf();
     777         1027 :             od << "                   ";
     778         2054 :             od.writeAttr("deadlockCheck", toString(myBidiExtended));
     779              :         }
     780         4239 :         od.closeTag();
     781         4239 :         od.openTag("flank");
     782         4239 :         od.writeAttr(SUMO_ATTR_LANES, toString(myFlank));
     783         4239 :         od.closeTag();
     784              : 
     785         8478 :         od.openTag("conflictLinks");
     786              : 
     787              :         std::vector<std::string> signals;
     788         7156 :         for (MSLink* link : myConflictLinks) {
     789         5834 :             signals.push_back(getTLLinkID(link));
     790              :         }
     791         8478 :         od.writeAttr("signals", joinToStringSorting(signals, " "));
     792         8478 :         od.closeTag();
     793              : 
     794              :         std::vector<std::string> foes;
     795        15349 :         for (MSDriveWay* dw : myFoes) {
     796        11110 :             foes.push_back(dw->myID);
     797              :         }
     798         4239 :         if (foes.size() > 0) {
     799         4215 :             od.openTag("foes");
     800         8430 :             od.writeAttr("driveWays", joinToStringSorting(foes, " "));
     801         8430 :             od.closeTag();
     802              :         }
     803         4614 :         for (auto item : mySidings) {
     804          375 :             od.openTag("sidings");
     805          750 :             od.writeAttr("foe", item.first->getID());
     806          903 :             for (auto siding : item.second) {
     807          528 :                 od.openTag("siding");
     808          528 :                 od.writeAttr("start", myRoute[siding.start]->getID());
     809          528 :                 od.writeAttr("end", myRoute[siding.end]->getID());
     810          528 :                 od.writeAttr("length", siding.length);
     811         1056 :                 od.closeTag();
     812              :             }
     813          750 :             od.closeTag();
     814              :         }
     815         4320 :         for (auto item : myDeadlocks) {
     816           81 :             od.openTag("deadlock");
     817          162 :             od.writeAttr("foes", joinNamedToStringSorting(item, " "));
     818          162 :             od.closeTag();
     819              :         }
     820         4239 :     }
     821        11580 :     od.closeTag(); // driveWay
     822              : 
     823         7341 :     for (const MSDriveWay* sub : mySubDriveWays) {
     824         1551 :         sub->writeBlocks(od);
     825              :     }
     826              : #ifdef DRIVEWAY_SANITY_CHECK
     827         5790 :     std::set<MSDriveWay*> uFoes(myFoes.begin(), myFoes.end());
     828         5790 :     if (uFoes.size() != myFoes.size()) {
     829            0 :         WRITE_WARNINGF("Duplicate foes in driveway '%'", getID());
     830              : 
     831              :     }
     832              : #endif
     833         5790 : }
     834              : 
     835              : 
     836              : void
     837          550 : MSDriveWay::writeBlockVehicles(OutputDevice& od) const {
     838          974 :     od.openTag(myIsSubDriveway ? "subDriveWay" : "driveWay");
     839          550 :     od.writeAttr(SUMO_ATTR_ID, myID);
     840         1940 :     for (const VehicleEvent& ve : myVehicleEvents) {
     841         2085 :         od.openTag(ve.isEntry ? "entry" : "exit");
     842         1390 :         od.writeAttr(SUMO_ATTR_ID, ve.id);
     843         1390 :         od.writeAttr(SUMO_ATTR_TIME, time2string(ve.time));
     844         1390 :         od.writeAttr("reason", ve.reason);
     845         2780 :         od.closeTag(); // event
     846              :     }
     847         1100 :     od.closeTag(); // driveWay
     848              : 
     849          676 :     for (const MSDriveWay* sub : mySubDriveWays) {
     850          126 :         sub->writeBlockVehicles(od);
     851              :     }
     852          550 : }
     853              : 
     854              : 
     855              : void
     856         5934 : MSDriveWay::buildRoute(const MSLink* origin, double length,
     857              :                        MSRouteIterator next, MSRouteIterator end,
     858              :                        LaneVisitedMap& visited,
     859              :                        std::set<MSLink*>& flankSwitches) {
     860              :     bool seekForwardSignal = true;
     861              :     bool seekBidiSwitch = true;
     862              :     bool foundUnsafeSwitch = false;
     863         5934 :     MSLane* toLane = origin ? origin->getViaLaneOrLane() : (*next)->getLanes()[0];
     864        11868 :     const std::string warnID = origin ? "rail signal " + getClickableTLLinkID(origin) : "insertion lane '" + toLane->getID() + "'";
     865              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     866              :     gDebugFlag4 = DEBUG_COND_DW;
     867              :     if (gDebugFlag4) std::cout << "buildRoute origin=" << warnID << " vehRoute=" << toString(ConstMSEdgeVector(next, end))
     868              :                                    << " visited=" << formatVisitedMap(visited) << "\n";
     869              : #endif
     870        38842 :     while ((seekForwardSignal || seekBidiSwitch)) {
     871        37020 :         if (length > MAX_BLOCK_LENGTH) {
     872           56 :             if (myNumWarnings < MAX_SIGNAL_WARNINGS) {
     873          224 :                 WRITE_WARNING("Block after " + warnID +
     874              :                               " exceeds maximum length (stopped searching after edge '" + toLane->getEdge().getID() + "' (length=" + toString(length) + "m).");
     875              :             }
     876           56 :             myNumWarnings++;
     877           56 :             myAbortedBuild = true;
     878              :             // length exceeded
     879              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     880              :             if (gDebugFlag4) {
     881              :                 std::cout << " abort: length=" << length << "\n";
     882              :             }
     883              : #endif
     884         4112 :             return;
     885              :         }
     886              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     887              :         if (gDebugFlag4) {
     888              :             std::cout << "   toLane=" << toLane->getID() << " visited=" << formatVisitedMap(visited) << "\n";
     889              :         }
     890              : #endif
     891        36964 :         const MSEdge* current = &toLane->getEdge();
     892        36964 :         if (current->isNormal()) {
     893        24205 :             myRoute.push_back(current);
     894        24205 :             if (next != end) {
     895              :                 next++;
     896              :             }
     897              :         }
     898        36964 :         appendMapIndex(visited, toLane);
     899        36964 :         length += toLane->getLength();
     900        36964 :         MSLane* bidi = toLane->getBidiLane();
     901        36964 :         if (seekForwardSignal) {
     902        18180 :             if (!foundUnsafeSwitch) {
     903        18180 :                 myForward.push_back(toLane);
     904        18180 :                 if (myForward.size() == 1) {
     905         5934 :                     myLane = toLane;
     906         5934 :                     if (MSGlobals::gUseMesoSim) {
     907         1676 :                         MESegment* s = MSGlobals::gMesoNet->getSegmentForEdge(myLane->getEdge());
     908         1676 :                         s->addDetector(this, myLane->getIndex());
     909              :                     } else {
     910         4258 :                         toLane->addMoveReminder(this);
     911              :                     }
     912              :                 }
     913              :             }
     914        18784 :         } else if (bidi == nullptr) {
     915          729 :             if (toLane->isInternal() && toLane->getIncomingLanes().front().viaLink->isTurnaround()) {
     916              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     917              :                 if (gDebugFlag4) {
     918              :                     std::cout << "      continue bidiSearch beyond turnaround\n";
     919              :                 }
     920              : #endif
     921              :             } else {
     922              :                 seekBidiSwitch = false;
     923              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     924              :                 if (gDebugFlag4) {
     925              :                     std::cout << "      noBidi, abort search for bidiSwitch\n";
     926              :                 }
     927              : #endif
     928              :             }
     929              :         }
     930        36964 :         if (bidi != nullptr) {
     931        29394 :             if (foundUnsafeSwitch) {
     932         7873 :                 myBidiExtended.push_back(bidi);
     933              :             } else {
     934        21521 :                 myBidi.push_back(bidi);
     935              :             }
     936        29394 :             if (!seekForwardSignal) {
     937              :                 // look for switch that could protect from oncoming vehicles
     938        37473 :                 for (const auto& ili : bidi->getIncomingLanes()) {
     939        19418 :                     if (ili.viaLink->getDirection() == LinkDirection::TURN) {
     940         1429 :                         continue;
     941              :                     }
     942        38957 :                     for (const MSLink* const link : ili.lane->getLinkCont()) {
     943        20968 :                         if (link->getDirection() == LinkDirection::TURN) {
     944          948 :                             continue;
     945              :                         }
     946        20020 :                         if (link->getViaLaneOrLane() != bidi) {
     947         2031 :                             myCoreSize = (int)myRoute.size();
     948              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     949              :                             if (gDebugFlag4) {
     950              :                                 const MSEdge* const bidiNext = bidi->getNextNormal();
     951              :                                 std::cout << "      found unsafe switch " << ili.viaLink->getDescription() << " (used=" << bidiNext->getID() << ")\n";
     952              :                             }
     953              : #endif
     954              :                             // trains along our route beyond this switch might create deadlock
     955              :                             foundUnsafeSwitch = true;
     956              :                             // the switch itself must still be guarded to ensure safety
     957         4208 :                             for (const auto& ili2 : bidi->getIncomingLanes()) {
     958         2177 :                                 if (ili2.viaLink->getDirection() != LinkDirection::TURN) {
     959         2099 :                                     flankSwitches.insert(ili.viaLink);
     960              :                                 }
     961              :                             }
     962              :                         }
     963              :                     }
     964              :                 }
     965              :             }
     966              :         }
     967              :         const std::vector<MSLink*>& links = toLane->getLinkCont();
     968        36964 :         toLane = nullptr;
     969        40198 :         for (const MSLink* const link : links) {
     970        34761 :             if ((next != end && &link->getLane()->getEdge() == *next)
     971        69142 :                     && isRailway(link->getViaLaneOrLane()->getPermissions())) {
     972        54314 :                 toLane = link->getViaLaneOrLane();
     973        32908 :                 if (link->getTLLogic() != nullptr && link->getTLIndex() >= 0) {
     974         6768 :                     if (link == origin) {
     975            0 :                         WRITE_WARNINGF(TL("Found circular block after % (% edges, length %)"), warnID, toString(myRoute.size()), toString(length));
     976              :                         //std::cout << getClickableTLLinkID(origin) << " circularBlock2=" << toString(myRoute) << "\n";
     977            0 :                         myAbortedBuild = true;
     978              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     979              :                         if (gDebugFlag4) {
     980              :                             std::cout << " abort: found circle\n";
     981              :                         }
     982              : #endif
     983            0 :                         return;
     984              :                     }
     985              :                     seekForwardSignal = false;
     986         6768 :                     myFoundSignal = true;
     987         6768 :                     seekBidiSwitch = bidi != nullptr;
     988              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     989              :                     if (gDebugFlag4) {
     990              :                         std::cout << "      found forwardSignal " << link->getTLLogic()->getID() << " seekBidiSwitch=" << seekBidiSwitch << "\n";
     991              :                     }
     992              : #endif
     993              :                 }
     994              :                 //if (links.size() > 1 && !foundUnsafeSwitch) {
     995        32908 :                 if (isSwitch(link)) {
     996              :                     // switch on driveway
     997              :                     //std::cout << "mySwitchDriveWays " << getID() << " link=" << link->getDescription() << "\n";
     998        18836 :                     mySwitchDriveWays[link].push_back(this);
     999              :                 }
    1000        32908 :                 if (link->getLane()->getBidiLane() != nullptr && &link->getLane()->getEdge() == current->getBidiEdge()) {
    1001              :                     // reversal on driveway
    1002          583 :                     myReversalDriveWays[current].push_back(this);
    1003          583 :                     myReversals.push_back(current);
    1004              :                 }
    1005              :                 break;
    1006              :             }
    1007              :         }
    1008        36964 :         if (toLane == nullptr) {
    1009         4056 :             if (next != end) {
    1010              :                 // no connection found, jump to next route edge
    1011              :                 toLane = (*next)->getLanes()[0];
    1012              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1013              :                 if (gDebugFlag4) {
    1014              :                     std::cout << "      abort: turn-around or jump\n";
    1015              :                 }
    1016              : #endif
    1017          128 :                 myFoundJump = true;
    1018          128 :                 return;
    1019              :             } else {
    1020              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1021              :                 if (gDebugFlag4) {
    1022              :                     std::cout << "      abort: no next lane available\n";
    1023              :                 }
    1024              : #endif
    1025         3928 :                 myTerminateRoute = true;
    1026         3928 :                 return;
    1027              :             }
    1028              :         }
    1029              :     }
    1030         1822 :     myBidiEnded = !seekBidiSwitch;
    1031              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1032              :     if (gDebugFlag4) {
    1033              :         std::cout << " normalEnd myBidiEnded=" << myBidiEnded << "\n";
    1034              :     }
    1035              : #endif
    1036              : }
    1037              : 
    1038              : 
    1039              : bool
    1040        39806 : MSDriveWay::isSwitch(const MSLink* link) {
    1041        76839 :     for (const MSLink* other : link->getLaneBefore()->getNormalPredecessorLane()->getLinkCont()) {
    1042        44377 :         if (other->getLane() != link->getLane() && !other->isTurnaround()) {
    1043              :             return true;
    1044              :         }
    1045              :     }
    1046        53654 :     for (auto ili : link->getLane()->getIncomingLanes()) {
    1047        37986 :         if (ili.viaLink != link && !ili.viaLink->isTurnaround()) {
    1048              :             return true;
    1049              :         }
    1050              :     }
    1051        15668 :     const MSLane* bidi = link->getLane()->getBidiLane();
    1052        15668 :     if (bidi != nullptr) {
    1053        26511 :         for (const MSLink* other : bidi->getLinkCont()) {
    1054        14099 :             if (other->getLane() != link->getLaneBefore()->getNormalPredecessorLane()->getBidiLane() && !other->isTurnaround()) {
    1055              :                 return true;
    1056              :             }
    1057              :         }
    1058              :     }
    1059              :     return false;
    1060              : }
    1061              : 
    1062              : 
    1063              : void
    1064        23736 : MSDriveWay::checkFlanks(const MSLink* originLink, const std::vector<const MSLane*>& lanes, const LaneVisitedMap& visited, bool allFoes, std::set<MSLink*>& flankSwitches) const {
    1065              : #ifdef DEBUG_CHECK_FLANKS
    1066              :     std::cout << " checkFlanks lanes=" << toString(lanes) << " allFoes=" << allFoes << "\n";
    1067              : #endif
    1068        13372 :     const MSLink* reverseOriginLink = originLink != nullptr && originLink->getLane()->getBidiLane() != nullptr && originLink->getLaneBefore()->getBidiLane() != nullptr
    1069        32196 :                                       ? originLink->getLane()->getBidiLane()->getLinkTo(originLink->getLaneBefore()->getBidiLane())
    1070              :                                       : nullptr;
    1071              :     //std::cout << "   originLink=" << originLink->getDescription() << "\n";
    1072         8460 :     if (reverseOriginLink != nullptr) {
    1073         8460 :         reverseOriginLink = reverseOriginLink->getCorrespondingExitLink();
    1074              :         //std::cout << "   reverseOriginLink=" << reverseOriginLink->getDescription() << "\n";
    1075              :     }
    1076        73494 :     for (int i = 0; i < (int)lanes.size(); i++) {
    1077        49758 :         const MSLane* lane = lanes[i];
    1078        49758 :         const MSLane* prev = i > 0 ? lanes[i - 1] : nullptr;
    1079        49758 :         const MSLane* next = i + 1 < (int)lanes.size() ? lanes[i + 1] : nullptr;
    1080        49758 :         if (lane->isInternal()) {
    1081        15864 :             continue;
    1082              :         }
    1083        71387 :         for (auto ili : lane->getIncomingLanes()) {
    1084        44901 :             if (ili.viaLink == originLink
    1085        35731 :                     || ili.viaLink == reverseOriginLink
    1086        33606 :                     || ili.viaLink->getDirection() == LinkDirection::TURN
    1087        67578 :                     || ili.viaLink->getDirection() == LinkDirection::TURN_LEFTHAND) {
    1088         7408 :                 continue;
    1089              :             }
    1090        30085 :             if (ili.lane != prev && ili.lane != next) {
    1091              : #ifdef DEBUG_CHECK_FLANKS
    1092              :                 std::cout << " add flankSwitch junction=" << ili.viaLink->getJunction()->getID() << " index=" << ili.viaLink->getIndex() << " iLane=" << ili.lane->getID() << " prev=" << Named::getIDSecure(prev) <<  " targetLane=" << lane->getID() << " next=" << Named::getIDSecure(next) << "\n";
    1093              : #endif
    1094              :                 flankSwitches.insert(ili.viaLink);
    1095        22616 :             } else if (allFoes) {
    1096              :                 // link is part of the driveway, find foes that cross the driveway without entering
    1097         8037 :                 checkCrossingFlanks(ili.viaLink, visited, flankSwitches);
    1098              :             }
    1099              :         }
    1100              :     }
    1101        23736 : }
    1102              : 
    1103              : 
    1104              : void
    1105         8037 : MSDriveWay::checkCrossingFlanks(MSLink* dwLink, const LaneVisitedMap& visited, std::set<MSLink*>& flankSwitches) const {
    1106              : #ifdef DEBUG_CHECK_FLANKS
    1107              :     std::cout << "  checkCrossingFlanks  dwLink=" << dwLink->getDescription() << " visited=" << formatVisitedMap(visited) << "\n";
    1108              : #endif
    1109              :     const MSJunction* junction = dwLink->getJunction();
    1110         8037 :     if (junction == nullptr) {
    1111              :         return; // unregulated junction;
    1112              :     }
    1113         7963 :     const MSJunctionLogic* logic = junction->getLogic();
    1114         7963 :     if (logic == nullptr) {
    1115              :         return; // unregulated junction;
    1116              :     }
    1117        42827 :     for (const MSEdge* in : junction->getIncoming()) {
    1118        34924 :         if (in->isInternal()) {
    1119        17655 :             continue;
    1120              :         }
    1121        34592 :         for (MSLane* inLane : in->getLanes()) {
    1122        17323 :             const MSLane* inBidi = inLane->getBidiLane();
    1123        25124 :             if (isRailway(inLane->getPermissions()) && visited.count(inLane) == 0 && (inBidi == nullptr || visited.count(inBidi) == 0)) {
    1124         6300 :                 for (MSLink* link : inLane->getLinkCont()) {
    1125         3279 :                     if (link->getIndex() >= 0 && logic->getFoesFor(dwLink->getIndex()).test(link->getIndex())
    1126         8453 :                             && visited.count(link->getLane()) == 0) {
    1127              : #ifdef DEBUG_CHECK_FLANKS
    1128              :                         std::cout << " add crossing flankSwitch junction=" << junction->getID() << " index=" << link->getIndex() << "\n";
    1129              : #endif
    1130          432 :                         if (link->getViaLane() == nullptr) {
    1131              :                             flankSwitches.insert(link);
    1132              :                         } else {
    1133              :                             flankSwitches.insert(link->getViaLane()->getLinkCont().front());
    1134              :                         }
    1135              :                     }
    1136              :                 }
    1137              :             }
    1138              :         }
    1139              :     }
    1140              : }
    1141              : 
    1142              : void
    1143         9866 : MSDriveWay::findFlankProtection(MSLink* link, MSLink* origLink, std::vector<const MSLane*>& flank) {
    1144              : #ifdef DEBUG_CHECK_FLANKS
    1145              :     std::cout << "  findFlankProtection link=" << link->getDescription() << " origLink=" << origLink->getDescription() << "\n";
    1146              : #endif
    1147         9866 :     if (link->getCorrespondingEntryLink()->getTLLogic() != nullptr) {
    1148         2193 :         MSLink* entry = const_cast<MSLink*>(link->getCorrespondingEntryLink());
    1149              :         // guarded by signal
    1150              : #ifdef DEBUG_CHECK_FLANKS
    1151              :         std::cout << "   flank guarded by " << entry->getTLLogic()->getID() << "\n";
    1152              : #endif
    1153              :         // @note, technically it's enough to collect links from foe driveways
    1154              :         // but this also adds "unused" conflict links which may aid comprehension
    1155         2193 :         myConflictLinks.push_back(entry);
    1156         2193 :         addFoes(entry);
    1157              :     } else {
    1158              :         const MSLane* lane = link->getLaneBefore();
    1159              :         std::vector<MSLink*> predLinks;
    1160        15295 :         for (auto ili : lane->getIncomingLanes()) {
    1161         7622 :             if (!ili.viaLink->isTurnaround()) {
    1162         7368 :                 predLinks.push_back(ili.viaLink);
    1163              :             }
    1164              :         }
    1165         7673 :         if (predLinks.size() > 1) {
    1166              :             // this is a switch
    1167              : #ifdef DEBUG_ADD_FOES
    1168              :             std::cout << "    predecessors of " << link->getDescription() << " isSwitch\n";
    1169              : #endif
    1170          705 :             for (MSLink* pred : predLinks) {
    1171          470 :                 addSwitchFoes(pred);
    1172              :             }
    1173         7438 :         } else if (predLinks.size() == 1) {
    1174         6898 :             if (isSwitch(link)) {
    1175         5907 :                 addSwitchFoes(link);
    1176              :             } else {
    1177              :                 // continue upstream via single predecessor
    1178          991 :                 findFlankProtection(predLinks.front(), origLink, flank);
    1179              :             }
    1180              :         }
    1181         7673 :     }
    1182         9866 : }
    1183              : 
    1184              : 
    1185              : void
    1186         6377 : MSDriveWay::addSwitchFoes(MSLink* link) {
    1187              :     auto it = mySwitchDriveWays.find(link);
    1188         6377 :     if (it != mySwitchDriveWays.end()) {
    1189              : #ifdef DEBUG_ADD_FOES
    1190              :         std::cout << "   driveway " << myID << " addSwitchFoes for link " << link->getDescription() << "\n";
    1191              : #endif
    1192         7305 :         for (MSDriveWay* foe : it->second) {
    1193         5035 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1194              : #ifdef DEBUG_ADD_FOES
    1195              :                 std::cout << "   foe=" << foe->myID
    1196              :                           << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this)
    1197              :                           << " cc1=" << crossingConflict(*foe) << " cc2=" << foe->crossingConflict(*this) << "\n";
    1198              : #endif
    1199         2695 :                 myFoes.push_back(foe);
    1200              :             } else {
    1201              : #ifdef DEBUG_ADD_FOES
    1202              :                 std::cout << "   cand=" << foe->myID << "\n";
    1203              : #endif
    1204              :             }
    1205              :         }
    1206              :     }
    1207         6377 : }
    1208              : 
    1209              : 
    1210              : MSDriveWay*
    1211         5934 : MSDriveWay::buildDriveWay(const std::string& id, const MSLink* link, MSRouteIterator first, MSRouteIterator end) {
    1212              :     // collect lanes and links that are relevant for setting this signal for the current driveWay
    1213              :     // For each driveway we collect
    1214              :     //   - conflictLanes (signal must be red if any conflict lane is occupied)
    1215              :     //   - conflictLinks (signal must be red if any conflict link is approached by a vehicle
    1216              :     //      - that cannot break in time (arrivalSpeedBraking > 0)
    1217              :     //      - approached by a vehicle with higher switching priority (see #3941)
    1218              :     // These objects are construct in steps:
    1219              :     //
    1220              :     // forwardBlock
    1221              :     // - search forward recursive from outgoing lane until controlled railSignal link found
    1222              :     //   -> add all found lanes to conflictLanes
    1223              :     //
    1224              :     // bidiBlock (if any forwardBlock edge has bidi edge)
    1225              :     // - search bidi backward recursive until first switch
    1226              :     //   - from switch search backward recursive all other incoming until controlled rail signal link
    1227              :     //     -> add final links to conflictLinks
    1228              :     //
    1229              :     // flanks
    1230              :     // - search backward recursive from flanking switches
    1231              :     //   until controlled railSignal link or protecting switch is found
    1232              :     //   -> add all found lanes to conflictLanes
    1233              :     //   -> add final links to conflictLinks
    1234         5934 :     MSDriveWay* dw = new MSDriveWay(link, id);
    1235              :     LaneVisitedMap visited;
    1236              :     std::vector<const MSLane*> before;
    1237         5934 :     MSLane* fromBidi = nullptr;
    1238         5934 :     if (link != nullptr) {
    1239         3343 :         appendMapIndex(visited, link->getLaneBefore());
    1240         3343 :         fromBidi = link->getLaneBefore()->getBidiLane();
    1241              :     }
    1242              :     std::set<MSLink*> flankSwitches; // list of switches that threaten the driveway and for which protection must be found
    1243              : 
    1244         5934 :     if (fromBidi != nullptr) {
    1245         2184 :         before.push_back(fromBidi);
    1246              :     }
    1247         5934 :     dw->buildRoute(link, 0., first, end, visited, flankSwitches);
    1248         5934 :     dw->myCoreSize = (int)dw->myRoute.size();
    1249         5934 :     dw->checkFlanks(link, dw->myForward, visited, true, flankSwitches);
    1250         5934 :     dw->checkFlanks(link, dw->myBidi, visited, false, flankSwitches);
    1251         5934 :     dw->checkFlanks(link, before, visited, true, flankSwitches);
    1252        13441 :     for (MSLink* fsLink : flankSwitches) {
    1253              : #ifdef DEBUG_ADD_FOES
    1254              :         if (DEBUG_COND_DW) {
    1255              :             std::cout << " fsLink=" << fsLink->getDescription() << "\n";
    1256              :         }
    1257              : #endif
    1258         7507 :         dw->findFlankProtection(fsLink, fsLink, dw->myFlank);
    1259              :     }
    1260              :     std::set<MSLink*> flankSwitchesBidiExtended;
    1261         5934 :     dw->checkFlanks(link, dw->myBidiExtended, visited, false, flankSwitchesBidiExtended);
    1262         7302 :     for (MSLink* link : flankSwitchesBidiExtended) {
    1263              : #ifdef DEBUG_ADD_FOES
    1264              :         if (DEBUG_COND_DW) {
    1265              :             std::cout << " fsLinkExtended=" << link->getDescription() << "\n";
    1266              :         }
    1267              : #endif
    1268         1368 :         dw->findFlankProtection(link, link, dw->myBidiExtended);
    1269              :     }
    1270         5934 :     MSRailSignal* rs = link ? const_cast<MSRailSignal*>(static_cast<const MSRailSignal*>(link->getTLLogic())) : nullptr;
    1271         9277 :     const bool movingBlock = (rs && rs->isMovingBlock()) || (!rs && OptionsCont::getOptions().getBool("railsignal-moving-block"));
    1272              : #ifdef DEBUG_BUILD_DRIVEWAY
    1273              :     if (DEBUG_COND_DW) {
    1274              :         std::cout << SIMTIME << " buildDriveWay " << dw->myID << " link=" << (link == nullptr ? "NULL" : link->getDescription())
    1275              :                   << "\n    route=" << toString(dw->myRoute)
    1276              :                   << "\n    forward=" << toString(dw->myForward)
    1277              :                   << "\n    bidi=" << toString(dw->myBidi)
    1278              :                   << "\n    bidiEx=" << toString(dw->myBidiExtended)
    1279              :                   << "\n    flank=" << toString(dw->myFlank)
    1280              :                   << "\n    flankSwitch=" << MSRailSignal::describeLinks(std::vector<MSLink*>(flankSwitches.begin(), flankSwitches.end()))
    1281              :                   << "\n    coreSize=" << dw->myCoreSize
    1282              :                   << "\n";
    1283              :     }
    1284              : #endif
    1285         5934 :     if (!rs || !rs->isMovingBlock()) {
    1286         5909 :         dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myForward.begin(), dw->myForward.end());
    1287              :     }
    1288         5934 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myBidi.begin(), dw->myBidi.end());
    1289         5934 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myFlank.begin(), dw->myFlank.end());
    1290         5934 :     dw->addBidiFoes(rs, false);
    1291         5934 :     dw->addBidiFoes(rs, true);
    1292              :     // add driveways that start on the same signal / lane
    1293         5934 :     dw->addParallelFoes(link, *first);
    1294              :     // add driveways that reverse along this driveways route
    1295         5934 :     dw->addReversalFoes();
    1296              :     // make foes unique and symmetrical
    1297         5934 :     std::set<MSDriveWay*, ComparatorNumericalIdLess> uniqueFoes(dw->myFoes.begin(), dw->myFoes.end());
    1298         5934 :     std::set<MSLink*> uniqueCLink(dw->myConflictLinks.begin(), dw->myConflictLinks.end());
    1299              :     dw->myFoes.clear();
    1300         5934 :     const MSEdge* lastEdge = &dw->myForward.back()->getEdge();
    1301        10032 :     for (MSDriveWay* foe : uniqueFoes) {
    1302         4098 :         const MSEdge* foeLastEdge = &foe->myForward.back()->getEdge();
    1303         4098 :         const bool sameLast = foeLastEdge == lastEdge;
    1304         4098 :         if (sameLast && !movingBlock) {
    1305         1049 :             dw->myFoes.push_back(foe);
    1306         1049 :             if (foe != dw) {
    1307         1049 :                 foe->myFoes.push_back(dw);
    1308              :             }
    1309              :         } else {
    1310         3049 :             if (foe->bidiBlockedByEnd(*dw)) {
    1311              : #ifdef DEBUG_ADD_FOES
    1312              :                 if (DEBUG_COND_DW) {
    1313              :                     std::cout << " setting " << dw->getID() << " as foe of " << foe->getID() << "\n";
    1314              :                 }
    1315              : #endif
    1316         1646 :                 foe->myFoes.push_back(dw);
    1317         1646 :                 foe->addSidings(dw);
    1318              :             } else {
    1319         1403 :                 dw->buildSubFoe(foe, movingBlock);
    1320              :             }
    1321         3049 :             if (dw->bidiBlockedByEnd(*foe)) {
    1322              : #ifdef DEBUG_ADD_FOES
    1323              :                 if (DEBUG_COND_DW) {
    1324              :                     std::cout << " addFoeCheckSiding " << foe->getID() << "\n";
    1325              :                 }
    1326              : #endif
    1327         1754 :                 dw->myFoes.push_back(foe);
    1328         1754 :                 dw->addSidings(foe);
    1329              :             } else  {
    1330         1295 :                 foe->buildSubFoe(dw, movingBlock);
    1331              :             }
    1332              :         }
    1333         4098 :         if (link) {
    1334         2887 :             foe->addConflictLink(link);
    1335              :         }
    1336              :         // ignore links that have the same start junction
    1337         4098 :         if (foe->myRoute.front()->getFromJunction() != dw->myRoute.front()->getFromJunction()) {
    1338         6282 :             for (auto ili : foe->myForward.front()->getIncomingLanes()) {
    1339         2875 :                 if (ili.viaLink->getTLLogic() != nullptr) {
    1340              :                     // ignore links that originate on myBidi
    1341         2546 :                     const MSLane* origin = ili.viaLink->getLaneBefore();
    1342         2546 :                     if (std::find(dw->myBidi.begin(), dw->myBidi.end(), origin) == dw->myBidi.end()) {
    1343              :                         uniqueCLink.insert(ili.viaLink);
    1344              :                     }
    1345              :                 }
    1346              :             }
    1347              :         }
    1348              :     }
    1349              :     dw->myConflictLinks.clear();
    1350         5934 :     dw->myConflictLinks.insert(dw->myConflictLinks.begin(), uniqueCLink.begin(), uniqueCLink.end());
    1351         5934 :     myEndingDriveways[lastEdge].push_back(dw);
    1352         5934 :     if (!movingBlock) {
    1353              :         // every driveway is it's own foe (also all driveways that depart in the same block)
    1354        12888 :         for (MSDriveWay* sameEnd : myEndingDriveways[lastEdge]) {
    1355              :             if (uniqueFoes.count(sameEnd) == 0) {
    1356         5972 :                 dw->myFoes.push_back(sameEnd);
    1357         5972 :                 if (sameEnd != dw) {
    1358          105 :                     sameEnd->myFoes.push_back(dw);
    1359              :                 }
    1360              :             }
    1361              :         }
    1362              :     }
    1363              : #ifdef DEBUG_BUILD_DRIVEWAY
    1364              :     if (DEBUG_COND_DW) {
    1365              :         std::cout << dw->myID << " finalFoes " << toString(dw->myFoes) << "\n";
    1366              :     }
    1367              : #endif
    1368         5934 :     return dw;
    1369         5934 : }
    1370              : 
    1371              : std::string
    1372         2917 : MSDriveWay::getTLLinkID(const MSLink* link) {
    1373         5834 :     return link->getTLLogic()->getID() + "_" + toString(link->getTLIndex());
    1374              : }
    1375              : 
    1376              : std::string
    1377            0 : MSDriveWay::getJunctionLinkID(const MSLink* link) {
    1378            0 :     return link->getJunction()->getID() + "_" + toString(link->getIndex());
    1379              : }
    1380              : 
    1381              : std::string
    1382         3353 : MSDriveWay::getClickableTLLinkID(const MSLink* link) {
    1383        10059 :     return "junction '" +  link->getTLLogic()->getID() + "', link " + toString(link->getTLIndex());
    1384              : }
    1385              : 
    1386              : std::string
    1387            0 : MSDriveWay::formatVisitedMap(const LaneVisitedMap& visited) {
    1388              :     UNUSED_PARAMETER(visited);
    1389              :     /*
    1390              :     std::vector<const MSLane*> lanes(visited.size(), nullptr);
    1391              :     for (auto item : visited) {
    1392              :         lanes[item.second] = item.first;
    1393              :     }
    1394              :     for (auto it = lanes.begin(); it != lanes.end();) {
    1395              :         if (*it == nullptr) {
    1396              :             it = lanes.erase(it);
    1397              :         } else {
    1398              :             it++;
    1399              :         }
    1400              :     }
    1401              :     return toString(lanes);
    1402              :     */
    1403            0 :     return "dummy";
    1404              : }
    1405              : 
    1406              : 
    1407              : void
    1408        40307 : MSDriveWay::appendMapIndex(LaneVisitedMap& map, const MSLane* lane) {
    1409              :     // avoid undefined behavior from evaluation order
    1410        40307 :     const int tmp = (int)map.size();
    1411        40307 :     map[lane] = tmp;
    1412        40307 : }
    1413              : 
    1414              : bool
    1415      8311394 : MSDriveWay::match(MSRouteIterator firstIt, MSRouteIterator endIt) const {
    1416              :     // @todo optimize: it is sufficient to check for specific edges (after each switch)
    1417      8311394 :     auto itRoute = firstIt;
    1418              :     auto itDwRoute = myRoute.begin();
    1419              :     bool match = true;
    1420     50007844 :     while (itRoute != endIt && itDwRoute != myRoute.end()) {
    1421     41717745 :         if (*itRoute != *itDwRoute) {
    1422              :             match = false;
    1423              : #ifdef DEBUG_MATCH
    1424              :             std::cout << "  check dw=" << getID() << " match failed at vehEdge=" << (*itRoute)->getID() << " dwEdge=" << (*itDwRoute)->getID() << "\n";
    1425              : #endif
    1426              :             break;
    1427              :         }
    1428              :         itRoute++;
    1429              :         itDwRoute++;
    1430              :     }
    1431              :     // if the vehicle arrives before the end of this driveway,
    1432              :     // we'd rather build a new driveway to avoid superfluous restrictions
    1433      8290099 :     if (match && itDwRoute == myRoute.end()
    1434     16589377 :             && (itRoute == endIt || myAbortedBuild || myBidiEnded || myFoundJump || myIsSubDriveway)) {
    1435              :         //std::cout << "  using dw=" << "\n";
    1436      8273645 :         if (itRoute != endIt) {
    1437              :             // check whether the current route requires an extended driveway
    1438       305908 :             const MSEdge* next = *itRoute;
    1439       305908 :             const MSEdge* prev = myRoute.back();
    1440          568 :             if (myFoundJump && prev->getBidiEdge() != next && prev->getBidiEdge() != nullptr
    1441       306116 :                     && prev->isConnectedTo(*next, (SUMOVehicleClass)(SVC_RAIL_CLASSES & prev->getPermissions()))) {
    1442              : #ifdef DEBUG_MATCH
    1443              :                 std::cout << "  check dw=" << getID() << " prev=" << prev->getID() << " next=" << next->getID() << "\n";
    1444              : #endif
    1445              :                 return false;
    1446              :             }
    1447       305898 :             if (!myFoundJump && prev->getBidiEdge() == next && prev == &myForward.back()->getEdge()) {
    1448              :                 assert(myIsSubDriveway || myBidiEnded);
    1449              :                 // must not leave driveway via reversal
    1450              : #ifdef DEBUG_MATCH
    1451              :                 std::cout << getID() << " back=" << myForward.back()->getID() << " noMatch route " << toString(ConstMSEdgeVector(firstIt, endIt)) << "\n";
    1452              : #endif
    1453              :                 return false;
    1454              :             }
    1455              :         }
    1456      8273620 :         return true;
    1457              :     }
    1458              :     return false;
    1459              : }
    1460              : 
    1461              : void
    1462         8851 : MSDriveWay::addFoes(const MSLink* link) {
    1463              : #ifdef DEBUG_ADD_FOES
    1464              :     std::cout << "driveway " << myID << " addFoes for link " << link->getDescription() << "\n";
    1465              : #endif
    1466         8851 :     const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(link->getTLLogic());
    1467         8851 :     if (rs != nullptr) {
    1468        11081 :         for (MSDriveWay* foe : rs->retrieveDriveWays(link->getTLIndex())) {
    1469              : #ifdef DEBUG_ADD_FOES
    1470              :             std::cout << "  cand foe=" << foe->myID << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this) << " cc1=" << crossingConflict(*foe) << " cc2=" <<  foe->crossingConflict(*this) << "\n";
    1471              : #endif
    1472         2244 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1473              : #ifdef DEBUG_ADD_FOES
    1474              :                 std::cout << "   foe=" << foe->myID << "\n";
    1475              : #endif
    1476         1911 :                 myFoes.push_back(foe);
    1477              :             }
    1478         8837 :         }
    1479              :     }
    1480         8851 : }
    1481              : 
    1482              : 
    1483              : void
    1484        11868 : MSDriveWay::addBidiFoes(const MSRailSignal* ownSignal, bool extended) {
    1485              : #ifdef DEBUG_ADD_FOES
    1486              :     std::cout << "driveway " << myID << " addBidiFoes extended=" << extended << "\n";
    1487              : #endif
    1488        11868 :     const std::vector<const MSLane*>& bidiLanes = extended ? myBidiExtended : myBidi;
    1489        41262 :     for (const MSLane* bidi : bidiLanes) {
    1490        61233 :         for (auto ili : bidi->getIncomingLanes()) {
    1491        31839 :             const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(ili.viaLink->getTLLogic());
    1492        31839 :             if (rs != nullptr && rs != ownSignal &&
    1493        31839 :                     std::find(bidiLanes.begin(), bidiLanes.end(), ili.lane) != bidiLanes.end()) {
    1494         3315 :                 addFoes(ili.viaLink);
    1495              :             }
    1496              :         }
    1497        29394 :         const MSEdge* bidiEdge = &bidi->getEdge();
    1498              :         if (myDepartureDriveways.count(bidiEdge) != 0) {
    1499         1942 :             for (MSDriveWay* foe : myDepartureDriveways[bidiEdge]) {
    1500          968 :                 if (flankConflict(*foe)) {
    1501              : #ifdef DEBUG_ADD_FOES
    1502              :                     std::cout << "  foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << "\n";
    1503              : #endif
    1504          726 :                     myFoes.push_back(foe);
    1505              :                 } else {
    1506              : #ifdef DEBUG_ADD_FOES
    1507              :                     std::cout << "  cand foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << " rejected\n";
    1508              : #endif
    1509              :                 }
    1510              :             }
    1511              :         }
    1512              :         if (myDepartureDrivewaysEnds.count(bidiEdge) != 0) {
    1513         1744 :             for (MSDriveWay* foe : myDepartureDrivewaysEnds[bidiEdge]) {
    1514          913 :                 if (flankConflict(*foe)) {
    1515              : #ifdef DEBUG_ADD_FOES
    1516              :                     std::cout << "  foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << "\n";
    1517              : #endif
    1518          706 :                     myFoes.push_back(foe);
    1519              :                 } else {
    1520              : #ifdef DEBUG_ADD_FOES
    1521              :                     std::cout << "  cand foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << " rejected\n";
    1522              : #endif
    1523              :                 }
    1524              :             }
    1525              :         }
    1526              :     }
    1527        11868 : }
    1528              : 
    1529              : 
    1530              : void
    1531         5934 : MSDriveWay::addParallelFoes(const MSLink* link, const MSEdge* first) {
    1532              : #ifdef DEBUG_ADD_FOES
    1533              :     std::cout << "driveway " << myID << " addParallelFoes\n";
    1534              : #endif
    1535         5934 :     if (link) {
    1536         3343 :         addFoes(link);
    1537              :     } else {
    1538              :         auto it = myDepartureDriveways.find(first);
    1539         2591 :         if (it != myDepartureDriveways.end()) {
    1540         2700 :             for (MSDriveWay* foe : it->second) {
    1541              : #ifdef DEBUG_ADD_FOES
    1542              :                 std::cout << "  foe " << foe->getID() << " departs on first=" << first->getID() << "\n";
    1543              : #endif
    1544          109 :                 myFoes.push_back(foe);
    1545              :             }
    1546              :         }
    1547              :     }
    1548         5934 : }
    1549              : 
    1550              : 
    1551              : void
    1552         5934 : MSDriveWay::addReversalFoes() {
    1553              : #ifdef DEBUG_ADD_FOES
    1554              :     std::cout << "driveway " << myID << " addReversalFoes\n";
    1555              : #endif
    1556              :     std::set<const MSEdge*> forward;
    1557        24114 :     for (const MSLane* lane : myForward) {
    1558        18180 :         if (lane->isNormal()) {
    1559        12612 :             forward.insert(&lane->getEdge());
    1560              :         }
    1561              :     }
    1562              :     int i = 0;
    1563        30139 :     for (const MSEdge* e : myRoute) {
    1564        12622 :         if (forward.count(e) != 0) {
    1565              :             // reversals in our own forward section must be ignored
    1566              :             continue;
    1567              :         }
    1568        11583 :         if (i == myCoreSize) {
    1569              :             break;
    1570              :         }
    1571        11583 :         i++;
    1572              :         auto it = myReversalDriveWays.find(e);
    1573        11583 :         if (it != myReversalDriveWays.end()) {
    1574         1288 :             for (MSDriveWay* foe : it->second) {
    1575              :                 // check whether the foe reverses into our own forward section
    1576              :                 // (it might reverse again or disappear via arrival)
    1577              : #ifdef DEBUG_ADD_FOES
    1578              :                 //std::cout << "  candidate foe " << foe->getID() << " reverses on edge=" << e->getID() << " forward=" << joinNamedToString(forward, " ") << " foeRoute=" << toString(foe->myRoute) << "\n";
    1579              : #endif
    1580         1722 :                 if (forwardRouteConflict(forward, *foe)) {
    1581              :                     std::set<const MSEdge*> foeForward;
    1582          999 :                     for (const MSLane* lane : foe->myForward) {
    1583          848 :                         if (lane->isNormal()) {
    1584          536 :                             foeForward.insert(&lane->getEdge());
    1585          536 :                             if (lane->getBidiLane() != nullptr) {
    1586          536 :                                 foeForward.insert(lane->getEdge().getBidiEdge());
    1587              :                             }
    1588              :                         }
    1589              :                     }
    1590              : #ifdef DEBUG_ADD_FOES
    1591              :                     std::cout << "  reversal cand=" << foe->getID() << " foeForward " << toString(foeForward) << "\n";
    1592              : #endif
    1593          302 :                     if (foe->forwardRouteConflict(foeForward, *this, true)) {
    1594              : #ifdef DEBUG_ADD_FOES
    1595              :                         std::cout << "  foe " << foe->getID() << " reverses on edge=" << e->getID() << "\n";
    1596              : #endif
    1597          116 :                         myFoes.push_back(foe);
    1598              :                     }
    1599              :                 }
    1600              :             }
    1601              :         }
    1602              :     }
    1603         5934 : }
    1604              : 
    1605              : 
    1606              : bool
    1607         2698 : MSDriveWay::buildSubFoe(MSDriveWay* foe, bool movingBlock) {
    1608              :     // Subdriveways (Teilfahrstraße) model the resolution of a driving conflict
    1609              :     // before a vehicle has left the driveway. This is possible when the driveway diverges from the foe
    1610              :     // driveway at an earlier point (switch or crossing).
    1611              :     //
    1612              :     // We already know that the last edge of this driveway doesn't impact the foe (unless the driveway ends within the block).
    1613              :     // Remove further edges from the end of the driveway (myForward) until the point of conflict is found.
    1614              :     //
    1615              :     // For movingBlock the logic is changed:
    1616              :     // We remove the conflict-free part as before but then keep removing the the conflict part until the
    1617              :     // another non-conconflit part is found
    1618         2698 :     if (myForward.size() < foe->myForward.size() &&
    1619         2698 :             myForward == std::vector<const MSLane*>(foe->myForward.begin(), foe->myForward.begin() + myForward.size())) {
    1620              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1621              :         std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " is subpart of foe=" << foe->getID() << "\n";
    1622              : #endif
    1623           16 :         foe->myFoes.push_back(this);
    1624           16 :         return true;
    1625              :     }
    1626         2682 :     int subLast = (int)myForward.size() - 2;
    1627              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1628              :     if (subLast < 0) {
    1629              :         std::cout << "  " << getID() << " cannot build subDriveWay for foe " << foe->getID() << " because myForward has only a single lane\n";
    1630              :     }
    1631              : #endif
    1632              :     bool foundConflict = false;
    1633         8563 :     while (subLast >= 0) {
    1634         8055 :         const MSLane* lane = myForward[subLast];
    1635         8055 :         MSDriveWay tmp(myOrigin, "tmp", true);
    1636         8055 :         tmp.myForward.push_back(lane);
    1637              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1638              :         std::cout << "  subLast=" << subLast << " lane=" << lane->getID() << " fc=" << tmp.flankConflict(*foe) << " cc=" << tmp.crossingConflict(*foe)
    1639              :                   << " bc=" << (std::find(foe->myBidi.begin(), foe->myBidi.end(), lane) != foe->myBidi.end()) << "\n";
    1640              : #endif
    1641         8055 :         const bool bidiConflict = std::find(foe->myBidi.begin(), foe->myBidi.end(), lane) != foe->myBidi.end();
    1642         8055 :         if (tmp.flankConflict(*foe) || tmp.crossingConflict(*foe) || bidiConflict) {
    1643              :             foundConflict = true;
    1644         2188 :             if (!movingBlock || bidiConflict) {
    1645              :                 break;
    1646              :             }
    1647         5867 :         } else if (foundConflict) {
    1648              :             break;
    1649              :         }
    1650         5881 :         subLast--;
    1651         8055 :     }
    1652         2682 :     if (subLast < 0) {
    1653          508 :         if (foe->myTerminateRoute) {
    1654          468 :             if (bidiBlockedByEnd(*foe) && bidiBlockedBy(*this) && foe->forwardEndOnRoute(this)) {
    1655           62 :                 foe->myFoes.push_back(this);
    1656              :                 // foe will get the sidings
    1657           62 :                 addSidings(foe, true);
    1658              :             }
    1659              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1660              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " terminates\n";
    1661              : #endif
    1662           40 :         } else if (myTerminateRoute && myBidi.size() <= myForward.size()) {
    1663            5 :             foe->myFoes.push_back(this);
    1664              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1665              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " terminates, foe=" << foe->getID() << "\n";
    1666              : #endif
    1667            5 :             return true;
    1668              :         } else if (foe->myReversals.size() % 2 == 1) {
    1669              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1670              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " has " << foe->myReversals.size() << " reversals\n";
    1671              : #endif
    1672              :         } else {
    1673              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1674              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " failed\n";
    1675              : #endif
    1676              : #ifdef SUBDRIVEWAY_WARN_NOCONFLICT
    1677              :             WRITE_WARNINGF("No point of conflict found between driveway '%' and driveway '%' when creating sub-driveway", getID(), foe->getID());
    1678              : #endif
    1679              :         }
    1680          503 :         return false;
    1681              :     }
    1682         2174 :     int subSize = subLast + 1;
    1683         2776 :     for (MSDriveWay* cand : mySubDriveWays) {
    1684         1092 :         if ((int)cand->myForward.size() == subSize) {
    1685              :             // can re-use existing sub-driveway
    1686          490 :             foe->myFoes.push_back(cand);
    1687          490 :             cand->myFoes.push_back(foe);
    1688              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1689              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " useExisting=" << cand->getID() << "\n";
    1690              : #endif
    1691          490 :             return true;
    1692              :         }
    1693              :     }
    1694         1684 :     std::vector<const MSLane*> forward(myForward.begin(), myForward.begin() + subSize);
    1695              :     std::vector<const MSEdge*> route;
    1696         7922 :     for (const MSLane* lane : forward) {
    1697         6238 :         if (lane->isNormal()) {
    1698         3675 :             route.push_back(&lane->getEdge());
    1699              :         }
    1700              :     }
    1701         1684 :     if (myRoute.size() > route.size()) {
    1702              :         // route continues. make sure the subDriveway does not end with a reversal
    1703         1684 :         const MSEdge* lastNormal = route.back();
    1704         1684 :         const MSEdge* nextNormal = myRoute[route.size()];
    1705         1684 :         if (lastNormal->getBidiEdge() == nextNormal) {
    1706              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1707              :             std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID()
    1708              :                       << " lastNormal=" << lastNormal->getID() << " nextNormal=" << nextNormal->getID() << " endWithReversal\n";
    1709              : #endif
    1710              :             return false;
    1711              :         }
    1712              :     }
    1713         3344 :     MSDriveWay* sub = new MSDriveWay(myOrigin, getID() + "." + toString(mySubDriveWays.size()));
    1714         1672 :     sub->myLane = myLane;
    1715         1672 :     sub->myIsSubDriveway = true;
    1716         1672 :     sub->myForward = forward;
    1717         1672 :     sub->myRoute = route;
    1718         1672 :     sub->myCoreSize = (int)sub->myRoute.size();
    1719         1672 :     myLane->addMoveReminder(sub);
    1720              : 
    1721              :     // copy trains that are currently on this driveway (and associated entry events)
    1722         1737 :     for (SUMOVehicle* veh : myTrains) {
    1723           65 :         if (std::find(sub->myRoute.begin(), sub->myRoute.end(), veh->getEdge()) != sub->myRoute.end()) {
    1724              :             sub->myTrains.insert(veh);
    1725           53 :             dynamic_cast<MSBaseVehicle*>(veh)->addReminder(sub);
    1726           53 :             for (const VehicleEvent& ve : myVehicleEvents) {
    1727            0 :                 if (ve.id == veh->getID()) {
    1728            0 :                     sub->myVehicleEvents.push_back(ve);
    1729              :                 }
    1730              :             }
    1731              :         }
    1732              :     }
    1733              : 
    1734         1672 :     foe->myFoes.push_back(sub);
    1735         1672 :     sub->myFoes.push_back(foe);
    1736         1672 :     mySubDriveWays.push_back(sub);
    1737              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1738              :     std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " sub=" << sub->getID() << " route=" << toString(sub->myRoute) << "\n";
    1739              : #endif
    1740              :     return true;
    1741         1684 : }
    1742              : 
    1743              : void
    1744         3462 : MSDriveWay::addSidings(MSDriveWay* foe, bool addToFoe) {
    1745         3462 :     const MSEdge* foeEndBidi = foe->myForward.back()->getEdge().getBidiEdge();
    1746              :     int forwardNormals = 0;
    1747        15840 :     for (auto lane : foe->myForward) {
    1748        12378 :         if (lane->isNormal()) {
    1749         8276 :             forwardNormals++;
    1750              :         }
    1751              :     }
    1752         3462 :     if (forwardNormals == (int)foe->myRoute.size()) {
    1753              : #ifdef DEBUG_BUILD_SIDINGS
    1754              :         std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " forwardNormals=" << forwardNormals << " frSize=" << foe->myRoute.size() <<  " aborted\n";
    1755              : #endif
    1756          724 :         return;
    1757              :     }
    1758              :     auto foeSearchBeg = foe->myRoute.begin() + forwardNormals;
    1759              :     auto foeSearchEnd = foe->myRoute.end();
    1760         2738 :     if (foeEndBidi == nullptr) {
    1761            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " noBidi\n");
    1762              :     }
    1763              :     int i;
    1764              :     std::vector<int> start;
    1765              :     std::vector<double> length;
    1766        16143 :     for (i = 0; i < (int)myRoute.size(); i++) {
    1767        16143 :         if (myRoute[i] == foeEndBidi) {
    1768              :             break;
    1769              :         }
    1770              :     }
    1771         2738 :     if (i == (int)myRoute.size()) {
    1772            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " foeEndBidi=" + foeEndBidi->getID() + " not on route\n");
    1773              :     }
    1774         2738 :     const MSEdge* next = myRoute[i];
    1775              : #ifdef DEBUG_BUILD_SIDINGS
    1776              :     std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " next=" << next->getID() << " forwardNormals=" << forwardNormals << " frSize=" << foe->myRoute.size() << " foeSearchBeg=" << (*foeSearchBeg)->getID() << "\n";
    1777              : #endif
    1778         2738 :     i--;
    1779        16143 :     for (; i >= 0; i--) {
    1780        13405 :         const MSEdge* cur = myRoute[i];
    1781        13405 :         if (hasRS(cur, next)) {
    1782         2685 :             if (std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge()) == foeSearchEnd) {
    1783          643 :                 start.push_back(i);
    1784          643 :                 length.push_back(0);
    1785              :             }
    1786              :         }
    1787        13405 :         if (!start.empty()) {
    1788         1807 :             auto itFind = std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge());
    1789         1807 :             if (itFind != foeSearchEnd) {
    1790              : #ifdef DEBUG_BUILD_SIDINGS
    1791              :                 std::cout << "endSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " curBidi=" << Named::getIDSecure(cur->getBidiEdge()) << " length=" << toString(length) << "\n";
    1792              : #endif
    1793          401 :                 const int firstIndex = i + 1;
    1794          401 :                 if (addToFoe) {
    1795           34 :                     auto& foeSidings = foe->mySidings[this];
    1796              :                     // indices must be mapped onto foe route;
    1797           34 :                     const MSEdge* first = myRoute[firstIndex];
    1798           34 :                     auto itFirst = std::find(foe->myRoute.begin(), foe->myRoute.end(), first);
    1799           34 :                     if (itFirst != foe->myRoute.end()) {
    1800           87 :                         for (int j = 0; j < (int)length.size(); j++) {
    1801           53 :                             const MSEdge* last = myRoute[start[j]];
    1802           53 :                             auto itLast = std::find(itFirst, foe->myRoute.end(), last);
    1803           53 :                             if (itLast != foe->myRoute.end()) {
    1804           53 :                                 foeSidings.insert(foeSidings.begin(), Siding((int)(itFirst - foe->myRoute.begin()), (int)(itLast - foe->myRoute.begin()), length[j]));
    1805              :                             }
    1806              :                         }
    1807              :                     }
    1808              :                 } else {
    1809          367 :                     auto& foeSidings = mySidings[foe];
    1810          846 :                     for (int j = 0; j < (int)length.size(); j++) {
    1811          479 :                         foeSidings.insert(foeSidings.begin(), Siding(firstIndex, start[j], length[j]));
    1812              :                     }
    1813              :                 }
    1814              :                 start.clear();
    1815              :                 length.clear();
    1816          401 :                 foeSearchBeg = itFind;
    1817              :             } else {
    1818         3156 :                 for (int j = 0; j < (int)length.size(); j++) {
    1819         1750 :                     length[j] += cur->getLength();
    1820              :                 }
    1821              :             }
    1822              :         }
    1823              :         next = cur;
    1824              :     }
    1825         2738 : }
    1826              : 
    1827              : 
    1828              : bool
    1829        13405 : MSDriveWay::hasRS(const MSEdge* cur, const MSEdge* next) {
    1830        13405 :     if (cur->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1831              :         // check if there is a controlled link between cur and next
    1832        10579 :         for (auto lane : cur->getLanes()) {
    1833        10962 :             for (const MSLink* link : lane->getLinkCont()) {
    1834         7015 :                 if (&link->getLane()->getEdge() == next && link->getTLLogic() != nullptr) {
    1835              :                     return true;
    1836              :                 }
    1837              :             }
    1838              :         }
    1839              :     }
    1840              :     return false;
    1841              : }
    1842              : 
    1843              : 
    1844              : bool
    1845           72 : MSDriveWay::forwardEndOnRoute(const MSDriveWay* foe) const {
    1846           72 :     const MSEdge* foeForwardEnd = &foe->myForward.back()->getNormalPredecessorLane()->getEdge();
    1847           72 :     return std::find(myRoute.begin(), myRoute.end(), foeForwardEnd) != myRoute.end();
    1848              : }
    1849              : 
    1850              : void
    1851         2887 : MSDriveWay::addConflictLink(const MSLink* link) {
    1852         2887 :     if (link->getTLLogic() != nullptr) {
    1853              :         // ignore links that originate on myBidi
    1854              :         // and also links from the same junction as my own link
    1855         2887 :         const MSLane* origin = link->getLaneBefore();
    1856         2887 :         if (std::find(myBidi.begin(), myBidi.end(), origin) == myBidi.end()) {
    1857         2029 :             if (link->getJunction() != myRoute.front()->getFromJunction()) {
    1858         1639 :                 if (std::find(myConflictLinks.begin(), myConflictLinks.end(), link) == myConflictLinks.end()) {
    1859         1195 :                     myConflictLinks.push_back(const_cast<MSLink*>(link));
    1860              :                 }
    1861              :             }
    1862              :         }
    1863              :     }
    1864         2887 : }
    1865              : 
    1866              : void
    1867          274 : MSDriveWay::addDWDeadlock(const std::vector<const MSDriveWay*>& deadlockFoes) {
    1868              :     std::set<const MSDriveWay*> filtered;
    1869         1425 :     for (const MSDriveWay* foe : deadlockFoes) {
    1870         1151 :         if (std::find(myFoes.begin(), myFoes.end(), foe) == myFoes.end()) {
    1871              :             filtered.insert(foe);
    1872              :         }
    1873              :     }
    1874          274 :     if (std::find(myDeadlocks.begin(), myDeadlocks.end(), filtered) == myDeadlocks.end()) {
    1875           81 :         myDeadlocks.push_back(filtered);
    1876              :         //std::cout << getID() << " deadlockFoes=" << toString(deadlockFoes) << "\n";
    1877              :     }
    1878          274 : }
    1879              : 
    1880              : const MSDriveWay*
    1881        52560 : MSDriveWay::getDepartureDriveway(const SUMOVehicle* veh) {
    1882        52560 :     const MSEdge* edge = veh->getEdge();
    1883        52560 :     if (edge->getFromJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1884         3643 :         for (const MSLane* lane : edge->getLanes()) {
    1885         3985 :             for (auto ili : lane->getIncomingLanes()) {
    1886         2441 :                 const MSLink* entry = ili.viaLink->getCorrespondingEntryLink();
    1887         2441 :                 const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(entry->getTLLogic());
    1888         1739 :                 if (rs != nullptr) {
    1889         1739 :                     const MSDriveWay* dw = &const_cast<MSRailSignal*>(rs)->retrieveDriveWayForVeh(entry->getTLIndex(), veh);
    1890         1739 :                     if (&dw->myForward.front()->getEdge() == edge) {
    1891              :                         return dw;
    1892              :                     }
    1893              :                 }
    1894              :             }
    1895              :         }
    1896              :     }
    1897        53571 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    1898        50980 :         if (dw->match(veh->getCurrentRouteEdge(), veh->getRoute().end())) {
    1899              :             return dw;
    1900              :         }
    1901              :     }
    1902         5182 :     const std::string id = edge->getFromJunction()->getID() + ".d" + toString(myDepartDrivewayIndex[edge->getFromJunction()]++);
    1903         2591 :     MSDriveWay* dw = buildDriveWay(id, nullptr, veh->getCurrentRouteEdge(), veh->getRoute().end());
    1904         2591 :     myDepartureDriveways[edge].push_back(dw);
    1905         2591 :     myDepartureDrivewaysEnds[&dw->myForward.back()->getEdge()].push_back(dw);
    1906              :     dw->setVehicle(veh->getID());
    1907              :     return dw;
    1908              : }
    1909              : 
    1910              : 
    1911              : void
    1912         1063 : MSDriveWay::writeDepatureBlocks(OutputDevice& od, bool writeVehicles) {
    1913         2723 :     for (auto item  : myDepartureDriveways) {
    1914         1660 :         const MSEdge* edge = item.first;
    1915         1660 :         if (item.second.size() > 0) {
    1916         3320 :             od.openTag("departJunction");
    1917              :             od.writeAttr(SUMO_ATTR_ID, edge->getFromJunction()->getID());
    1918         3421 :             for (const MSDriveWay* dw : item.second) {
    1919         1761 :                 if (writeVehicles) {
    1920          137 :                     dw->writeBlockVehicles(od);
    1921              :                 } else {
    1922         1624 :                     dw->writeBlocks(od);
    1923              :                 }
    1924              :             }
    1925         3320 :             od.closeTag(); // departJunction
    1926              :         }
    1927              :     }
    1928         1063 : }
    1929              : 
    1930              : void
    1931          425 : MSDriveWay::saveState(OutputDevice& out) {
    1932              :     // all driveways are in myEndingDriveways which makes it convenient
    1933          463 :     for (auto item : myEndingDriveways) {
    1934           80 :         for (MSDriveWay* dw : item.second) {
    1935           42 :             dw->_saveState(out);
    1936           51 :             for (MSDriveWay* sub : dw->mySubDriveWays) {
    1937            9 :                 sub->_saveState(out);
    1938              :             }
    1939              :         }
    1940              :     }
    1941          425 : }
    1942              : 
    1943              : void
    1944           51 : MSDriveWay::_saveState(OutputDevice& out) const {
    1945           51 :     if (!myTrains.empty() || haveSubTrains()) {
    1946           37 :         out.openTag(myIsSubDriveway ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
    1947              :         out.writeAttr(SUMO_ATTR_ID, getID());
    1948           40 :         out.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
    1949           20 :         if (!myTrains.empty()) {
    1950           40 :             out.writeAttr(SUMO_ATTR_VEHICLES, toString(myTrains));
    1951              :         }
    1952           40 :         out.closeTag();
    1953              :     }
    1954           51 : }
    1955              : 
    1956              : 
    1957              : bool
    1958           31 : MSDriveWay::haveSubTrains() const {
    1959           36 :     for (MSDriveWay* sub : mySubDriveWays) {
    1960            5 :         if (!sub->myTrains.empty()) {
    1961              :             return true;
    1962              :         }
    1963              :     }
    1964              :     return false;
    1965              : }
    1966              : 
    1967              : void
    1968           29 : MSDriveWay::loadState(const SUMOSAXAttributes& attrs, int tag) {
    1969           29 :     if ((int)myDriveWayRouteLookup.size() < myGlobalDriveWayIndex) {
    1970           69 :         for (auto item : myEndingDriveways) {
    1971          110 :             for (MSDriveWay* dw : item.second) {
    1972           56 :                 myDriveWayRouteLookup[dw->myRoute] = dw;
    1973              :             }
    1974              :         }
    1975              :     }
    1976           29 :     MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
    1977              :     bool ok;
    1978           29 :     const std::string id  = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    1979           29 :     const std::string edges = attrs.get<std::string>(SUMO_ATTR_EDGES, id.c_str(), ok);
    1980              :     ConstMSEdgeVector route;
    1981           29 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
    1982           29 :         MSEdge::parseEdgesList(edges, route, id);
    1983              :     }
    1984              :     MSDriveWay* dw = nullptr;
    1985           29 :     if (tag == SUMO_TAG_DRIVEWAY) {
    1986              :         auto it = myDriveWayRouteLookup.find(route);
    1987           26 :         if (it == myDriveWayRouteLookup.end()) {
    1988              :             //WRITE_WARNING(TLF("Unknown driveWay '%' with route '%'", id, edges));
    1989              :             //return;
    1990            0 :             throw ProcessError(TLF("Unknown driveWay '%' with route '%'", id, edges));
    1991              :         }
    1992           26 :         dw = it->second;
    1993           26 :         myDriveWayLookup[id] = dw;
    1994              :     } else {
    1995            3 :         std::string parentID = id.substr(0, id.rfind('.'));
    1996              :         auto it = myDriveWayLookup.find(parentID);
    1997            3 :         if (it == myDriveWayLookup.end()) {
    1998              :             //WRITE_WARNING(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
    1999              :             //return;
    2000            0 :             throw ProcessError(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
    2001              :         }
    2002            3 :         MSDriveWay* parent = it->second;
    2003            3 :         for (MSDriveWay* sub : parent->mySubDriveWays) {
    2004            1 :             if (sub->myRoute == route) {
    2005              :                 dw = sub;
    2006              :                 break;
    2007              :             }
    2008              :         }
    2009            3 :         if (dw == nullptr) {
    2010              :             // missing subdriveways can be ignored. They may have been created
    2011              :             // as foes for driveways that are not relevant at state loading time
    2012              :             return;
    2013              :         }
    2014              :     }
    2015           54 :     const std::string vehicles = attrs.getOpt<std::string>(SUMO_ATTR_VEHICLES, id.c_str(), ok, "");
    2016           81 :     for (const std::string& vehID : StringTokenizer(vehicles).getVector()) {
    2017           27 :         MSBaseVehicle* veh = dynamic_cast<MSBaseVehicle*>(c.getVehicle(vehID));
    2018           27 :         if (veh == nullptr) {
    2019            0 :             throw ProcessError(TLF("Unknown vehicle '%' in driveway '%'", vehID, id));
    2020              :         }
    2021           27 :         dw->myTrains.insert(veh);
    2022           27 :         veh->addReminder(dw);
    2023           27 :     }
    2024           29 : }
    2025              : 
    2026              : 
    2027              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1