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

Generated by: LCOV version 2.0-1