LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSDriveWay.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 96.1 % 790 759
Test Date: 2025-01-02 15:43:51 Functions: 94.6 % 56 53

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

Generated by: LCOV version 2.0-1