LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSDriveWay.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 96.5 % 830 801
Test Date: 2026-01-01 15:49:29 Functions: 95.0 % 60 57

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2025 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        40412 : MSDriveWay::init() {
      80        40412 :     myWriteVehicles = OptionsCont::getOptions().isSet("railsignal-vehicle-output");
      81        40412 : }
      82              : 
      83              : // ===========================================================================
      84              : // MSDriveWay method definitions
      85              : // ===========================================================================
      86              : 
      87              : 
      88        20898 : MSDriveWay::MSDriveWay(const MSLink* origin, const std::string& id, bool temporary) :
      89        41796 :     MSMoveReminder("DriveWay_" + (temporary ? "tmp" : id)),
      90              :     Named(id),
      91        10032 :     myNumericalID(temporary ? -1 : myGlobalDriveWayIndex++),
      92        20898 :     myOrigin(origin),
      93        20898 :     myActive(nullptr),
      94        20898 :     myCoreSize(0),
      95        20898 :     myForwardEdgeCount(0),
      96        20898 :     myFoundSignal(false),
      97        20898 :     myFoundJump(false),
      98        20898 :     myTerminateRoute(false),
      99        20898 :     myAbortedBuild(false),
     100        20898 :     myBidiEnded(false),
     101        72726 :     myIsSubDriveway(false)
     102        20898 : {}
     103              : 
     104              : 
     105        30930 : MSDriveWay::~MSDriveWay() {
     106        22964 :     for (const MSDriveWay* sub : mySubDriveWays) {
     107         2066 :         delete sub;
     108              :     }
     109              :     mySubDriveWays.clear();
     110        93624 : }
     111              : 
     112              : void
     113        40047 : MSDriveWay::cleanup() {
     114        40047 :     myGlobalDriveWayIndex = 0;
     115              :     myBlockLengthWarnings.clear();
     116        40047 :     myWriteVehicles = false;
     117              : 
     118        42818 :     for (auto item : myDepartureDriveways) {
     119         5751 :         for (MSDriveWay* dw : item.second) {
     120         2980 :             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        40047 : }
     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        32628 : MSDriveWay::notifyEnter(SUMOTrafficObject& veh, Notification reason, const MSLane* enteredLane) {
     143              : #ifdef DEBUG_MOVEREMINDER
     144              :     std::cout << SIMTIME << " notifyEnter " << getDescription() << " veh=" << veh.getID() << " lane=" << (MSGlobals::gUseMesoSim ? veh.getEdge()->getID() : Named::getIDSecure(enteredLane)) << " reason=" << reason << "\n";
     145              : #endif
     146        65256 :     if (veh.isVehicle() && (enteredLane == myLane || (MSGlobals::gUseMesoSim && veh.getEdge() == &myLane->getEdge()))
     147        63861 :             && (reason == NOTIFICATION_DEPARTED || reason == NOTIFICATION_JUNCTION || reason == NOTIFICATION_PARKING)) {
     148        31151 :         SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     149        31151 :         MSRouteIterator firstIt = std::find(sveh.getCurrentRouteEdge(), sveh.getRoute().end(), myLane->getNextNormal());
     150        31151 :         if (match(firstIt, sveh.getRoute().end())) {
     151              :             if (myTrains.count(&sveh) == 0) {
     152        19106 :                 enterDriveWay(sveh, reason);
     153              :             }
     154        19128 :             return true;
     155              :         }
     156         1477 :     } else if (reason == NOTIFICATION_REROUTE) {
     157              :         assert(veh.isVehicle());
     158         1425 :         SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     159              :         assert(myTrains.count(&sveh) == 0);
     160         1425 :         int movedPast = matchesPastRoute(sveh);
     161              :         // vehicle must still be one the drivway
     162         1425 :         if (movedPast >= 0 && movedPast < myForwardEdgeCount) {
     163          719 :             enterDriveWay(sveh, reason);
     164          719 :             return true;
     165              :         }
     166              :     }
     167              :     return false;
     168              : }
     169              : 
     170              : 
     171              : bool
     172       105882 : MSDriveWay::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/, Notification reason, const MSLane* enteredLane) {
     173              :     UNUSED_PARAMETER(enteredLane);
     174              : #ifdef DEBUG_MOVEREMINDER
     175              :     std::cout << SIMTIME << " notifyLeave " << getDescription() << " veh=" << veh.getID() << " lane=" << Named::getIDSecure(enteredLane) << " reason=" << toString(reason) << "\n";
     176              : #endif
     177       105882 :     if (veh.isVehicle()) {
     178              :         // leaving network with departure, teleport etc
     179       105882 :         if (reason != MSMoveReminder::NOTIFICATION_JUNCTION && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     180         5786 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     181         5786 :             if (myWriteVehicles) {
     182          524 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     183              :             }
     184         5786 :             return false;
     185       100096 :         } else if (MSGlobals::gUseMesoSim && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     186              :             // notifyLeave is called before moving the route iterator
     187         7138 :             const MSLane* leftLane = (*(dynamic_cast<SUMOVehicle&>(veh).getCurrentRouteEdge()))->getLanes().front();
     188         7138 :             return notifyLeaveBack(veh, reason, leftLane);
     189              :         } else {
     190              :             return true;
     191              :         }
     192              :     } else {
     193              :         return false;
     194              :     }
     195              : }
     196              : 
     197              : 
     198              : bool
     199        71762 : MSDriveWay::notifyLeaveBack(SUMOTrafficObject& veh, Notification reason, const MSLane* leftLane) {
     200              : #ifdef DEBUG_MOVEREMINDER
     201              :     std::cout << SIMTIME << " notifyLeaveBack " << getDescription() << " veh=" << veh.getID() << " lane=" << Named::getIDSecure(leftLane) << " reason=" << toString(reason) << "\n";
     202              : #endif
     203        71762 :     if (veh.isVehicle()) {
     204        71762 :         if (leftLane == myForward.back() && (veh.getBackLane() != leftLane->getBidiLane() || MSGlobals::gUseMesoSim)) {
     205        13376 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     206        13376 :             if (myWriteVehicles) {
     207         1234 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     208              :             }
     209        13376 :             return false;
     210              :         } else {
     211        58386 :             return true;
     212              :         }
     213              :     } else {
     214              :         return false;
     215              :     }
     216              : }
     217              : 
     218              : 
     219              : bool
     220         1440 : MSDriveWay::notifyReroute(SUMOTrafficObject& veh) {
     221              : #ifdef DEBUG_MOVEREMINDER
     222              :     std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << "\n";
     223              : #endif
     224              :     assert(veh.isVehicle());
     225         1440 :     SUMOVehicle* sveh = dynamic_cast<SUMOVehicle*>(&veh);
     226              :     assert(myTrains.count(sveh) != 0);
     227         1440 :     if (matchesPastRoute(*sveh) >= 0) {
     228              :         //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " valid\n";
     229              :         return true;
     230              :     }
     231              :     // no match found, remove
     232              :     myTrains.erase(sveh);
     233          701 :     if (myWriteVehicles) {
     234           30 :         myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), NOTIFICATION_REROUTE));
     235              :     }
     236              :     //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " invalid\n";
     237              :     return false;
     238              : }
     239              : 
     240              : 
     241              : int
     242         2865 : MSDriveWay::matchesPastRoute(SUMOVehicle& sveh) const {
     243              :     // look backwards along the route to find the driveway lane
     244         2865 :     const ConstMSEdgeVector& routeEdges = sveh.getRoute().getEdges();
     245        18756 :     for (int i = sveh.getRoutePosition(); i >= 0; i--) {
     246        18338 :         if (routeEdges[i] == myLane->getNextNormal()) {
     247              :             MSRouteIterator firstIt = routeEdges.begin() + i;
     248         2447 :             if (match(firstIt, sveh.getRoute().end())) {
     249              :                 // driveway is still valid after rerouting
     250              :                 //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " valid\n";
     251         1557 :                 return sveh.getRoutePosition() - i;
     252              :             }
     253          890 :             break;
     254              :         }
     255              :     }
     256              :     return -1;
     257              : }
     258              : 
     259              : 
     260              : void
     261        19825 : MSDriveWay::enterDriveWay(SUMOVehicle& sveh, Notification reason) {
     262        19825 :     myTrains.insert(&sveh);
     263        19825 :     if (myOrigin != nullptr) {
     264        13876 :         MSRailSignalControl::getInstance().notifyApproach(myOrigin);
     265              :     }
     266        65056 :     for (const MSDriveWay* foe : myFoes) {
     267        45231 :         if (foe->myOrigin != nullptr) {
     268        36225 :             MSRailSignalControl::getInstance().notifyApproach(foe->myOrigin);
     269              :         }
     270              :     }
     271        19825 :     if (myWriteVehicles) {
     272         1780 :         myVehicleEvents.push_back(VehicleEvent(SIMSTEP, true, sveh.getID(), reason));
     273              :     }
     274        19825 : }
     275              : 
     276              : bool
     277       596841 : MSDriveWay::reserve(const Approaching& closest, MSEdgeVector& occupied) {
     278       596841 :     if (foeDriveWayOccupied(true, closest.first, occupied)) {
     279              :         return false;
     280              :     }
     281       493289 :     for (MSLink* foeLink : myConflictLinks) {
     282       135288 :         if (hasLinkConflict(closest, foeLink)) {
     283              : #ifdef DEBUG_SIGNALSTATE
     284              :             if (gDebugFlag4 || DEBUG_HELPER(closest.first)) {
     285              :                 std::cout << getID() << " linkConflict with " << getTLLinkID(foeLink) << "\n";
     286              :             }
     287              : #endif
     288              :             return false;
     289              :         }
     290              :     }
     291       358001 :     myActive = closest.first;
     292       358001 :     return true;
     293              : }
     294              : 
     295              : 
     296              : bool
     297       135288 : MSDriveWay::hasLinkConflict(const Approaching& veh, const MSLink* foeLink) const {
     298              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     299              :     if (gDebugFlag4) {
     300              :         std::cout << "   checkLinkConflict foeLink=" << getTLLinkID(foeLink) << " ego=" << Named::getIDSecure(veh.first) << "\n";
     301              :     }
     302              : #endif
     303       135288 :     if (foeLink->getApproaching().size() > 0) {
     304        44642 :         Approaching foe = foeLink->getClosest();
     305              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     306              :         if (gDebugFlag4) {
     307              :             std::cout << "     approaching foe=" << foe.first->getID() << "\n";
     308              :         }
     309              : #endif
     310        44642 :         if (foe.first == veh.first) {
     311        44642 :             return false;
     312              :         }
     313              :         const MSTrafficLightLogic* foeTLL = foeLink->getTLLogic();
     314              :         assert(foeTLL != nullptr);
     315        39831 :         const MSRailSignal* constFoeRS = dynamic_cast<const MSRailSignal*>(foeTLL);
     316              :         MSRailSignal* foeRS = const_cast<MSRailSignal*>(constFoeRS);
     317        39831 :         if (foeRS != nullptr) {
     318        39831 :             const MSDriveWay& foeDriveWay = foeRS->retrieveDriveWayForVeh(foeLink->getTLIndex(), foe.first);
     319              :             MSEdgeVector occupied;
     320        64032 :             if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied) ||
     321        40424 :                     !foeRS->constraintsAllow(foe.first) ||
     322        31876 :                     !overlap(foeDriveWay) ||
     323        55484 :                     !isFoeOrSubFoe(&foeDriveWay) ||
     324        12467 :                     canUseSiding(veh.first, &foeDriveWay).first) {
     325              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     326              :                 if (gDebugFlag4) {
     327              :                     if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied)) {
     328              :                         std::cout << "     foe blocked\n";
     329              :                     } else if (!foeRS->constraintsAllow(foe.first)) {
     330              :                         std::cout << "     foe constrained\n";
     331              :                     } else if (!overlap(foeDriveWay)) {
     332              :                         std::cout << "     no overlap\n";
     333              :                     } else if (!isFoeOrSubFoe(&foeDriveWay)) {
     334              :                         std::cout << "     foeDW=" << foeDriveWay.getID() << " is not a foe to " << getID() << "\n";
     335              :                     } else if (canUseSiding(veh.first, &foeDriveWay).first) {
     336              :                         std::cout << "     use siding\n";
     337              :                     }
     338              :                 }
     339              : #endif
     340        27502 :                 return false;
     341              :             }
     342              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     343              :             if (gDebugFlag4) {
     344              :                 std::cout
     345              :                         << "  aSB=" << veh.second.arrivalSpeedBraking << " foeASB=" << foe.second.arrivalSpeedBraking
     346              :                         << "  aT=" << veh.second.arrivalTime << " foeAT=" << foe.second.arrivalTime
     347              :                         << "  aS=" << veh.first->getSpeed() << " foeS=" << foe.first->getSpeed()
     348              :                         << "  aD=" << veh.second.dist << " foeD=" << foe.second.dist
     349              :                         << "  aW=" << veh.first->getWaitingTime() << " foeW=" << foe.first->getWaitingTime()
     350              :                         << "  aN=" << veh.first->getNumericalID() << " foeN=" << foe.first->getNumericalID()
     351              :                         << "\n";
     352              :             }
     353              : #endif
     354        12329 :             const bool yield = mustYield(veh, foe);
     355        12329 :             if (MSRailSignal::storeVehicles()) {
     356          540 :                 MSRailSignal::rivalVehicles().push_back(foe.first);
     357          540 :                 if (yield) {
     358          315 :                     MSRailSignal::priorityVehicles().push_back(foe.first);
     359              :                 }
     360              :             }
     361        12329 :             return yield;
     362        39831 :         }
     363              :     }
     364              :     return false;
     365              : }
     366              : 
     367              : 
     368              : bool
     369        25437 : MSDriveWay::isFoeOrSubFoe(const MSDriveWay* foe) const {
     370        25437 :     if (std::find(myFoes.begin(), myFoes.end(), foe) != myFoes.end()) {
     371              :         return true;
     372              :     }
     373        17491 :     for (const MSDriveWay* sub : foe->mySubDriveWays) {
     374         9784 :         if (isFoeOrSubFoe(sub)) {
     375              :             return true;
     376              :         }
     377              :     }
     378              :     return false;
     379              : }
     380              : 
     381              : 
     382              : bool
     383        12329 : MSDriveWay::mustYield(const Approaching& veh, const Approaching& foe) {
     384        12329 :     if (foe.second.arrivalSpeedBraking == veh.second.arrivalSpeedBraking) {
     385         7954 :         if (foe.second.arrivalTime == veh.second.arrivalTime) {
     386          556 :             if (foe.first->getSpeed() == veh.first->getSpeed()) {
     387          536 :                 if (foe.second.dist == veh.second.dist) {
     388          500 :                     if (foe.first->getWaitingTime() == veh.first->getWaitingTime()) {
     389          482 :                         return foe.first->getNumericalID() < veh.first->getNumericalID();
     390              :                     } else {
     391           18 :                         return foe.first->getWaitingTime() > veh.first->getWaitingTime();
     392              :                     }
     393              :                 } else {
     394           36 :                     return foe.second.dist < veh.second.dist;
     395              :                 }
     396              :             } else {
     397           20 :                 return foe.first->getSpeed() > veh.first->getSpeed();
     398              :             }
     399              :         } else {
     400         7398 :             return foe.second.arrivalTime < veh.second.arrivalTime;
     401              :         }
     402              :     } else {
     403         4375 :         return foe.second.arrivalSpeedBraking > veh.second.arrivalSpeedBraking;
     404              :     }
     405              : }
     406              : 
     407              : 
     408              : bool
     409        16035 : MSDriveWay::conflictLaneOccupied(bool store, const SUMOVehicle* ego) const {
     410       210513 :     for (const MSLane* lane : myConflictLanes) {
     411              :         if (!lane->isEmpty()) {
     412         4410 :             std::string joinVehicle = "";
     413         4410 :             if (ego != nullptr && !MSGlobals::gUseMesoSim) {
     414            0 :                 const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
     415            0 :                 if (stop != nullptr) {
     416            0 :                     joinVehicle = stop->join;
     417              :                 }
     418              :             }
     419              : #ifdef DEBUG_SIGNALSTATE
     420              :             if (gDebugFlag4) {
     421              :                 std::cout << SIMTIME << " conflictLane " << lane->getID() << " occupied ego=" << Named::getIDSecure(ego) << " vehNumber=" << lane->getVehicleNumber() << "\n";
     422              :                 if (joinVehicle != "") {
     423              :                     std::cout << "  joinVehicle=" << joinVehicle << " occupant=" << toString(lane->getVehiclesSecure()) << "\n";
     424              :                     lane->releaseVehicles();
     425              :                 }
     426              :             }
     427              : #endif
     428         4410 :             if (lane->getVehicleNumberWithPartials() == 1) {
     429         4410 :                 MSVehicle* foe = lane->getLastAnyVehicle();
     430         4410 :                 if (joinVehicle != "") {
     431            0 :                     if (foe->getID() == joinVehicle && foe->isStopped()) {
     432              : #ifdef DEBUG_SIGNALSTATE
     433              :                         if (gDebugFlag4) {
     434              :                             std::cout << "    ignore join-target '" << joinVehicle << "\n";
     435              :                         }
     436              : #endif
     437            0 :                         continue;
     438              :                     }
     439              :                 }
     440         4410 :                 if (ego != nullptr) {
     441            0 :                     if (foe == ego && std::find(myForward.begin(), myForward.end(), lane) == myForward.end()) {
     442              : #ifdef DEBUG_SIGNALSTATE
     443              :                         if (gDebugFlag4) {
     444              :                             std::cout << "    ignore ego as oncoming '" << ego->getID() << "\n";
     445              :                         }
     446              : #endif
     447            0 :                         continue;
     448              :                     }
     449            0 :                     if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
     450              : #ifdef DEBUG_SIGNALSTATE
     451              :                         if (gDebugFlag4) {
     452              :                             std::cout << "    ignore " << foe->getID() << " for which ego is join-target\n";
     453              :                         }
     454              : #endif
     455            0 :                         continue;
     456              :                     }
     457              :                 }
     458              :             }
     459         4410 :             if (MSRailSignal::storeVehicles() && store) {
     460         4410 :                 MSRailSignal::blockingVehicles().push_back(lane->getLastAnyVehicle());
     461              :             }
     462              :             return true;
     463              :         }
     464              :     }
     465        11625 :     return false;
     466              : }
     467              : 
     468              : 
     469              : bool
     470      7122004 : MSDriveWay::foeDriveWayApproached() const {
     471     14351308 :     for (const MSDriveWay* foeDW : myFoes) {
     472     14289132 :         if (foeDW->myOrigin != nullptr && foeDW->myOrigin->getApproaching().size() > 0) {
     473              : #ifdef DEBUG_SIGNALSTATE
     474              :             if (gDebugFlag4) {
     475              :                 std::cout << SIMTIME << " foeLink=" << foeDW->myOrigin->getDescription() << " approachedBy=" << foeDW->myOrigin->getApproaching().begin()->first->getID() << "\n";
     476              :             }
     477              : #endif
     478              :             return true;
     479              :         }
     480              :     }
     481              :     return false;
     482              : }
     483              : 
     484              : 
     485              : bool
     486      8382952 : MSDriveWay::foeDriveWayOccupied(bool store, const SUMOVehicle* ego, MSEdgeVector& occupied) const {
     487     24490957 :     for (const MSDriveWay* foeDW : myFoes) {
     488     16985689 :         if (!foeDW->myTrains.empty()) {
     489              : #ifdef DEBUG_SIGNALSTATE
     490              :             if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
     491              :                 std::cout << SIMTIME << " " << getID() << " foeDriveWay " << foeDW->getID() << " occupied ego=" << Named::getIDSecure(ego) << " foeVeh=" << toString(foeDW->myTrains) << "\n";
     492              :             }
     493              : #endif
     494       903642 :             if (foeDW->myTrains.size() == 1) {
     495       902488 :                 SUMOVehicle* foe = *foeDW->myTrains.begin();
     496       902488 :                 if (foe == ego) {
     497              : #ifdef DEBUG_SIGNALSTATE
     498              :                     if (gDebugFlag4 || DEBUG_HELPER(ego)) {
     499              :                         std::cout << "    ignore ego as foe '" << Named::getIDSecure(ego) << "\n";
     500              :                     }
     501              : #endif
     502        26046 :                     continue;
     503              :                 }
     504       881689 :                 if (hasJoin(ego, foe)) {
     505           36 :                     continue;
     506              :                 }
     507              :             }
     508       882807 :             std::pair<bool, const MSDriveWay*> useSiding = canUseSiding(ego, foeDW);
     509              : #ifdef DEBUG_SIGNALSTATE
     510              :             if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
     511              :                 auto it = mySidings.find(foeDW);
     512              :                 int numSidings = 0;
     513              :                 if (it != mySidings.end()) {
     514              :                     numSidings = it->second.size();
     515              :                 }
     516              :                 std::cout << "  useSiding=" << useSiding.first << " sidingFoe=" << Named::getIDSecure(useSiding.second) << " numSidings=" << numSidings << "\n";
     517              :             }
     518              : #endif
     519       882807 :             if (useSiding.first) {
     520         5211 :                 continue;
     521              :             } else {
     522       877596 :                 if (MSRailSignal::storeVehicles() && store) {
     523          740 :                     for (SUMOVehicle* foe : foeDW->myTrains) {
     524          370 :                         MSRailSignal::blockingVehicles().push_back(foe);
     525              :                     }
     526          370 :                     MSRailSignal::blockingDriveWays().push_back(foeDW);
     527              :                 }
     528      1756578 :                 for (const SUMOVehicle* foe : foeDW->myTrains) {
     529       878982 :                     occupied.push_back(const_cast<MSEdge*>(foe->getEdge()));
     530       878982 :                     MSEdge* bidi = const_cast<MSEdge*>(foe->getEdge()->getBidiEdge());
     531       878982 :                     if (bidi != nullptr) {
     532       478107 :                         occupied.push_back(bidi);
     533              :                     }
     534              :                     /// @todo: if foe occupies more than one edge we should add all of them to the occupied vector
     535              :                 }
     536       287924 :                 if (ego != nullptr && MSGlobals::gTimeToTeleportRSDeadlock > 0
     537      1120094 :                         && (ego->getWaitingTime() > ego->getVehicleType().getCarFollowModel().getStartupDelay() || !ego->isOnRoad())) {
     538              :                     // if there is an occupied siding, it becomes part of the waitRelation
     539       136196 :                     SUMOVehicle* foe = *(useSiding.second == nullptr ? foeDW : useSiding.second)->myTrains.begin();
     540       136196 :                     const MSRailSignal* rs = myOrigin != nullptr ? dynamic_cast<const MSRailSignal*>(myOrigin->getTLLogic()) : nullptr;
     541       136196 :                     MSRailSignalControl::getInstance().addWaitRelation(ego, rs, foe);
     542              :                 }
     543       877596 :                 return true;
     544              :             }
     545     16082047 :         } else if (foeDW != this && isDepartDriveway() && !foeDW->isDepartDriveway()) {
     546         9138 :             if (foeDW->myOrigin->getApproaching().size() > 0) {
     547         1601 :                 Approaching foeA = foeDW->myOrigin->getClosest();
     548         1601 :                 const SUMOVehicle* foe = foeA.first;
     549         1601 :                 if (foeA.second.dist < foe->getBrakeGap(true)) {
     550          123 :                     MSRouteIterator firstIt = std::find(foe->getCurrentRouteEdge(), foe->getRoute().end(), foeDW->myRoute.front());
     551          123 :                     if (firstIt != foe->getRoute().end()) {
     552          123 :                         if (foeDW->match(firstIt, foe->getRoute().end())) {
     553           88 :                             bool useSiding = canUseSiding(ego, foeDW).first;
     554              : #ifdef DEBUG_SIGNALSTATE
     555              :                             if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
     556              :                                 std::cout << SIMTIME << " " << getID() << " blocked by " << foeDW->getID() << " (approached by " << foe->getID() << ") useSiding=" << useSiding << "\n";
     557              :                             }
     558              : #endif
     559           88 :                             if (useSiding) {
     560              :                                 //std::cout << SIMTIME << " " << getID() << " ego=" << ego->getID() << " foeDW=" << foeDW->getID() << " myFoes=" << toString(myFoes) << "\n";
     561            0 :                                 continue;
     562              :                             } else {
     563           88 :                                 return true;
     564              :                             }
     565              :                         }
     566              :                     }
     567              :                 }
     568              :             }
     569              :         }
     570              :     }
     571      7506238 :     for (const std::set<const MSDriveWay*>& dlFoes : myDeadlocks) {
     572              :         bool allOccupied = true;
     573        10051 :         for (const MSDriveWay* dlFoe : dlFoes) {
     574         7395 :             if (dlFoe->myTrains.empty()) {
     575              :                 allOccupied = false;
     576              :                 //std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << "  deadlockCheck clear " << dlFoe->getID() << "\n";
     577              :                 break;
     578              :             }
     579              :         }
     580         3626 :         if (allOccupied) {
     581              : #ifdef DEBUG_SIGNALSTATE
     582              :             if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
     583              :                 std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " deadlockCheck " << joinNamedToString(dlFoes, " ") << "\n";
     584              :             }
     585              : #endif
     586         8645 :             for (const MSDriveWay* dlFoe : dlFoes) {
     587         5989 :                 MSRailSignal::blockingDriveWays().push_back(dlFoe);
     588              :             }
     589              :             return true;
     590              :         }
     591              :     }
     592              :     return false;
     593              : }
     594              : 
     595              : 
     596              : bool
     597       881689 : MSDriveWay::hasJoin(const SUMOVehicle* ego, const SUMOVehicle* foe) {
     598       881689 :     if (ego != nullptr && !MSGlobals::gUseMesoSim) {
     599       225716 :         std::string joinVehicle = "";
     600       225716 :         const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
     601       225716 :         if (stop != nullptr) {
     602        91087 :             joinVehicle = stop->join;
     603              :         }
     604       225716 :         if (joinVehicle == "" && !ego->hasDeparted() && ego->getStops().size() > 1) {
     605              :             // check one more stop
     606         5968 :             auto it = ego->getStops().begin();
     607              :             std::advance(it, 1);
     608         5968 :             joinVehicle = it->pars.join;
     609              :         }
     610       225716 :         if (joinVehicle != "") {
     611              : #ifdef DEBUG_SIGNALSTATE
     612              :             if (gDebugFlag4 || DEBUG_COND_DW) {
     613              :                 std::cout << "  joinVehicle=" << joinVehicle << "\n";
     614              :             }
     615              : #endif
     616          345 :             if (foe->getID() == joinVehicle && foe->isStopped()) {
     617              : #ifdef DEBUG_SIGNALSTATE
     618              :                 if (gDebugFlag4 || DEBUG_COND_DW) {
     619              :                     std::cout << "    ignore join-target '" << joinVehicle << "\n";
     620              :                 }
     621              : #endif
     622              :                 return true;
     623              :             }
     624              :         }
     625              : 
     626       225692 :         if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
     627              : #ifdef DEBUG_SIGNALSTATE
     628              :             if (gDebugFlag4 || DEBUG_COND_DW) {
     629              :                 std::cout << "    ignore " << foe->getID() << " for which ego is join-target\n";
     630              :             }
     631              : #endif
     632              :             return true;
     633              :         }
     634              :     }
     635              :     return false;
     636              : }
     637              : 
     638              : 
     639              : std::pair<bool, const MSDriveWay*>
     640       899151 : MSDriveWay::canUseSiding(const SUMOVehicle* ego, const MSDriveWay* foe, bool recurse) const {
     641              :     auto it = mySidings.find(foe);
     642       899151 :     if (it != mySidings.end()) {
     643        14605 :         for (auto siding : it->second) {
     644              :             // assume siding is usuable when computing state for unapproached signal (ego == nullptr)
     645        13813 :             if (ego == nullptr || siding.length >= ego->getLength()) {
     646              :                 // if the siding is already "reserved" by another vehicle we cannot use it here
     647        10462 :                 const MSEdge* sidingEnd = myRoute[siding.end];
     648        23810 :                 for (MSDriveWay* sidingApproach : myEndingDriveways[sidingEnd]) {
     649        18155 :                     if (!sidingApproach->myTrains.empty()) {
     650              :                         // possibly the foe vehicle can use the other part of the siding
     651         5113 :                         if (recurse) {
     652              :                             const SUMOVehicle* foeVeh = nullptr;
     653         3789 :                             if (!foe->myTrains.empty()) {
     654         3775 :                                 foeVeh = *foe->myTrains.begin();
     655           14 :                             } else if (foe->myOrigin != nullptr && foe->myOrigin->getApproaching().size() > 0) {
     656           14 :                                 foeVeh = foe->myOrigin->getClosest().first;
     657              :                             }
     658         3789 :                             if (foeVeh == nullptr) {
     659            0 :                                 WRITE_WARNINGF("Invalid call to canUseSiding dw=% foe=% ego=% time=%", getID(), foe->getID(), Named::getIDSecure(ego), time2string(SIMSTEP));
     660            0 :                                 continue;
     661              :                             }
     662         3789 :                             if (foe->canUseSiding(foeVeh, this, false).first) {
     663          306 :                                 continue;
     664              :                             }
     665              :                         }
     666              :                         // possibly the foe vehicle
     667              :                         // @todo: in principle it might still be possible to continue if vehicle that approaches the siding can safely leave the situation
     668              : #ifdef DEBUG_SIGNALSTATE
     669              :                         if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
     670              :                             std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
     671              :                                       << " foeVeh=" << toString(foe->myTrains)
     672              :                                       << " sidingEnd=" << sidingEnd->getID() << " sidingApproach=" << sidingApproach->getID() << " approaching=" << toString(sidingApproach->myTrains) << "\n";
     673              :                         }
     674              : #endif
     675         4807 :                         return std::make_pair(false, sidingApproach);
     676              :                     }
     677              :                 }
     678              :                 //std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
     679              :                 //    << " foeVeh=" << toString(foe->myTrains)
     680              :                 //    << " sidingEnd=" << sidingEnd->getID() << "usable\n";
     681         5655 :                 return std::make_pair(true, nullptr);
     682              :             }
     683              :         }
     684              :     }
     685       888689 :     return std::make_pair(false, nullptr);
     686              : }
     687              : 
     688              : bool
     689        19954 : MSDriveWay::overlap(const MSDriveWay& other) const {
     690        32736 :     for (int i = 0; i < myCoreSize; i++) {
     691       373463 :         for (int j = 0; j < other.myCoreSize; j++) {
     692       360681 :             const MSEdge* edge = myRoute[i];
     693       360681 :             const MSEdge* edge2 = other.myRoute[j];
     694              :             if (edge->getToJunction() == edge2->getToJunction()
     695       360681 :                     || edge->getToJunction() == edge2->getFromJunction()) {
     696              :                 // XXX might be rail_crossing with parallel tracks
     697              :                 return true;
     698              :             }
     699              :         }
     700              :     }
     701              :     return false;
     702              : }
     703              : 
     704              : 
     705              : bool
     706        73205 : MSDriveWay::flankConflict(const MSDriveWay& other) const {
     707       243085 :     for (const MSLane* lane : myForward) {
     708       738198 :         for (const MSLane* lane2 : other.myForward) {
     709       562938 :             if (lane == lane2) {
     710              :                 return true;
     711              :             }
     712              :         }
     713      2389573 :         for (const MSLane* lane2 : other.myBidi) {
     714      2216999 :             if (lane == lane2) {
     715         5258 :                 if (bidiBlockedBy(other)) {
     716              :                     // it's only a deadlock if both trains block symmetrically
     717              :                     return true;
     718              :                 }
     719              :             }
     720              :         }
     721      6082378 :         for (const MSLane* lane2 : other.myBidiExtended) {
     722      5912498 :             if (lane == lane2) {
     723        20270 :                 if (bidiBlockedBy(other)) {
     724              :                     // it's only a deadlock if both trains block symmetrically
     725              :                     return true;
     726              :                 }
     727              :             }
     728              :         }
     729              :     }
     730              :     return false;
     731              : }
     732              : 
     733              : 
     734              : bool
     735        62651 : MSDriveWay::crossingConflict(const MSDriveWay& other) const {
     736       221759 :     for (const MSLane* lane : myForward) {
     737       642230 :         for (const MSLane* lane2 : other.myForward) {
     738       483122 :             if (lane->isNormal() && lane2->isNormal() && lane->getEdge().getToJunction() == lane2->getEdge().getToJunction()) {
     739              :                 return true;
     740              :             }
     741              :         }
     742              :     }
     743              :     return false;
     744              : }
     745              : 
     746              : 
     747              : bool
     748        25868 : MSDriveWay::bidiBlockedBy(const MSDriveWay& other) const {
     749       417492 :     for (const MSLane* lane : myBidi) {
     750      1376143 :         for (const MSLane* lane2 : other.myForward) {
     751       984519 :             if (lane == lane2) {
     752              :                 return true;
     753              :             }
     754              :         }
     755              :     }
     756       442320 :     for (const MSLane* lane : myBidiExtended) {
     757      1434676 :         for (const MSLane* lane2 : other.myForward) {
     758      1014995 :             if (lane == lane2) {
     759         2250 :                 if (overlap(other)) {
     760              :                     return true;
     761              :                 }
     762              :             }
     763              :         }
     764              :     }
     765              :     return false;
     766              : }
     767              : 
     768              : 
     769              : bool
     770         8018 : MSDriveWay::bidiBlockedByEnd(const MSDriveWay& other) const {
     771         8018 :     const MSLane* end = other.myForward.back();
     772        63295 :     for (const MSLane* lane : myBidi) {
     773        58044 :         if (lane == end) {
     774              :             return true;
     775              :         }
     776              :     }
     777        41835 :     for (const MSLane* lane : myBidiExtended) {
     778        38065 :         if (lane == end) {
     779         1481 :             if (overlap(other)) {
     780              :                 return true;
     781              :             }
     782              :         }
     783              :     }
     784              :     return false;
     785              : }
     786              : 
     787              : bool
     788         5769 : MSDriveWay::forwardRouteConflict(std::set<const MSEdge*> forward, const MSDriveWay& other, bool secondCheck) {
     789              :     int i = 0;
     790        62373 :     for (const MSEdge* edge2 : other.myRoute) {
     791        61571 :         if (i == other.myCoreSize) {
     792              :             return false;
     793              :         }
     794        61571 :         i++;
     795        61571 :         if (edge2 == myForward.front()->getNextNormal() && !secondCheck) {
     796              :             // foe should not pass from behind through our own forward section
     797              :             return false;
     798              :         }
     799              :         if (forward.count(edge2->getBidiEdge()) != 0) {
     800              :             return true;
     801              :         }
     802              :     }
     803              :     return false;
     804              : }
     805              : 
     806              : void
     807         8057 : MSDriveWay::writeBlocks(OutputDevice& od) const {
     808        14236 :     od.openTag(myIsSubDriveway ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
     809         8057 :     od.writeAttr(SUMO_ATTR_ID, myID);
     810         8057 :     od.writeAttr(SUMO_ATTR_VEHICLE, myFirstVehicle);
     811         8057 :     od.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
     812         8057 :     if (myCoreSize != (int)myRoute.size()) {
     813            0 :         od.writeAttr("core", myCoreSize);
     814              :     }
     815         8057 :     od.openTag("forward");
     816         8057 :     od.writeAttr(SUMO_ATTR_LANES, toString(myForward));
     817         8057 :     od.closeTag();
     818         8057 :     if (!myIsSubDriveway) {
     819         6179 :         od.openTag("bidi");
     820        12358 :         od.writeAttr(SUMO_ATTR_LANES, toString(myBidi));
     821         6179 :         if (myBidiExtended.size() > 0) {
     822         1951 :             od.lf();
     823         1951 :             od << "                   ";
     824         3902 :             od.writeAttr("deadlockCheck", toString(myBidiExtended));
     825              :         }
     826         6179 :         od.closeTag();
     827         6179 :         od.openTag("flank");
     828         6179 :         od.writeAttr(SUMO_ATTR_LANES, toString(myFlank));
     829         6179 :         od.closeTag();
     830              : 
     831        12358 :         od.openTag("conflictLinks");
     832              : 
     833              :         std::vector<std::string> signals;
     834         9887 :         for (MSLink* link : myConflictLinks) {
     835         7416 :             signals.push_back(getTLLinkID(link));
     836              :         }
     837        12358 :         od.writeAttr("signals", joinToStringSorting(signals, " "));
     838        12358 :         od.closeTag();
     839              : 
     840              :         std::vector<std::string> foes;
     841        23006 :         for (MSDriveWay* dw : myFoes) {
     842        16827 :             foes.push_back(dw->myID);
     843              :         }
     844         6179 :         if (foes.size() > 0) {
     845         6144 :             od.openTag("foes");
     846        12288 :             od.writeAttr("driveWays", joinToStringSorting(foes, " "));
     847        12288 :             od.closeTag();
     848              :         }
     849         6795 :         for (auto item : mySidings) {
     850          616 :             od.openTag("sidings");
     851         1232 :             od.writeAttr("foe", item.first->getID());
     852         3163 :             for (auto siding : item.second) {
     853         2547 :                 od.openTag("siding");
     854         2547 :                 od.writeAttr("start", myRoute[siding.start]->getID());
     855         2547 :                 od.writeAttr("end", myRoute[siding.end]->getID());
     856         2547 :                 od.writeAttr("length", siding.length);
     857         5094 :                 od.closeTag();
     858              :             }
     859         1232 :             od.closeTag();
     860              :         }
     861         6260 :         for (auto item : myDeadlocks) {
     862           81 :             od.openTag("deadlock");
     863          162 :             od.writeAttr("foes", joinNamedToStringSorting(item, " "));
     864          162 :             od.closeTag();
     865              :         }
     866         6179 :     }
     867        16114 :     od.closeTag(); // driveWay
     868              : 
     869         9935 :     for (const MSDriveWay* sub : mySubDriveWays) {
     870         1878 :         sub->writeBlocks(od);
     871              :     }
     872              : #ifdef DRIVEWAY_SANITY_CHECK
     873         8057 :     std::set<MSDriveWay*> uFoes(myFoes.begin(), myFoes.end());
     874         8057 :     if (uFoes.size() != myFoes.size()) {
     875            0 :         WRITE_WARNINGF("Duplicate foes in driveway '%'", getID());
     876              : 
     877              :     }
     878              : #endif
     879         8057 : }
     880              : 
     881              : 
     882              : void
     883          765 : MSDriveWay::writeBlockVehicles(OutputDevice& od) const {
     884         1351 :     od.openTag(myIsSubDriveway ? "subDriveWay" : "driveWay");
     885          765 :     od.writeAttr(SUMO_ATTR_ID, myID);
     886         2556 :     for (const VehicleEvent& ve : myVehicleEvents) {
     887         2685 :         od.openTag(ve.isEntry ? "entry" : "exit");
     888         1791 :         od.writeAttr(SUMO_ATTR_ID, ve.id);
     889         1791 :         od.writeAttr(SUMO_ATTR_TIME, time2string(ve.time));
     890         1791 :         od.writeAttr("reason", Notifications.getString(ve.reason));
     891         3582 :         od.closeTag(); // event
     892              :     }
     893         1530 :     od.closeTag(); // driveWay
     894              : 
     895          944 :     for (const MSDriveWay* sub : mySubDriveWays) {
     896          179 :         sub->writeBlockVehicles(od);
     897              :     }
     898          765 : }
     899              : 
     900              : 
     901              : void
     902         7966 : MSDriveWay::buildRoute(const MSLink* origin,
     903              :                        MSRouteIterator next, MSRouteIterator end,
     904              :                        LaneVisitedMap& visited,
     905              :                        std::set<MSLink*>& flankSwitches) {
     906         7966 :     double length = 0;
     907              :     bool seekForwardSignal = true;
     908              :     bool seekBidiSwitch = true;
     909              :     bool foundUnsafeSwitch = false;
     910         7966 :     MSLane* toLane = origin ? origin->getViaLaneOrLane() : (*next)->getLanes()[0];
     911        20918 :     const std::string warnID = origin ? "rail signal " + getClickableTLLinkID(origin) : "insertion lane '" + toLane->getID() + "'";
     912              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     913              :     gDebugFlag4 = DEBUG_COND_DW;
     914              :     if (gDebugFlag4) std::cout << "buildRoute origin=" << warnID << " vehRoute=" << toString(ConstMSEdgeVector(next, end))
     915              :                                    << " visited=" << formatVisitedMap(visited) << "\n";
     916              : #endif
     917              :     while (true) {
     918        98601 :         if (length > MSGlobals::gMaxRailSignalBlockLength) {
     919              :             // typical block length in germany on main lines is 3-5km on branch lines up to 7km
     920              :             // special branches that are used by one train exclusively could also be up to 20km in length
     921              :             // minimum block size in germany is 37.5m (LZB)
     922              :             // larger countries (USA, Russia) might see blocks beyond 20km)
     923          210 :             if (seekForwardSignal && myBlockLengthWarnings.count(myRoute.front()) == 0) {
     924          186 :                 WRITE_WARNINGF("Block after % exceeds maximum length (stopped searching after edge '%' (length=%m).",
     925              :                                warnID, toLane->getEdge().getID(), length);
     926              :                 myBlockLengthWarnings.insert(myRoute.front());
     927              :             }
     928          210 :             myAbortedBuild = true;
     929              :             // length exceeded
     930              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     931              :             if (gDebugFlag4) {
     932              :                 std::cout << " abort: length=" << length << "\n";
     933              :             }
     934              : #endif
     935         7966 :             return;
     936              :         }
     937              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     938              :         if (gDebugFlag4) {
     939              :             std::cout << "   toLane=" << toLane->getID() << " visited=" << formatVisitedMap(visited) << "\n";
     940              :         }
     941              : #endif
     942        98391 :         const MSEdge* current = &toLane->getEdge();
     943        98391 :         if (current->isNormal()) {
     944        61534 :             myRoute.push_back(current);
     945        61534 :             if (next != end) {
     946              :                 next++;
     947              :             }
     948              :         }
     949        98391 :         appendMapIndex(visited, toLane);
     950        98391 :         length += toLane->getLength();
     951        98391 :         MSLane* bidi = toLane->getBidiLane();
     952        98391 :         if (seekForwardSignal) {
     953        26083 :             if (!foundUnsafeSwitch) {
     954        26083 :                 myForward.push_back(toLane);
     955        26083 :                 if (toLane->isNormal()) {
     956        17373 :                     myForwardEdgeCount++;
     957              :                 }
     958        26083 :                 if (myForward.size() == 1) {
     959         7966 :                     myLane = toLane;
     960         7966 :                     if (MSGlobals::gUseMesoSim) {
     961         2348 :                         MESegment* s = MSGlobals::gMesoNet->getSegmentForEdge(myLane->getEdge());
     962         2348 :                         s->addDetector(this, myLane->getIndex());
     963              :                     } else {
     964         5618 :                         toLane->addMoveReminder(this, false);
     965              :                     }
     966              :                 }
     967              :             }
     968        72308 :         } else if (bidi == nullptr) {
     969        22373 :             if (toLane->isInternal() && toLane->getIncomingLanes().front().viaLink->isTurnaround()) {
     970              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     971              :                 if (gDebugFlag4) {
     972              :                     std::cout << "      continue bidiSearch beyond turnaround\n";
     973              :                 }
     974              : #endif
     975              :             } else {
     976              :                 seekBidiSwitch = false;
     977              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     978              :                 if (gDebugFlag4) {
     979              :                     std::cout << "      noBidi, abort search for bidiSwitch\n";
     980              :                 }
     981              : #endif
     982              :             }
     983              :         }
     984        98391 :         if (bidi != nullptr) {
     985        64715 :             if (!seekForwardSignal && !foundUnsafeSwitch && bidi->isNormal()) {
     986              :                 // look for switch that could protect from oncoming vehicles
     987        23200 :                 for (const MSLink* const link : bidi->getLinkCont()) {
     988        13015 :                     if (link->getDirection() == LinkDirection::TURN) {
     989          531 :                         continue;
     990              :                     }
     991        24806 :                     if (!myBidi.empty() && link->getViaLaneOrLane() != myBidi.back()) {
     992         2207 :                         myCoreSize = (int)myRoute.size() - 1;
     993         2207 :                         MSLink* used = const_cast<MSLink*>(bidi->getLinkTo(myBidi.back()));
     994              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     995              :                         if (gDebugFlag4) {
     996              :                             std::cout << "      found unsafe switch " << link->getDescription() << " (used=" << (used == nullptr ? "NULL" : used->getDescription()) << ")\n";
     997              :                         }
     998              : #endif
     999              :                         // trains along our route beyond this switch might create deadlock
    1000              :                         foundUnsafeSwitch = true;
    1001              :                         // the switch itself must still be guarded to ensure safety
    1002         2207 :                         if (used != nullptr) {
    1003              :                             // possibly nullptr if there was an intermediate section of unidirectional edges
    1004              :                             flankSwitches.insert(used);
    1005              :                         }
    1006              :                         break;
    1007              :                     }
    1008              :                 }
    1009              :             }
    1010        62508 :             if (foundUnsafeSwitch) {
    1011        32533 :                 myBidiExtended.push_back(bidi);
    1012              :             } else {
    1013        32182 :                 myBidi.push_back(bidi);
    1014              :             }
    1015              :         }
    1016        98391 :         const std::vector<MSLink*>& links = toLane->getLinkCont();
    1017        98391 :         toLane = nullptr;
    1018       104754 :         for (const MSLink* const link : links) {
    1019        94798 :             if ((next != end && &link->getLane()->getEdge() == *next)
    1020       188376 :                     && isRailwayOrShared(link->getViaLaneOrLane()->getPermissions())) {
    1021       147489 :                 toLane = link->getViaLaneOrLane();
    1022        90991 :                 if (link->getTLLogic() != nullptr && link->getTLIndex() >= 0) {
    1023        26383 :                     if (link == origin) {
    1024          356 :                         if (seekForwardSignal) {
    1025            0 :                             WRITE_WARNINGF(TL("Found circular block after % (% edges, length %)"), warnID, toString(myRoute.size()), toString(length));
    1026              :                         }
    1027              :                         //std::cout << getClickableTLLinkID(origin) << " circularBlock2=" << toString(myRoute) << "\n";
    1028          356 :                         myAbortedBuild = true;
    1029              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1030              :                         if (gDebugFlag4) {
    1031              :                             std::cout << " abort: found circle\n";
    1032              :                         }
    1033              : #endif
    1034          356 :                         return;
    1035              :                     }
    1036              :                     seekForwardSignal = false;
    1037        26027 :                     myFoundSignal = true;
    1038              :                     seekBidiSwitch = bidi != nullptr;
    1039              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1040              :                     if (gDebugFlag4) {
    1041              :                         std::cout << "      found forwardSignal " << link->getTLLogic()->getID() << " seekBidiSwitch=" << seekBidiSwitch << "\n";
    1042              :                     }
    1043              : #endif
    1044              :                 }
    1045              :                 //if (links.size() > 1 && !foundUnsafeSwitch) {
    1046        90635 :                 if (isSwitch(link)) {
    1047              :                     // switch on driveway
    1048              :                     //std::cout << "mySwitchDriveWays " << getID() << " link=" << link->getDescription() << "\n";
    1049        51635 :                     mySwitchDriveWays[link].push_back(this);
    1050              :                 }
    1051        90635 :                 if (link->getLane()->getBidiLane() != nullptr && &link->getLane()->getEdge() == current->getBidiEdge()) {
    1052              :                     // reversal on driveway
    1053         1380 :                     myReversalDriveWays[current].push_back(this);
    1054         1380 :                     myReversals.push_back(current);
    1055              :                 }
    1056              :                 break;
    1057              :             }
    1058              :         }
    1059        98035 :         if (toLane == nullptr) {
    1060         7400 :             if (next != end) {
    1061              :                 // no connection found, jump to next route edge
    1062              :                 toLane = (*next)->getLanes()[0];
    1063              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1064              :                 if (gDebugFlag4) {
    1065              :                     std::cout << "      abort: turn-around or jump\n";
    1066              :                 }
    1067              : #endif
    1068           87 :                 myFoundJump = true;
    1069           87 :                 return;
    1070              :             } else {
    1071              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1072              :                 if (gDebugFlag4) {
    1073              :                     std::cout << "      abort: no next lane available\n";
    1074              :                 }
    1075              : #endif
    1076         7313 :                 myTerminateRoute = true;
    1077         7313 :                 return;
    1078              :             }
    1079              :         }
    1080        90635 :     }
    1081              :     myBidiEnded = !seekBidiSwitch;
    1082              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1083              :     if (gDebugFlag4) {
    1084              :         std::cout << " normalEnd myBidiEnded=" << myBidiEnded << "\n";
    1085              :     }
    1086              : #endif
    1087              : }
    1088              : 
    1089              : 
    1090              : bool
    1091       101505 : MSDriveWay::isSwitch(const MSLink* link) {
    1092       196633 :     for (const MSLink* other : link->getLaneBefore()->getNormalPredecessorLane()->getLinkCont()) {
    1093       111208 :         if (other->getLane() != link->getLane() && !other->isTurnaround()) {
    1094              :             return true;
    1095              :         }
    1096              :     }
    1097       138955 :     for (auto ili : link->getLane()->getIncomingLanes()) {
    1098        97465 :         if (ili.viaLink != link && !ili.viaLink->isTurnaround()) {
    1099              :             return true;
    1100              :         }
    1101              :     }
    1102        41490 :     const MSLane* bidi = link->getLane()->getBidiLane();
    1103        41490 :     if (bidi != nullptr) {
    1104        62974 :         for (const MSLink* other : bidi->getLinkCont()) {
    1105        32588 :             if (other->getLane() != link->getLaneBefore()->getNormalPredecessorLane()->getBidiLane() && !other->isTurnaround()) {
    1106              :                 return true;
    1107              :             }
    1108              :         }
    1109              :     }
    1110              :     return false;
    1111              : }
    1112              : 
    1113              : 
    1114              : void
    1115        31864 : MSDriveWay::checkFlanks(const MSLink* originLink, const std::vector<const MSLane*>& lanes, const LaneVisitedMap& visited, bool allFoes, std::set<MSLink*>& flankSwitches) const {
    1116              : #ifdef DEBUG_CHECK_FLANKS
    1117              :     std::cout << " checkFlanks lanes=" << toString(lanes) << " allFoes=" << allFoes << "\n";
    1118              : #endif
    1119        19944 :     const MSLink* reverseOriginLink = originLink != nullptr && originLink->getLane()->getBidiLane() != nullptr && originLink->getLaneBefore()->getBidiLane() != nullptr
    1120        42568 :                                       ? originLink->getLane()->getBidiLane()->getLinkTo(originLink->getLaneBefore()->getBidiLane())
    1121              :                                       : nullptr;
    1122              :     //std::cout << "   originLink=" << originLink->getDescription() << "\n";
    1123        10704 :     if (reverseOriginLink != nullptr) {
    1124        10704 :         reverseOriginLink = reverseOriginLink->getCorrespondingExitLink();
    1125              :         //std::cout << "   reverseOriginLink=" << reverseOriginLink->getDescription() << "\n";
    1126              :     }
    1127       125420 :     for (int i = 0; i < (int)lanes.size(); i++) {
    1128        93556 :         const MSLane* lane = lanes[i];
    1129        93556 :         const MSLane* prev = i > 0 ? lanes[i - 1] : nullptr;
    1130        93556 :         const MSLane* next = i + 1 < (int)lanes.size() ? lanes[i + 1] : nullptr;
    1131        93556 :         if (lane->isInternal()) {
    1132        32146 :             continue;
    1133              :         }
    1134       127616 :         for (auto ili : lane->getIncomingLanes()) {
    1135        76765 :             if (ili.viaLink == originLink
    1136        63854 :                     || ili.viaLink == reverseOriginLink
    1137        60841 :                     || ili.viaLink->getDirection() == LinkDirection::TURN
    1138       121853 :                     || ili.viaLink->getDirection() == LinkDirection::TURN_LEFTHAND) {
    1139        10559 :                 continue;
    1140              :             }
    1141        55647 :             if (ili.lane != prev && ili.lane != next) {
    1142              : #ifdef DEBUG_CHECK_FLANKS
    1143              :                 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";
    1144              : #endif
    1145              :                 flankSwitches.insert(ili.viaLink);
    1146        44073 :             } else if (allFoes) {
    1147              :                 // link is part of the driveway, find foes that cross the driveway without entering
    1148        11330 :                 checkCrossingFlanks(ili.viaLink, visited, flankSwitches);
    1149              :             }
    1150              :         }
    1151              :     }
    1152        31864 : }
    1153              : 
    1154              : 
    1155              : void
    1156        11330 : MSDriveWay::checkCrossingFlanks(MSLink* dwLink, const LaneVisitedMap& visited, std::set<MSLink*>& flankSwitches) const {
    1157              : #ifdef DEBUG_CHECK_FLANKS
    1158              :     std::cout << "  checkCrossingFlanks  dwLink=" << dwLink->getDescription() << " visited=" << formatVisitedMap(visited) << "\n";
    1159              : #endif
    1160              :     const MSJunction* junction = dwLink->getJunction();
    1161        11330 :     if (junction == nullptr) {
    1162              :         return; // unregulated junction;
    1163              :     }
    1164        11256 :     const MSJunctionLogic* logic = junction->getLogic();
    1165        11256 :     if (logic == nullptr) {
    1166              :         return; // unregulated junction;
    1167              :     }
    1168        57872 :     for (const MSEdge* in : junction->getIncoming()) {
    1169        46676 :         if (in->isInternal()) {
    1170        23855 :             continue;
    1171              :         }
    1172        45696 :         for (MSLane* inLane : in->getLanes()) {
    1173        22875 :             const MSLane* inBidi = inLane->getBidiLane();
    1174        32168 :             if (isRailwayOrShared(inLane->getPermissions()) && visited.count(inLane) == 0 && (inBidi == nullptr || visited.count(inBidi) == 0)) {
    1175         7696 :                 for (MSLink* link : inLane->getLinkCont()) {
    1176         3997 :                     if (link->getIndex() >= 0 && logic->getFoesFor(dwLink->getIndex()).test(link->getIndex())
    1177        10410 :                             && visited.count(link->getLane()) == 0) {
    1178              : #ifdef DEBUG_CHECK_FLANKS
    1179              :                         std::cout << " add crossing flankSwitch junction=" << junction->getID() << " index=" << link->getIndex() << "\n";
    1180              : #endif
    1181          542 :                         if (link->getViaLane() == nullptr) {
    1182              :                             flankSwitches.insert(link);
    1183              :                         } else {
    1184              :                             flankSwitches.insert(link->getViaLane()->getLinkCont().front());
    1185              :                         }
    1186              :                     }
    1187              :                 }
    1188              :             }
    1189              :         }
    1190              :     }
    1191              : }
    1192              : 
    1193              : void
    1194        13959 : MSDriveWay::findFlankProtection(MSLink* link, MSLink* origLink, std::vector<const MSLane*>& flank) {
    1195              : #ifdef DEBUG_CHECK_FLANKS
    1196              :     std::cout << "  findFlankProtection link=" << link->getDescription() << " origLink=" << origLink->getDescription() << "\n";
    1197              : #endif
    1198        13959 :     if (link->getCorrespondingEntryLink()->getTLLogic() != nullptr && link->getJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1199         2212 :         MSLink* entry = const_cast<MSLink*>(link->getCorrespondingEntryLink());
    1200              :         // guarded by signal
    1201              : #ifdef DEBUG_CHECK_FLANKS
    1202              :         std::cout << "   flank guarded by " << entry->getTLLogic()->getID() << "\n";
    1203              : #endif
    1204              :         // @note, technically it's enough to collect links from foe driveways
    1205              :         // but this also adds "unused" conflict links which may aid comprehension
    1206         2212 :         myConflictLinks.push_back(entry);
    1207         2212 :         addFoes(entry);
    1208              :     } else {
    1209              :         const MSLane* lane = link->getLaneBefore();
    1210              :         std::vector<MSLink*> predLinks;
    1211        23446 :         for (auto ili : lane->getIncomingLanes()) {
    1212        11699 :             if (!ili.viaLink->isTurnaround()) {
    1213        11312 :                 predLinks.push_back(ili.viaLink);
    1214              :             }
    1215              :         }
    1216        11747 :         if (predLinks.size() > 1) {
    1217              :             // this is a switch
    1218              : #ifdef DEBUG_ADD_FOES
    1219              :             std::cout << "    predecessors of " << link->getDescription() << " isSwitch\n";
    1220              : #endif
    1221          663 :             for (MSLink* pred : predLinks) {
    1222          442 :                 addSwitchFoes(pred);
    1223              :             }
    1224        11526 :         } else if (predLinks.size() == 1) {
    1225        10870 :             if (isSwitch(link)) {
    1226         9476 :                 addSwitchFoes(link);
    1227              :             } else {
    1228              :                 // continue upstream via single predecessor
    1229         1394 :                 findFlankProtection(predLinks.front(), origLink, flank);
    1230              :             }
    1231              :         }
    1232              :         // check for insertions
    1233        11747 :         if (myDepartureDriveways.count(&lane->getEdge()) != 0) {
    1234          296 :             for (MSDriveWay* foe : myDepartureDriveways[&lane->getEdge()]) {
    1235          150 :                 if (flankConflict(*foe) || crossingConflict(*foe)) {
    1236              : #ifdef DEBUG_ADD_FOES
    1237              :                     std::cout << "  foe " << foe->getID() << " departs on flank=" << lane->getID() << "\n";
    1238              : #endif
    1239          110 :                     myFoes.push_back(foe);
    1240              :                 } else {
    1241              : #ifdef DEBUG_ADD_FOES
    1242              :                     std::cout << "  cand foe " << foe->getID() << " departs on flank=" << lane->getID() << " rejected\n";
    1243              : #endif
    1244              :                 }
    1245              :             }
    1246              :         }
    1247        11747 :     }
    1248        13959 : }
    1249              : 
    1250              : 
    1251              : void
    1252         9918 : MSDriveWay::addSwitchFoes(MSLink* link) {
    1253              :     auto it = mySwitchDriveWays.find(link);
    1254         9918 :     if (it != mySwitchDriveWays.end()) {
    1255              : #ifdef DEBUG_ADD_FOES
    1256              :         std::cout << "   driveway " << myID << " addSwitchFoes for link " << link->getDescription() << "\n";
    1257              : #endif
    1258        33859 :         for (MSDriveWay* foe : it->second) {
    1259        30155 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1260              : #ifdef DEBUG_ADD_FOES
    1261              :                 std::cout << "   foe=" << foe->myID
    1262              :                           << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this)
    1263              :                           << " cc1=" << crossingConflict(*foe) << " cc2=" << foe->crossingConflict(*this) << "\n";
    1264              : #endif
    1265         3737 :                 myFoes.push_back(foe);
    1266              :             } else {
    1267              : #ifdef DEBUG_ADD_FOES
    1268              :                 std::cout << "   cand=" << foe->myID << "\n";
    1269              : #endif
    1270              :             }
    1271              :         }
    1272              :     }
    1273         9918 : }
    1274              : 
    1275              : 
    1276              : MSDriveWay*
    1277         7966 : MSDriveWay::buildDriveWay(const std::string& id, const MSLink* link, MSRouteIterator first, MSRouteIterator end) {
    1278              :     // collect lanes and links that are relevant for setting this signal for the current driveWay
    1279              :     // For each driveway we collect
    1280              :     //   - conflictLanes (signal must be red if any conflict lane is occupied)
    1281              :     //   - conflictLinks (signal must be red if any conflict link is approached by a vehicle
    1282              :     //      - that cannot break in time (arrivalSpeedBraking > 0)
    1283              :     //      - approached by a vehicle with higher switching priority (see #3941)
    1284              :     // These objects are construct in steps:
    1285              :     //
    1286              :     // forwardBlock
    1287              :     // - search forward recursive from outgoing lane until controlled railSignal link found
    1288              :     //   -> add all found lanes to conflictLanes
    1289              :     //
    1290              :     // bidiBlock (if any forwardBlock edge has bidi edge)
    1291              :     // - search bidi backward recursive until first switch
    1292              :     //   - from switch search backward recursive all other incoming until controlled rail signal link
    1293              :     //     -> add final links to conflictLinks
    1294              :     //
    1295              :     // flanks
    1296              :     // - search backward recursive from flanking switches
    1297              :     //   until controlled railSignal link or protecting switch is found
    1298              :     //   -> add all found lanes to conflictLanes
    1299              :     //   -> add final links to conflictLinks
    1300         7966 :     MSDriveWay* dw = new MSDriveWay(link, id);
    1301              :     LaneVisitedMap visited;
    1302              :     std::vector<const MSLane*> before;
    1303         7966 :     MSLane* fromBidi = nullptr;
    1304         7966 :     if (link != nullptr) {
    1305         4986 :         appendMapIndex(visited, link->getLaneBefore());
    1306         4986 :         fromBidi = link->getLaneBefore()->getBidiLane();
    1307              :     }
    1308              :     std::set<MSLink*> flankSwitches; // list of switches that threaten the driveway and for which protection must be found
    1309              : 
    1310         7966 :     if (fromBidi != nullptr) {
    1311         2758 :         before.push_back(fromBidi);
    1312              :     }
    1313         7966 :     dw->buildRoute(link, first, end, visited, flankSwitches);
    1314         7966 :     dw->myCoreSize = (int)dw->myRoute.size();
    1315         7966 :     dw->checkFlanks(link, dw->myForward, visited, true, flankSwitches);
    1316         7966 :     dw->checkFlanks(link, dw->myBidi, visited, false, flankSwitches);
    1317         7966 :     dw->checkFlanks(link, before, visited, true, flankSwitches);
    1318        16489 :     for (MSLink* fsLink : flankSwitches) {
    1319              : #ifdef DEBUG_ADD_FOES
    1320              :         if (DEBUG_COND_DW) {
    1321              :             std::cout << " fsLink=" << fsLink->getDescription() << "\n";
    1322              :         }
    1323              : #endif
    1324         8523 :         dw->findFlankProtection(fsLink, fsLink, dw->myFlank);
    1325              :     }
    1326              :     std::set<MSLink*> flankSwitchesBidiExtended;
    1327         7966 :     dw->checkFlanks(link, dw->myBidiExtended, visited, false, flankSwitchesBidiExtended);
    1328        12008 :     for (MSLink* const flink : flankSwitchesBidiExtended) {
    1329              : #ifdef DEBUG_ADD_FOES
    1330              :         if (DEBUG_COND_DW) {
    1331              :             std::cout << " fsLinkExtended=" << flink->getDescription() << "\n";
    1332              :         }
    1333              : #endif
    1334         4042 :         dw->findFlankProtection(flink, flink, dw->myBidiExtended);
    1335              :     }
    1336         7966 :     MSRailSignal* rs = link ? const_cast<MSRailSignal*>(static_cast<const MSRailSignal*>(link->getTLLogic())) : nullptr;
    1337        12952 :     const bool movingBlock = (rs && rs->isMovingBlock()) || (!rs && OptionsCont::getOptions().getBool("railsignal-moving-block"));
    1338              : #ifdef DEBUG_BUILD_DRIVEWAY
    1339              :     if (DEBUG_COND_DW) {
    1340              :         std::cout << SIMTIME << " buildDriveWay " << dw->myID << " link=" << (link == nullptr ? "NULL" : link->getDescription())
    1341              :                   << "\n    route=" << toString(dw->myRoute)
    1342              :                   << "\n    forward=" << toString(dw->myForward)
    1343              :                   << "\n    bidi=" << toString(dw->myBidi)
    1344              :                   << "\n    bidiEx=" << toString(dw->myBidiExtended)
    1345              :                   << "\n    flank=" << toString(dw->myFlank)
    1346              :                   << "\n    flankSwitch=" << MSRailSignal::describeLinks(std::vector<MSLink*>(flankSwitches.begin(), flankSwitches.end()))
    1347              :                   << "\n    coreSize=" << dw->myCoreSize
    1348              :                   << "\n";
    1349              :     }
    1350              : #endif
    1351         7966 :     if (!rs || !rs->isMovingBlock()) {
    1352         7911 :         dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myForward.begin(), dw->myForward.end());
    1353              :     }
    1354         7966 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myBidi.begin(), dw->myBidi.end());
    1355         7966 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myFlank.begin(), dw->myFlank.end());
    1356         7966 :     dw->addBidiFoes(rs, false);
    1357         7966 :     dw->addBidiFoes(rs, true);
    1358              :     // add driveways that start on the same signal / lane
    1359         7966 :     dw->addParallelFoes(link, *first);
    1360              :     // add driveways that reverse along this driveways route
    1361         7966 :     dw->addReversalFoes(movingBlock);
    1362              :     // make foes unique and symmetrical
    1363         7966 :     std::set<MSDriveWay*, ComparatorNumericalIdLess> uniqueFoes(dw->myFoes.begin(), dw->myFoes.end());
    1364              :     dw->myFoes.clear();
    1365              :     // check for self-intersecting forward-section in movingBlock mode
    1366         7966 :     if (movingBlock && uniqueFoes.count(dw) == 0) {
    1367              :         std::set<const MSJunction*> forwardJunctions;
    1368          964 :         for (const MSLane* fw : dw->myForward) {
    1369          877 :             if (fw->isNormal()) {
    1370          501 :                 const MSJunction* fwTo = fw->getEdge().getToJunction();
    1371              :                 if (forwardJunctions.count(fwTo) == 1) {
    1372           15 :                     dw->myFoes.push_back(dw);
    1373              : #ifdef DEBUG_ADD_FOES
    1374              :                     if (DEBUG_COND_DW) {
    1375              :                         std::cout << " self-intersecting movingBlock for dw=" << dw->getID() << "\n";
    1376              :                     }
    1377              : #endif
    1378           15 :                     break;
    1379              :                 }
    1380              :                 forwardJunctions.insert(fwTo);
    1381              :             }
    1382              :         }
    1383              :     }
    1384         7966 :     std::set<MSLink*> uniqueCLink(dw->myConflictLinks.begin(), dw->myConflictLinks.end());
    1385         7966 :     const MSEdge* lastEdge = &dw->myForward.back()->getEdge();
    1386        14138 :     for (MSDriveWay* foe : uniqueFoes) {
    1387         6172 :         const MSEdge* foeLastEdge = &foe->myForward.back()->getEdge();
    1388         6172 :         const bool sameLast = foeLastEdge == lastEdge;
    1389         6172 :         if (sameLast && !movingBlock) {
    1390         2450 :             dw->myFoes.push_back(foe);
    1391         2450 :             if (foe != dw) {
    1392         2450 :                 foe->myFoes.push_back(dw);
    1393              :             }
    1394              :         } else {
    1395         3722 :             if (foe->bidiBlockedByEnd(*dw)) {
    1396              : #ifdef DEBUG_ADD_FOES
    1397              :                 if (DEBUG_COND_DW) {
    1398              :                     std::cout << " setting " << dw->getID() << " as foe of " << foe->getID() << "\n";
    1399              :                 }
    1400              : #endif
    1401         1867 :                 foe->myFoes.push_back(dw);
    1402         1867 :                 foe->addSidings(dw);
    1403              :             } else {
    1404         1855 :                 dw->buildSubFoe(foe, movingBlock);
    1405              :             }
    1406         3722 :             if (foe != dw) { // check for movingBlock
    1407         3699 :                 if (dw->bidiBlockedByEnd(*foe)) {
    1408              : #ifdef DEBUG_ADD_FOES
    1409              :                     if (DEBUG_COND_DW) {
    1410              :                         std::cout << " addFoeCheckSiding " << foe->getID() << "\n";
    1411              :                     }
    1412              : #endif
    1413         2041 :                     dw->myFoes.push_back(foe);
    1414         2041 :                     dw->addSidings(foe);
    1415              :                 } else  {
    1416         1658 :                     foe->buildSubFoe(dw, movingBlock);
    1417              :                 }
    1418              :             }
    1419              :         }
    1420         6172 :         if (link) {
    1421         4521 :             foe->addConflictLink(link);
    1422              :         }
    1423              :         // ignore links that have the same start junction
    1424         6172 :         if (foe->myRoute.front()->getFromJunction() != dw->myRoute.front()->getFromJunction()) {
    1425         7702 :             for (auto ili : foe->myForward.front()->getIncomingLanes()) {
    1426         3544 :                 if (ili.viaLink->getTLLogic() != nullptr) {
    1427              :                     // ignore links that originate on myBidi
    1428         3104 :                     const MSLane* origin = ili.viaLink->getLaneBefore();
    1429         3104 :                     if (std::find(dw->myBidi.begin(), dw->myBidi.end(), origin) == dw->myBidi.end()) {
    1430              :                         uniqueCLink.insert(ili.viaLink);
    1431              :                     }
    1432              :                 }
    1433              :             }
    1434              :         }
    1435              :     }
    1436              :     dw->myConflictLinks.clear();
    1437         7966 :     dw->myConflictLinks.insert(dw->myConflictLinks.begin(), uniqueCLink.begin(), uniqueCLink.end());
    1438         7966 :     myEndingDriveways[lastEdge].push_back(dw);
    1439         7966 :     if (!movingBlock) {
    1440              :         // every driveway is it's own foe (also all driveways that depart in the same block)
    1441        18339 :         for (MSDriveWay* sameEnd : myEndingDriveways[lastEdge]) {
    1442              :             if (uniqueFoes.count(sameEnd) == 0) {
    1443         8048 :                 dw->myFoes.push_back(sameEnd);
    1444         8048 :                 if (sameEnd != dw) {
    1445          207 :                     sameEnd->myFoes.push_back(dw);
    1446              :                 }
    1447              :             }
    1448              :         }
    1449              :     }
    1450              : #ifdef DEBUG_BUILD_DRIVEWAY
    1451              :     if (DEBUG_COND_DW) {
    1452              :         std::cout << dw->myID << " finalFoes " << toString(dw->myFoes) << "\n";
    1453              :     }
    1454              : #endif
    1455         7966 :     return dw;
    1456         7966 : }
    1457              : 
    1458              : std::string
    1459         3708 : MSDriveWay::getTLLinkID(const MSLink* link) {
    1460         7416 :     return link->getTLLogic()->getID() + "_" + toString(link->getTLIndex());
    1461              : }
    1462              : 
    1463              : std::string
    1464            0 : MSDriveWay::getJunctionLinkID(const MSLink* link) {
    1465            0 :     return link->getJunction()->getID() + "_" + toString(link->getIndex());
    1466              : }
    1467              : 
    1468              : std::string
    1469         4986 : MSDriveWay::getClickableTLLinkID(const MSLink* link) {
    1470        14958 :     return "junction '" +  link->getTLLogic()->getID() + "', link " + toString(link->getTLIndex());
    1471              : }
    1472              : 
    1473              : std::string
    1474            0 : MSDriveWay::formatVisitedMap(const LaneVisitedMap& visited) {
    1475              :     UNUSED_PARAMETER(visited);
    1476              :     /*
    1477              :     std::vector<const MSLane*> lanes(visited.size(), nullptr);
    1478              :     for (auto item : visited) {
    1479              :         lanes[item.second] = item.first;
    1480              :     }
    1481              :     for (auto it = lanes.begin(); it != lanes.end();) {
    1482              :         if (*it == nullptr) {
    1483              :             it = lanes.erase(it);
    1484              :         } else {
    1485              :             it++;
    1486              :         }
    1487              :     }
    1488              :     return toString(lanes);
    1489              :     */
    1490            0 :     return "dummy";
    1491              : }
    1492              : 
    1493              : 
    1494              : void
    1495       103377 : MSDriveWay::appendMapIndex(LaneVisitedMap& map, const MSLane* lane) {
    1496              :     // avoid undefined behavior from evaluation order
    1497       103377 :     const int tmp = (int)map.size();
    1498       103377 :     map[lane] = tmp;
    1499       103377 : }
    1500              : 
    1501              : bool
    1502      8012232 : MSDriveWay::match(MSRouteIterator firstIt, MSRouteIterator endIt) const {
    1503              :     // @todo optimize: it is sufficient to check for specific edges (after each switch)
    1504      8012232 :     auto itRoute = firstIt;
    1505              :     auto itDwRoute = myRoute.begin();
    1506              :     bool match = true;
    1507     56439731 :     while (itRoute != endIt && itDwRoute != myRoute.end()) {
    1508     48617836 :         if (*itRoute != *itDwRoute) {
    1509              :             match = false;
    1510              : #ifdef DEBUG_MATCH
    1511              :             std::cout << "  check dw=" << getID() << " match failed at vehEdge=" << (*itRoute)->getID() << " dwEdge=" << (*itDwRoute)->getID() << "\n";
    1512              : #endif
    1513              :             break;
    1514              :         }
    1515              :         itRoute++;
    1516              :         itDwRoute++;
    1517              :     }
    1518              :     // if the vehicle arrives before the end of this driveway,
    1519              :     // we'd rather build a new driveway to avoid superfluous restrictions
    1520      7821895 :     if (match && itDwRoute == myRoute.end()
    1521     15807467 :             && (itRoute == endIt || myAbortedBuild || myBidiEnded || myFoundJump || myIsSubDriveway)) {
    1522              :         //std::cout << "  using dw=" << "\n";
    1523      7782580 :         if (itRoute != endIt) {
    1524              :             // check whether the current route requires an extended driveway
    1525       142289 :             const MSEdge* next = *itRoute;
    1526       142289 :             const MSEdge* prev = myRoute.back();
    1527          478 :             if (myFoundJump && prev->getBidiEdge() != next && prev->getBidiEdge() != nullptr
    1528       142540 :                     && prev->isConnectedTo(*next, (SUMOVehicleClass)(SVC_RAIL_CLASSES & prev->getPermissions()))) {
    1529              : #ifdef DEBUG_MATCH
    1530              :                 std::cout << "  check dw=" << getID() << " prev=" << prev->getID() << " next=" << next->getID() << "\n";
    1531              : #endif
    1532              :                 return false;
    1533              :             }
    1534       142272 :             if (!myFoundJump && prev->getBidiEdge() == next && prev == &myForward.back()->getEdge()) {
    1535              :                 assert(myIsSubDriveway || myBidiEnded);
    1536              :                 // must not leave driveway via reversal
    1537              : #ifdef DEBUG_MATCH
    1538              :                 std::cout << getID() << " back=" << myForward.back()->getID() << " noMatch route " << toString(ConstMSEdgeVector(firstIt, endIt)) << "\n";
    1539              : #endif
    1540              :                 return false;
    1541              :             }
    1542              :         }
    1543      7782557 :         return true;
    1544              :     }
    1545              :     return false;
    1546              : }
    1547              : 
    1548              : void
    1549        11443 : MSDriveWay::addFoes(const MSLink* link) {
    1550              : #ifdef DEBUG_ADD_FOES
    1551              :     std::cout << "driveway " << myID << " addFoes for link " << link->getDescription() << "\n";
    1552              : #endif
    1553        11443 :     const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(link->getTLLogic());
    1554        11443 :     if (rs != nullptr) {
    1555        15821 :         for (MSDriveWay* foe : rs->retrieveDriveWays(link->getTLIndex())) {
    1556              : #ifdef DEBUG_ADD_FOES
    1557              :             std::cout << "  cand foe=" << foe->myID << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this) << " cc1=" << crossingConflict(*foe) << " cc2=" <<  foe->crossingConflict(*this) << "\n";
    1558              : #endif
    1559         4378 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1560              : #ifdef DEBUG_ADD_FOES
    1561              :                 std::cout << "   foe=" << foe->myID << "\n";
    1562              : #endif
    1563         3304 :                 myFoes.push_back(foe);
    1564              :             }
    1565        11443 :         }
    1566              :     }
    1567        11443 : }
    1568              : 
    1569              : 
    1570              : void
    1571        15932 : MSDriveWay::addBidiFoes(const MSRailSignal* ownSignal, bool extended) {
    1572              : #ifdef DEBUG_ADD_FOES
    1573              :     std::cout << "driveway " << myID << " addBidiFoes extended=" << extended << "\n";
    1574              : #endif
    1575        15932 :     const std::vector<const MSLane*>& bidiLanes = extended ? myBidiExtended : myBidi;
    1576        80647 :     for (const MSLane* bidi : bidiLanes) {
    1577       132540 :         for (auto ili : bidi->getIncomingLanes()) {
    1578        67825 :             const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(ili.viaLink->getTLLogic());
    1579        67825 :             if (rs != nullptr && rs != ownSignal &&
    1580        67825 :                     std::find(bidiLanes.begin(), bidiLanes.end(), ili.lane) != bidiLanes.end()) {
    1581         4245 :                 addFoes(ili.viaLink);
    1582              :             }
    1583              :         }
    1584        64715 :         const MSEdge* bidiEdge = &bidi->getEdge();
    1585              :         if (myDepartureDriveways.count(bidiEdge) != 0) {
    1586         3090 :             for (MSDriveWay* foe : myDepartureDriveways[bidiEdge]) {
    1587         1433 :                 if (flankConflict(*foe)) {
    1588              : #ifdef DEBUG_ADD_FOES
    1589              :                     std::cout << "  foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << "\n";
    1590              : #endif
    1591          851 :                     myFoes.push_back(foe);
    1592              :                 } else {
    1593              : #ifdef DEBUG_ADD_FOES
    1594              :                     std::cout << "  cand foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << " rejected\n";
    1595              : #endif
    1596              :                 }
    1597              :             }
    1598              :         }
    1599              :         if (myDepartureDrivewaysEnds.count(bidiEdge) != 0) {
    1600         2630 :             for (MSDriveWay* foe : myDepartureDrivewaysEnds[bidiEdge]) {
    1601         1362 :                 if (flankConflict(*foe)) {
    1602              : #ifdef DEBUG_ADD_FOES
    1603              :                     std::cout << "  foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << "\n";
    1604              : #endif
    1605          842 :                     myFoes.push_back(foe);
    1606              :                 } else {
    1607              : #ifdef DEBUG_ADD_FOES
    1608              :                     std::cout << "  cand foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << " rejected\n";
    1609              : #endif
    1610              :                 }
    1611              :             }
    1612              :         }
    1613              :     }
    1614        15932 : }
    1615              : 
    1616              : 
    1617              : void
    1618         7966 : MSDriveWay::addParallelFoes(const MSLink* link, const MSEdge* first) {
    1619              : #ifdef DEBUG_ADD_FOES
    1620              :     std::cout << "driveway " << myID << " addParallelFoes\n";
    1621              : #endif
    1622         7966 :     if (link) {
    1623         4986 :         addFoes(link);
    1624              :     } else {
    1625              :         auto it = myDepartureDriveways.find(first);
    1626         2980 :         if (it != myDepartureDriveways.end()) {
    1627         3146 :             for (MSDriveWay* foe : it->second) {
    1628              : #ifdef DEBUG_ADD_FOES
    1629              :                 std::cout << "  foe " << foe->getID() << " departs on first=" << first->getID() << "\n";
    1630              : #endif
    1631          193 :                 myFoes.push_back(foe);
    1632              :             }
    1633              :         }
    1634              :     }
    1635         7966 : }
    1636              : 
    1637              : 
    1638              : void
    1639         7966 : MSDriveWay::addReversalFoes(bool movingBlock) {
    1640              : #ifdef DEBUG_ADD_FOES
    1641              :     std::cout << "driveway " << myID << " addReversalFoes\n";
    1642              : #endif
    1643              :     std::set<const MSEdge*> forward;
    1644        34049 :     for (const MSLane* lane : myForward) {
    1645        26083 :         if (lane->isNormal()) {
    1646        17373 :             forward.insert(&lane->getEdge());
    1647              :         }
    1648              :     }
    1649              :     int i = 0;
    1650        69500 :     for (const MSEdge* e : myRoute) {
    1651        17387 :         if (forward.count(e) != 0 && !movingBlock) {
    1652              :             // reversals in our own forward can be ignored because each driveway
    1653              :             // is automatically a foe of itself by default
    1654              :             continue;
    1655              :         }
    1656        44766 :         if (i == myCoreSize) {
    1657              :             break;
    1658              :         }
    1659        44766 :         i++;
    1660              :         auto it = myReversalDriveWays.find(e);
    1661        44766 :         if (it != myReversalDriveWays.end()) {
    1662         6336 :             for (MSDriveWay* foe : it->second) {
    1663              :                 // check whether the foe reverses into our own forward section
    1664              :                 // (it might reverse again or disappear via arrival)
    1665              : #ifdef DEBUG_ADD_FOES
    1666              :                 //std::cout << "  candidate foe " << foe->getID() << " reverses on edge=" << e->getID() << " forward=" << joinNamedToString(forward, " ") << " foeRoute=" << toString(foe->myRoute) << "\n";
    1667              : #endif
    1668        11160 :                 if (forwardRouteConflict(forward, *foe)) {
    1669              :                     std::set<const MSEdge*> foeForward;
    1670         1162 :                     for (const MSLane* lane : foe->myForward) {
    1671          973 :                         if (lane->isNormal()) {
    1672          607 :                             foeForward.insert(&lane->getEdge());
    1673          607 :                             if (lane->getBidiLane() != nullptr) {
    1674          596 :                                 foeForward.insert(lane->getEdge().getBidiEdge());
    1675              :                             }
    1676              :                         }
    1677              :                     }
    1678              : #ifdef DEBUG_ADD_FOES
    1679              :                     std::cout << "  reversal cand=" << foe->getID() << " foeForward " << toString(foeForward) << "\n";
    1680              : #endif
    1681          378 :                     if (foe->forwardRouteConflict(foeForward, *this, true)) {
    1682              : #ifdef DEBUG_ADD_FOES
    1683              :                         std::cout << "  foe " << foe->getID() << " reverses on edge=" << e->getID() << "\n";
    1684              : #endif
    1685          143 :                         myFoes.push_back(foe);
    1686              :                     }
    1687         5391 :                 } else if (movingBlock && foe == this) {
    1688              : #ifdef DEBUG_ADD_FOES
    1689              :                     std::cout << "  dw " << getID() << " reverses on forward edge=" << e->getID() << " (movingBlock)\n";
    1690              : #endif
    1691           23 :                     myFoes.push_back(foe);
    1692              :                 }
    1693              :             }
    1694              :         }
    1695              :     }
    1696         7966 : }
    1697              : 
    1698              : 
    1699              : bool
    1700         3513 : MSDriveWay::buildSubFoe(MSDriveWay* foe, bool movingBlock) {
    1701              :     // Subdriveways (Teilfahrstraße) model the resolution of a driving conflict
    1702              :     // before a vehicle has left the driveway. This is possible when the driveway diverges from the foe
    1703              :     // driveway at an earlier point (switch or crossing).
    1704              :     //
    1705              :     // We already know that the last edge of this driveway doesn't impact the foe (unless the driveway ends within the block).
    1706              :     // Remove further edges from the end of the driveway (myForward) until the point of conflict is found.
    1707              :     //
    1708              :     // For movingBlock the logic is changed:
    1709              :     // We remove the conflict-free part as before but then keep removing the conflict part until another non-conconflit part is found
    1710         3513 :     if (myForward.size() < foe->myForward.size() &&
    1711         3513 :             myForward == std::vector<const MSLane*>(foe->myForward.begin(), foe->myForward.begin() + myForward.size())) {
    1712              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1713              :         std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " is subpart of foe=" << foe->getID() << "\n";
    1714              : #endif
    1715           29 :         foe->myFoes.push_back(this);
    1716           29 :         return true;
    1717              :     }
    1718         3484 :     int subLast = (int)myForward.size() - 2;
    1719              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1720              :     if (subLast < 0) {
    1721              :         std::cout << "  " << getID() << " cannot build subDriveWay for foe " << foe->getID() << " because myForward has only a single lane\n";
    1722              :     }
    1723              : #endif
    1724              :     bool foundConflict = false;
    1725        11605 :     while (subLast >= 0) {
    1726        10866 :         const MSLane* lane = myForward[subLast];
    1727        10866 :         MSDriveWay tmp(myOrigin, "tmp", true);
    1728        10866 :         tmp.myForward.push_back(lane);
    1729              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1730              :         std::cout << "  subLast=" << subLast << " lane=" << lane->getID() << " fc=" << tmp.flankConflict(*foe) << " cc=" << tmp.crossingConflict(*foe)
    1731              :                   << " bc=" << (std::find(foe->myBidi.begin(), foe->myBidi.end(), lane) != foe->myBidi.end()) << "\n";
    1732              : #endif
    1733        10866 :         const bool bidiConflict = std::find(foe->myBidi.begin(), foe->myBidi.end(), lane) != foe->myBidi.end();
    1734        10866 :         if (tmp.flankConflict(*foe) || tmp.crossingConflict(*foe) || bidiConflict) {
    1735              :             foundConflict = true;
    1736         2762 :             if (!movingBlock || bidiConflict) {
    1737              :                 break;
    1738              :             }
    1739         8104 :         } else if (foundConflict) {
    1740              :             break;
    1741              :         }
    1742         8121 :         subLast--;
    1743        10866 :     }
    1744         3484 :     if (subLast < 0) {
    1745          739 :         if (&myForward.back()->getEdge() == myRoute.back() && foe->forwardEndOnRoute(this)) {
    1746              :             // driveway ends in the middle of the block and only the final edge overlaps with the foe driveWay
    1747           14 :             foe->myFoes.push_back(this);
    1748          725 :         } else if (foe->myTerminateRoute) {
    1749          597 :             if (bidiBlockedByEnd(*foe) && bidiBlockedBy(*this) && foe->forwardEndOnRoute(this)) {
    1750           89 :                 foe->myFoes.push_back(this);
    1751              :                 // foe will get the sidings
    1752           89 :                 addSidings(foe, true);
    1753              :             }
    1754              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1755              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " terminates\n";
    1756              : #endif
    1757          128 :         } else if (myTerminateRoute && myBidi.size() <= myForward.size()) {
    1758            0 :             foe->myFoes.push_back(this);
    1759              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1760              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " terminates, foe=" << foe->getID() << "\n";
    1761              : #endif
    1762            0 :             return true;
    1763              :         } else if (foe->myReversals.size() % 2 == 1) {
    1764              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1765              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " has " << foe->myReversals.size() << " reversals\n";
    1766              : #endif
    1767              :         } else {
    1768              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1769              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " failed\n";
    1770              : #endif
    1771              : #ifdef SUBDRIVEWAY_WARN_NOCONFLICT
    1772              :             WRITE_WARNINGF("No point of conflict found between driveway '%' and driveway '%' when creating sub-driveway", getID(), foe->getID());
    1773              : #endif
    1774              :         }
    1775          739 :         return false;
    1776              :     }
    1777         2745 :     int subSize = subLast + 1;
    1778         3487 :     for (MSDriveWay* cand : mySubDriveWays) {
    1779         1401 :         if ((int)cand->myForward.size() == subSize) {
    1780              :             // can re-use existing sub-driveway
    1781          659 :             foe->myFoes.push_back(cand);
    1782          659 :             cand->myFoes.push_back(foe);
    1783              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1784              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " useExisting=" << cand->getID() << "\n";
    1785              : #endif
    1786          659 :             return true;
    1787              :         }
    1788              :     }
    1789         2086 :     std::vector<const MSLane*> forward(myForward.begin(), myForward.begin() + subSize);
    1790              :     std::vector<const MSEdge*> route;
    1791         9596 :     for (const MSLane* lane : forward) {
    1792         7510 :         if (lane->isNormal()) {
    1793         4470 :             route.push_back(&lane->getEdge());
    1794              :         }
    1795              :     }
    1796         2086 :     if (route.empty()) {
    1797              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1798              :         std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID() << " empty subRoute\n";
    1799              : #endif
    1800              :         return false;
    1801              :     }
    1802         2086 :     if (myRoute.size() > route.size()) {
    1803              :         // route continues. make sure the subDriveway does not end with a reversal
    1804         2086 :         const MSEdge* lastNormal = route.back();
    1805         2086 :         const MSEdge* nextNormal = myRoute[route.size()];
    1806         2086 :         if (lastNormal->getBidiEdge() == nextNormal) {
    1807              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1808              :             std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID()
    1809              :                       << " lastNormal=" << lastNormal->getID() << " nextNormal=" << nextNormal->getID() << " endWithReversal\n";
    1810              : #endif
    1811              :             return false;
    1812              :         }
    1813              :     }
    1814         4132 :     MSDriveWay* sub = new MSDriveWay(myOrigin, getID() + "." + toString(mySubDriveWays.size()));
    1815         2066 :     sub->myLane = myLane;
    1816         2066 :     sub->myIsSubDriveway = true;
    1817         2066 :     sub->myForward = forward;
    1818         2066 :     sub->myRoute = route;
    1819         2066 :     sub->myCoreSize = (int)sub->myRoute.size();
    1820         2066 :     myLane->addMoveReminder(sub, false);
    1821              : 
    1822              :     // copy trains that are currently on this driveway (and associated entry events)
    1823         2188 :     for (SUMOVehicle* veh : myTrains) {
    1824          122 :         auto itOnSub = std::find(sub->myRoute.begin(), sub->myRoute.end(), veh->getEdge());
    1825          122 :         if (itOnSub != sub->myRoute.end()) {
    1826              :             sub->myTrains.insert(veh);
    1827              :             // non-zero is enough to avoid superfluous activation via activateReminders (and removal)
    1828          109 :             const double pos = sub->myRoute.front()->getLength();
    1829          109 :             dynamic_cast<MSBaseVehicle*>(veh)->addReminder(sub, pos);
    1830          116 :             for (const VehicleEvent& ve : myVehicleEvents) {
    1831            7 :                 if (ve.id == veh->getID()) {
    1832            7 :                     sub->myVehicleEvents.push_back(ve);
    1833              :                 }
    1834              :             }
    1835              :         }
    1836              :     }
    1837              : 
    1838         2066 :     foe->myFoes.push_back(sub);
    1839         2066 :     sub->myFoes.push_back(foe);
    1840         2066 :     mySubDriveWays.push_back(sub);
    1841              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1842              :     std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " sub=" << sub->getID() << " route=" << toString(sub->myRoute) << "\n";
    1843              : #endif
    1844              :     return true;
    1845         2086 : }
    1846              : 
    1847              : void
    1848         3997 : MSDriveWay::addSidings(MSDriveWay* foe, bool addToFoe) {
    1849         3997 :     const MSEdge* foeEndBidi = foe->myForward.back()->getEdge().getBidiEdge();
    1850              :     int forwardNormals = 0;
    1851        18037 :     for (auto lane : foe->myForward) {
    1852        14040 :         if (lane->isNormal()) {
    1853         9363 :             forwardNormals++;
    1854              :         }
    1855              :     }
    1856         3997 :     if (forwardNormals == (int)foe->myRoute.size()) {
    1857              : #ifdef DEBUG_BUILD_SIDINGS
    1858              :         std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " forwardNormals=" << forwardNormals << " frSize=" << foe->myRoute.size() <<  " aborted\n";
    1859              : #endif
    1860          773 :         return;
    1861              :     }
    1862              :     auto foeSearchBeg = foe->myRoute.begin() + forwardNormals;
    1863              :     auto foeSearchEnd = foe->myRoute.end();
    1864         3224 :     if (foeEndBidi == nullptr) {
    1865            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " noBidi\n");
    1866              :     }
    1867              :     int i;
    1868              :     std::vector<int> start;
    1869              :     std::vector<double> length;
    1870        21062 :     for (i = 0; i < (int)myRoute.size(); i++) {
    1871        21062 :         if (myRoute[i] == foeEndBidi) {
    1872              :             break;
    1873              :         }
    1874              :     }
    1875         3224 :     if (i == (int)myRoute.size()) {
    1876            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " foeEndBidi=" + foeEndBidi->getID() + " not on route\n");
    1877              :     }
    1878         3224 :     const MSEdge* next = myRoute[i];
    1879              : #ifdef DEBUG_BUILD_SIDINGS
    1880              :     std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " next=" << next->getID() << " forwardNormals=" << forwardNormals << " frSize=" << foe->myRoute.size() << " foeSearchBeg=" << (*foeSearchBeg)->getID() << "\n";
    1881              : #endif
    1882         3224 :     i--;
    1883        21062 :     for (; i >= 0; i--) {
    1884        17838 :         const MSEdge* cur = myRoute[i];
    1885        17838 :         if (hasRS(cur, next)) {
    1886         5243 :             if (std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge()) == foeSearchEnd) {
    1887         2876 :                 start.push_back(i);
    1888         2876 :                 length.push_back(0);
    1889              :             }
    1890              :         }
    1891        17838 :         if (!start.empty()) {
    1892         4689 :             auto itFind = std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge());
    1893         4689 :             if (itFind != foeSearchEnd) {
    1894              : #ifdef DEBUG_BUILD_SIDINGS
    1895              :                 std::cout << "endSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " curBidi=" << Named::getIDSecure(cur->getBidiEdge()) << " length=" << toString(length) << "\n";
    1896              : #endif
    1897          707 :                 const int firstIndex = i + 1;
    1898          707 :                 if (addToFoe) {
    1899           69 :                     auto& foeSidings = foe->mySidings[this];
    1900              :                     // indices must be mapped onto foe route;
    1901           69 :                     const MSEdge* first = myRoute[firstIndex];
    1902           69 :                     auto itFirst = std::find(foe->myRoute.begin(), foe->myRoute.end(), first);
    1903           69 :                     if (itFirst != foe->myRoute.end()) {
    1904          279 :                         for (int j = 0; j < (int)length.size(); j++) {
    1905          221 :                             const MSEdge* last = myRoute[start[j]];
    1906          221 :                             auto itLast = std::find(itFirst, foe->myRoute.end(), last);
    1907          221 :                             if (itLast != foe->myRoute.end()) {
    1908          221 :                                 foeSidings.insert(foeSidings.begin(), Siding((int)(itFirst - foe->myRoute.begin()), (int)(itLast - foe->myRoute.begin()), length[j]));
    1909              :                             }
    1910              :                         }
    1911              :                     }
    1912              :                 } else {
    1913          638 :                     auto& foeSidings = mySidings[foe];
    1914         2968 :                     for (int j = 0; j < (int)length.size(); j++) {
    1915         2330 :                         foeSidings.insert(foeSidings.begin(), Siding(firstIndex, start[j], length[j]));
    1916              :                     }
    1917              :                 }
    1918              :                 start.clear();
    1919              :                 length.clear();
    1920          707 :                 foeSearchBeg = itFind;
    1921              :             } else {
    1922        25300 :                 for (int j = 0; j < (int)length.size(); j++) {
    1923        21318 :                     length[j] += cur->getLength();
    1924              :                 }
    1925              :             }
    1926              :         }
    1927              :         next = cur;
    1928              :     }
    1929         3224 : }
    1930              : 
    1931              : 
    1932              : bool
    1933        17838 : MSDriveWay::hasRS(const MSEdge* cur, const MSEdge* next) {
    1934        17838 :     if (cur->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1935              :         // check if there is a controlled link between cur and next
    1936        13815 :         for (auto lane : cur->getLanes()) {
    1937        14221 :             for (const MSLink* link : lane->getLinkCont()) {
    1938         9935 :                 if (&link->getLane()->getEdge() == next && link->getTLLogic() != nullptr) {
    1939              :                     return true;
    1940              :                 }
    1941              :             }
    1942              :         }
    1943              :     }
    1944              :     return false;
    1945              : }
    1946              : 
    1947              : 
    1948              : bool
    1949          113 : MSDriveWay::forwardEndOnRoute(const MSDriveWay* foe) const {
    1950          113 :     const MSEdge* foeForwardEnd = &foe->myForward.back()->getNormalPredecessorLane()->getEdge();
    1951          113 :     return std::find(myRoute.begin(), myRoute.end(), foeForwardEnd) != myRoute.end();
    1952              : }
    1953              : 
    1954              : void
    1955         4521 : MSDriveWay::addConflictLink(const MSLink* link) {
    1956         4521 :     if (link->getTLLogic() != nullptr) {
    1957              :         // ignore links that originate on myBidi
    1958              :         // and also links from the same junction as my own link
    1959         4521 :         const MSLane* origin = link->getLaneBefore();
    1960         4521 :         if (std::find(myBidi.begin(), myBidi.end(), origin) == myBidi.end()) {
    1961         3621 :             if (link->getJunction() != myRoute.front()->getFromJunction()) {
    1962         2159 :                 if (std::find(myConflictLinks.begin(), myConflictLinks.end(), link) == myConflictLinks.end()) {
    1963         1487 :                     myConflictLinks.push_back(const_cast<MSLink*>(link));
    1964              :                 }
    1965              :             }
    1966              :         }
    1967              :     }
    1968         4521 : }
    1969              : 
    1970              : void
    1971          297 : MSDriveWay::addDWDeadlock(const std::vector<const MSDriveWay*>& deadlockFoes) {
    1972              :     std::set<const MSDriveWay*> filtered;
    1973         1515 :     for (const MSDriveWay* foe : deadlockFoes) {
    1974         1218 :         if (std::find(myFoes.begin(), myFoes.end(), foe) == myFoes.end()) {
    1975              :             filtered.insert(foe);
    1976              :         }
    1977              :     }
    1978          297 :     if (std::find(myDeadlocks.begin(), myDeadlocks.end(), filtered) == myDeadlocks.end()) {
    1979           81 :         myDeadlocks.push_back(filtered);
    1980              :         //std::cout << getID() << " deadlockFoes=" << toString(deadlockFoes) << "\n";
    1981              :     }
    1982          297 : }
    1983              : 
    1984              : const MSDriveWay*
    1985        53548 : MSDriveWay::getDepartureDriveway(const SUMOVehicle* veh, bool init) {
    1986        53548 :     const MSEdge* edge = init ? veh->getRoute().getEdges()[veh->getDepartEdge()] : veh->getEdge();
    1987              :     assert(isRailwayOrShared(edge->getPermissions()));
    1988        53548 :     if (edge->getFromJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1989         3486 :         for (const MSLane* lane : edge->getLanes()) {
    1990         3795 :             for (auto ili : lane->getIncomingLanes()) {
    1991         2324 :                 const MSLink* entry = ili.viaLink->getCorrespondingEntryLink();
    1992         2324 :                 const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(entry->getTLLogic());
    1993         1689 :                 if (rs != nullptr) {
    1994         1689 :                     const MSDriveWay* dw = &const_cast<MSRailSignal*>(rs)->retrieveDriveWayForVeh(entry->getTLIndex(), veh);
    1995         1689 :                     if (&dw->myForward.front()->getEdge() == edge) {
    1996              :                         return dw;
    1997              :                     }
    1998              :                 }
    1999              :             }
    2000              :         }
    2001              :     }
    2002        56210 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    2003        53230 :         auto matchStart = init ? veh->getRoute().begin() + veh->getDepartEdge() : veh->getCurrentRouteEdge();
    2004        53230 :         if (dw->match(matchStart, veh->getRoute().end())) {
    2005        50024 :             return dw;
    2006              :         }
    2007              :     }
    2008         5960 :     const std::string id = edge->getFromJunction()->getID() + ".d" + toString(myDepartDrivewayIndex[edge->getFromJunction()]++);
    2009         2980 :     MSDriveWay* dw = buildDriveWay(id, nullptr, veh->getCurrentRouteEdge(), veh->getRoute().end());
    2010         2980 :     myDepartureDriveways[edge].push_back(dw);
    2011         2980 :     myDepartureDrivewaysEnds[&dw->myForward.back()->getEdge()].push_back(dw);
    2012              :     dw->setVehicle(veh->getID());
    2013              :     return dw;
    2014              : }
    2015              : 
    2016              : 
    2017              : void
    2018         1354 : MSDriveWay::writeDepatureBlocks(OutputDevice& od, bool writeVehicles) {
    2019         3400 :     for (auto item  : myDepartureDriveways) {
    2020         2046 :         const MSEdge* edge = item.first;
    2021         2046 :         if (item.second.size() > 0) {
    2022         4092 :             od.openTag("departJunction");
    2023         2046 :             od.writeAttr(SUMO_ATTR_ID, edge->getFromJunction()->getID());
    2024         4283 :             for (const MSDriveWay* dw : item.second) {
    2025         2237 :                 if (writeVehicles) {
    2026          215 :                     dw->writeBlockVehicles(od);
    2027              :                 } else {
    2028         2022 :                     dw->writeBlocks(od);
    2029              :                 }
    2030              :             }
    2031         4092 :             od.closeTag(); // departJunction
    2032              :         }
    2033              :     }
    2034         1354 : }
    2035              : 
    2036              : void
    2037          476 : MSDriveWay::saveState(OutputDevice& out) {
    2038              :     // all driveways are in myEndingDriveways which makes it convenient
    2039          551 :     for (auto item : myEndingDriveways) {
    2040          164 :         for (MSDriveWay* dw : item.second) {
    2041           89 :             dw->_saveState(out);
    2042          107 :             for (MSDriveWay* sub : dw->mySubDriveWays) {
    2043           18 :                 sub->_saveState(out);
    2044              :             }
    2045              :         }
    2046              :     }
    2047          476 : }
    2048              : 
    2049              : void
    2050          107 : MSDriveWay::_saveState(OutputDevice& out) const {
    2051          107 :     if (!myTrains.empty() || haveSubTrains()) {
    2052           73 :         out.openTag(myIsSubDriveway ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
    2053           38 :         out.writeAttr(SUMO_ATTR_ID, getID());
    2054           76 :         out.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
    2055           38 :         if (!myTrains.empty()) {
    2056           76 :             out.writeAttr(SUMO_ATTR_VEHICLES, toString(myTrains));
    2057              :         }
    2058           76 :         out.closeTag();
    2059              :     }
    2060          107 : }
    2061              : 
    2062              : 
    2063              : bool
    2064           69 : MSDriveWay::haveSubTrains() const {
    2065           76 :     for (MSDriveWay* sub : mySubDriveWays) {
    2066            7 :         if (!sub->myTrains.empty()) {
    2067              :             return true;
    2068              :         }
    2069              :     }
    2070              :     return false;
    2071              : }
    2072              : 
    2073              : void
    2074           41 : MSDriveWay::loadState(const SUMOSAXAttributes& attrs, int tag) {
    2075           41 :     if ((int)myDriveWayRouteLookup.size() < myGlobalDriveWayIndex) {
    2076          106 :         for (auto item : myEndingDriveways) {
    2077          168 :             for (MSDriveWay* dw : item.second) {
    2078           86 :                 myDriveWayRouteLookup[dw->myRoute] = dw;
    2079              :             }
    2080              :         }
    2081              :     }
    2082           41 :     MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
    2083              :     bool ok;
    2084           41 :     const std::string id  = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    2085           41 :     const std::string edges = attrs.get<std::string>(SUMO_ATTR_EDGES, id.c_str(), ok);
    2086              :     ConstMSEdgeVector route;
    2087           41 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
    2088           41 :         MSEdge::parseEdgesList(edges, route, id);
    2089              :     }
    2090              :     MSDriveWay* dw = nullptr;
    2091           41 :     if (tag == SUMO_TAG_DRIVEWAY) {
    2092              :         auto it = myDriveWayRouteLookup.find(route);
    2093           38 :         if (it == myDriveWayRouteLookup.end()) {
    2094              :             //WRITE_WARNING(TLF("Unknown driveWay '%' with route '%'", id, edges));
    2095              :             //return;
    2096            0 :             throw ProcessError(TLF("Unknown driveWay '%' with route '%'", id, edges));
    2097              :         }
    2098           38 :         dw = it->second;
    2099           38 :         myDriveWayLookup[id] = dw;
    2100              :     } else {
    2101            3 :         std::string parentID = id.substr(0, id.rfind('.'));
    2102              :         auto it = myDriveWayLookup.find(parentID);
    2103            3 :         if (it == myDriveWayLookup.end()) {
    2104              :             //WRITE_WARNING(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
    2105              :             //return;
    2106            0 :             throw ProcessError(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
    2107              :         }
    2108            3 :         MSDriveWay* parent = it->second;
    2109            3 :         for (MSDriveWay* sub : parent->mySubDriveWays) {
    2110            1 :             if (sub->myRoute == route) {
    2111              :                 dw = sub;
    2112              :                 break;
    2113              :             }
    2114              :         }
    2115            3 :         if (dw == nullptr) {
    2116              :             // missing subdriveways can be ignored. They may have been created
    2117              :             // as foes for driveways that are not relevant at state loading time
    2118              :             return;
    2119              :         }
    2120              :     }
    2121           78 :     const std::string vehicles = attrs.getOpt<std::string>(SUMO_ATTR_VEHICLES, id.c_str(), ok, "");
    2122          117 :     for (const std::string& vehID : StringTokenizer(vehicles).getVector()) {
    2123           39 :         MSBaseVehicle* veh = dynamic_cast<MSBaseVehicle*>(c.getVehicle(vehID));
    2124           39 :         if (veh == nullptr) {
    2125            0 :             throw ProcessError(TLF("Unknown vehicle '%' in driveway '%'", vehID, id));
    2126              :         }
    2127           39 :         if (!dw->hasTrain(veh)) {
    2128            5 :             dw->myTrains.insert(veh);
    2129            5 :             veh->addReminder(dw);
    2130              :         }
    2131           39 :     }
    2132           41 : }
    2133              : 
    2134              : const MSDriveWay*
    2135            0 : MSDriveWay::retrieveDepartDriveWay(const MSEdge* edge, const std::string& id) {
    2136            0 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    2137            0 :         if (dw->getID() == id) {
    2138              :             return dw;
    2139              :         }
    2140              :     }
    2141              :     return nullptr;
    2142              : }
    2143              : 
    2144              : 
    2145              : bool
    2146         1494 : MSDriveWay::hasTrain(SUMOVehicle* veh) const {
    2147         1494 :     return myTrains.count(veh) != 0;
    2148              : }
    2149              : 
    2150              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1