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 % 832 803
Test Date: 2025-11-13 15:38:19 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        39025 : MSDriveWay::init() {
      80        39025 :     myWriteVehicles = OptionsCont::getOptions().isSet("railsignal-vehicle-output");
      81        39025 : }
      82              : 
      83              : // ===========================================================================
      84              : // MSDriveWay method definitions
      85              : // ===========================================================================
      86              : 
      87              : 
      88        18131 : MSDriveWay::MSDriveWay(const MSLink* origin, const std::string& id, bool temporary) :
      89        36262 :     MSMoveReminder("DriveWay_" + (temporary ? "tmp" : id)),
      90              :     Named(id),
      91         9182 :     myNumericalID(temporary ? -1 : myGlobalDriveWayIndex++),
      92        18131 :     myOrigin(origin),
      93        18131 :     myActive(nullptr),
      94        18131 :     myCoreSize(0),
      95        18131 :     myForwardEdgeCount(0),
      96        18131 :     myFoundSignal(false),
      97        18131 :     myFoundJump(false),
      98        18131 :     myTerminateRoute(false),
      99        18131 :     myAbortedBuild(false),
     100        18131 :     myBidiEnded(false),
     101        63575 :     myIsSubDriveway(false)
     102        18131 : {}
     103              : 
     104              : 
     105        27313 : MSDriveWay::~MSDriveWay() {
     106        20107 :     for (const MSDriveWay* sub : mySubDriveWays) {
     107         1976 :         delete sub;
     108              :     }
     109              :     mySubDriveWays.clear();
     110        81706 : }
     111              : 
     112              : void
     113        38670 : MSDriveWay::cleanup() {
     114        38670 :     myGlobalDriveWayIndex = 0;
     115              :     myBlockLengthWarnings.clear();
     116        38670 :     myWriteVehicles = false;
     117              : 
     118        41412 :     for (auto item : myDepartureDriveways) {
     119         5618 :         for (MSDriveWay* dw : item.second) {
     120         2876 :             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        38670 : }
     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        39121 : 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        78242 :     if (veh.isVehicle() && (enteredLane == myLane || (MSGlobals::gUseMesoSim && veh.getEdge() == &myLane->getEdge()))
     147        61092 :             && (reason == NOTIFICATION_DEPARTED || reason == NOTIFICATION_JUNCTION || reason == NOTIFICATION_PARKING)) {
     148        21887 :         SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     149        21887 :         MSRouteIterator firstIt = std::find(sveh.getCurrentRouteEdge(), sveh.getRoute().end(), myLane->getNextNormal());
     150        21887 :         if (match(firstIt, sveh.getRoute().end())) {
     151              :             if (myTrains.count(&sveh) == 0) {
     152        18553 :                 enterDriveWay(sveh, reason);
     153              :             }
     154        18582 :             return true;
     155              :         }
     156        17234 :     } else if (reason == NOTIFICATION_REROUTE) {
     157              :         assert(veh.isVehicle());
     158        17176 :         SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     159              :         assert(myTrains.count(&sveh) == 0);
     160        17176 :         int movedPast = matchesPastRoute(sveh);
     161              :         // vehicle must still be one the drivway
     162        17176 :         if (movedPast >= 0 && movedPast < myForwardEdgeCount) {
     163          114 :             enterDriveWay(sveh, reason);
     164          114 :             return true;
     165              :         }
     166              :     }
     167              :     return false;
     168              : }
     169              : 
     170              : 
     171              : bool
     172       102315 : 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       102315 :     if (veh.isVehicle()) {
     178              :         // leaving network with departure, teleport etc
     179       102315 :         if (reason != MSMoveReminder::NOTIFICATION_JUNCTION && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     180         5777 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     181         5777 :             if (myWriteVehicles) {
     182          524 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     183              :             }
     184         5777 :             return false;
     185        96538 :         } else if (MSGlobals::gUseMesoSim && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     186              :             // notifyLeave is called before moving the route iterator
     187         7100 :             const MSLane* leftLane = (*(dynamic_cast<SUMOVehicle&>(veh).getCurrentRouteEdge()))->getLanes().front();
     188         7100 :             return notifyLeaveBack(veh, reason, leftLane);
     189              :         } else {
     190              :             return true;
     191              :         }
     192              :     } else {
     193              :         return false;
     194              :     }
     195              : }
     196              : 
     197              : 
     198              : bool
     199        68360 : 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        68360 :     if (veh.isVehicle()) {
     204        68360 :         if (leftLane == myForward.back() && (veh.getBackLane() != leftLane->getBidiLane() || MSGlobals::gUseMesoSim)) {
     205        12876 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     206        12876 :             if (myWriteVehicles) {
     207         1228 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     208              :             }
     209        12876 :             return false;
     210              :         } else {
     211        55484 :             return true;
     212              :         }
     213              :     } else {
     214              :         return false;
     215              :     }
     216              : }
     217              : 
     218              : 
     219              : bool
     220          967 : 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          967 :     SUMOVehicle* sveh = dynamic_cast<SUMOVehicle*>(&veh);
     226              :     assert(myTrains.count(sveh) != 0);
     227          967 :     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           54 :     if (myWriteVehicles) {
     234           26 :         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        18143 : MSDriveWay::matchesPastRoute(SUMOVehicle& sveh) const {
     243              :     // look backwards along the route to find the driveway lane
     244        18143 :     const ConstMSEdgeVector& routeEdges = sveh.getRoute().getEdges();
     245       304215 :     for (int i = sveh.getRoutePosition(); i >= 0; i--) {
     246       303773 :         if (routeEdges[i] == myLane->getNextNormal()) {
     247              :             MSRouteIterator firstIt = routeEdges.begin() + i;
     248        17701 :             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        17647 :                 return sveh.getRoutePosition() - i;
     252              :             }
     253           54 :             break;
     254              :         }
     255              :     }
     256              :     return -1;
     257              : }
     258              : 
     259              : 
     260              : void
     261        18667 : MSDriveWay::enterDriveWay(SUMOVehicle& sveh, Notification reason) {
     262        18667 :     myTrains.insert(&sveh);
     263        18667 :     if (myOrigin != nullptr) {
     264        12652 :         MSRailSignalControl::getInstance().notifyApproach(myOrigin);
     265              :     }
     266        49453 :     for (const MSDriveWay* foe : myFoes) {
     267        30786 :         if (foe->myOrigin != nullptr) {
     268        22253 :             MSRailSignalControl::getInstance().notifyApproach(foe->myOrigin);
     269              :         }
     270              :     }
     271        18667 :     if (myWriteVehicles) {
     272         1774 :         myVehicleEvents.push_back(VehicleEvent(SIMSTEP, true, sveh.getID(), reason));
     273              :     }
     274        18667 : }
     275              : 
     276              : bool
     277       594262 : MSDriveWay::reserve(const Approaching& closest, MSEdgeVector& occupied) {
     278       594262 :     if (foeDriveWayOccupied(true, closest.first, occupied)) {
     279              :         return false;
     280              :     }
     281       481192 :     for (MSLink* foeLink : myConflictLinks) {
     282       127548 :         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       353644 :     myActive = closest.first;
     292       353644 :     return true;
     293              : }
     294              : 
     295              : 
     296              : bool
     297       127548 : 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       127548 :     if (foeLink->getApproaching().size() > 0) {
     304        43055 :         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        43055 :         if (foe.first == veh.first) {
     311        43055 :             return false;
     312              :         }
     313              :         const MSTrafficLightLogic* foeTLL = foeLink->getTLLogic();
     314              :         assert(foeTLL != nullptr);
     315        38664 :         const MSRailSignal* constFoeRS = dynamic_cast<const MSRailSignal*>(foeTLL);
     316              :         MSRailSignal* foeRS = const_cast<MSRailSignal*>(constFoeRS);
     317        38664 :         if (foeRS != nullptr) {
     318        38664 :             const MSDriveWay& foeDriveWay = foeRS->retrieveDriveWayForVeh(foeLink->getTLIndex(), foe.first);
     319              :             MSEdgeVector occupied;
     320        62423 :             if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied) ||
     321        39155 :                     !foeRS->constraintsAllow(foe.first) ||
     322        30132 :                     !overlap(foeDriveWay) ||
     323        53400 :                     !isFoeOrSubFoe(&foeDriveWay) ||
     324        12192 :                     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        26563 :                 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        12101 :             const bool yield = mustYield(veh, foe);
     355        12101 :             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        12101 :             return yield;
     362        38664 :         }
     363              :     }
     364              :     return false;
     365              : }
     366              : 
     367              : 
     368              : bool
     369        24092 : MSDriveWay::isFoeOrSubFoe(const MSDriveWay* foe) const {
     370        24092 :     if (std::find(myFoes.begin(), myFoes.end(), foe) != myFoes.end()) {
     371              :         return true;
     372              :     }
     373        15991 :     for (const MSDriveWay* sub : foe->mySubDriveWays) {
     374         9356 :         if (isFoeOrSubFoe(sub)) {
     375              :             return true;
     376              :         }
     377              :     }
     378              :     return false;
     379              : }
     380              : 
     381              : 
     382              : bool
     383        12101 : MSDriveWay::mustYield(const Approaching& veh, const Approaching& foe) {
     384        12101 :     if (foe.second.arrivalSpeedBraking == veh.second.arrivalSpeedBraking) {
     385         7772 :         if (foe.second.arrivalTime == veh.second.arrivalTime) {
     386          552 :             if (foe.first->getSpeed() == veh.first->getSpeed()) {
     387          534 :                 if (foe.second.dist == veh.second.dist) {
     388          498 :                     if (foe.first->getWaitingTime() == veh.first->getWaitingTime()) {
     389          480 :                         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           18 :                 return foe.first->getSpeed() > veh.first->getSpeed();
     398              :             }
     399              :         } else {
     400         7220 :             return foe.second.arrivalTime < veh.second.arrivalTime;
     401              :         }
     402              :     } else {
     403         4329 :         return foe.second.arrivalSpeedBraking > veh.second.arrivalSpeedBraking;
     404              :     }
     405              : }
     406              : 
     407              : 
     408              : bool
     409        16035 : MSDriveWay::conflictLaneOccupied(bool store, const SUMOVehicle* ego) const {
     410       204411 :     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      3299067 : MSDriveWay::foeDriveWayApproached() const {
     471      6672284 :     for (const MSDriveWay* foeDW : myFoes) {
     472      6609743 :         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      4553604 : MSDriveWay::foeDriveWayOccupied(bool store, const SUMOVehicle* ego, MSEdgeVector& occupied) const {
     487     12389205 :     for (const MSDriveWay* foeDW : myFoes) {
     488      8711636 :         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       899852 :             if (foeDW->myTrains.size() == 1) {
     495       898928 :                 SUMOVehicle* foe = *foeDW->myTrains.begin();
     496       898928 :                 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        23917 :                     continue;
     503              :                 }
     504       878330 :                 if (hasJoin(ego, foe)) {
     505           36 :                     continue;
     506              :                 }
     507              :             }
     508       879218 :             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       879218 :             if (useSiding.first) {
     520         3283 :                 continue;
     521              :             } else {
     522       875935 :                 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      1753026 :                 for (const SUMOVehicle* foe : foeDW->myTrains) {
     529       877091 :                     occupied.push_back(const_cast<MSEdge*>(foe->getEdge()));
     530       877091 :                     MSEdge* bidi = const_cast<MSEdge*>(foe->getEdge()->getBidiEdge());
     531       877091 :                     if (bidi != nullptr) {
     532       473262 :                         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       289637 :                 if (ego != nullptr && MSGlobals::gTimeToTeleportRSDeadlock > 0
     537      1118500 :                         && (ego->getWaitingTime() > ego->getVehicleType().getCarFollowModel().getStartupDelay() || !ego->isOnRoad())) {
     538              :                     // if there is an occupied siding, it becomes part of the waitRelation
     539       134115 :                     SUMOVehicle* foe = *(useSiding.second == nullptr ? foeDW : useSiding.second)->myTrains.begin();
     540       134115 :                     const MSRailSignal* rs = myOrigin != nullptr ? dynamic_cast<const MSRailSignal*>(myOrigin->getTLLogic()) : nullptr;
     541       134115 :                     MSRailSignalControl::getInstance().addWaitRelation(ego, rs, foe);
     542              :                 }
     543       875935 :                 return true;
     544              :             }
     545      7811784 :         } else if (foeDW != this && isDepartDriveway() && !foeDW->isDepartDriveway()) {
     546         8986 :             if (foeDW->myOrigin->getApproaching().size() > 0) {
     547         1448 :                 Approaching foeA = foeDW->myOrigin->getClosest();
     548         1448 :                 const SUMOVehicle* foe = foeA.first;
     549         1448 :                 if (foeA.second.dist < foe->getBrakeGap(true)) {
     550          128 :                     MSRouteIterator firstIt = std::find(foe->getCurrentRouteEdge(), foe->getRoute().end(), foeDW->myRoute.front());
     551          128 :                     if (firstIt != foe->getRoute().end()) {
     552          128 :                         if (foeDW->match(firstIt, foe->getRoute().end())) {
     553          100 :                             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          100 :                             if (useSiding) {
     560              :                                 //std::cout << SIMTIME << " " << getID() << " ego=" << ego->getID() << " foeDW=" << foeDW->getID() << " myFoes=" << toString(myFoes) << "\n";
     561            0 :                                 continue;
     562              :                             } else {
     563          100 :                                 return true;
     564              :                             }
     565              :                         }
     566              :                     }
     567              :                 }
     568              :             }
     569              :         }
     570              :     }
     571      3678539 :     for (const std::set<const MSDriveWay*>& dlFoes : myDeadlocks) {
     572              :         bool allOccupied = true;
     573         9852 :         for (const MSDriveWay* dlFoe : dlFoes) {
     574         7160 :             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         3662 :         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         8360 :             for (const MSDriveWay* dlFoe : dlFoes) {
     587         5668 :                 MSRailSignal::blockingDriveWays().push_back(dlFoe);
     588              :             }
     589              :             return true;
     590              :         }
     591              :     }
     592              :     return false;
     593              : }
     594              : 
     595              : 
     596              : bool
     597       878330 : MSDriveWay::hasJoin(const SUMOVehicle* ego, const SUMOVehicle* foe) {
     598       878330 :     if (ego != nullptr && !MSGlobals::gUseMesoSim) {
     599       228434 :         std::string joinVehicle = "";
     600       228434 :         const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
     601       228434 :         if (stop != nullptr) {
     602        92437 :             joinVehicle = stop->join;
     603              :         }
     604       228434 :         if (joinVehicle == "" && !ego->hasDeparted() && ego->getStops().size() > 1) {
     605              :             // check one more stop
     606         5963 :             auto it = ego->getStops().begin();
     607              :             std::advance(it, 1);
     608         5963 :             joinVehicle = it->pars.join;
     609              :         }
     610       228434 :         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       228410 :         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       894696 : MSDriveWay::canUseSiding(const SUMOVehicle* ego, const MSDriveWay* foe, bool recurse) const {
     641              :     auto it = mySidings.find(foe);
     642       894696 :     if (it != mySidings.end()) {
     643        11860 :         for (auto siding : it->second) {
     644              :             // assume siding is usuable when computing state for unapproached signal (ego == nullptr)
     645        11068 :             if (ego == nullptr || siding.length >= ego->getLength()) {
     646              :                 // if the siding is already "reserved" by another vehicle we cannot use it here
     647         7717 :                 const MSEdge* sidingEnd = myRoute[siding.end];
     648        15013 :                 for (MSDriveWay* sidingApproach : myEndingDriveways[sidingEnd]) {
     649        11429 :                     if (!sidingApproach->myTrains.empty()) {
     650              :                         // possibly the foe vehicle can use the other part of the siding
     651         4343 :                         if (recurse) {
     652              :                             const SUMOVehicle* foeVeh = nullptr;
     653         3186 :                             if (!foe->myTrains.empty()) {
     654         3178 :                                 foeVeh = *foe->myTrains.begin();
     655            8 :                             } else if (foe->myOrigin != nullptr && foe->myOrigin->getApproaching().size() > 0) {
     656            8 :                                 foeVeh = foe->myOrigin->getClosest().first;
     657              :                             }
     658         3186 :                             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         3186 :                             if (foe->canUseSiding(foeVeh, this, false).first) {
     663          210 :                                 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         4133 :                         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         3584 :                 return std::make_pair(true, nullptr);
     682              :             }
     683              :         }
     684              :     }
     685       886979 :     return std::make_pair(false, nullptr);
     686              : }
     687              : 
     688              : bool
     689        18485 : MSDriveWay::overlap(const MSDriveWay& other) const {
     690        41747 :     for (int i = 0; i < myCoreSize; i++) {
     691       158977 :         for (int j = 0; j < other.myCoreSize; j++) {
     692       135715 :             const MSEdge* edge = myRoute[i];
     693       135715 :             const MSEdge* edge2 = other.myRoute[j];
     694              :             if (edge->getToJunction() == edge2->getToJunction()
     695       135715 :                     || 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        20759 : MSDriveWay::flankConflict(const MSDriveWay& other) const {
     707        51535 :     for (const MSLane* lane : myForward) {
     708       194381 :         for (const MSLane* lane2 : other.myForward) {
     709       158805 :             if (lane == lane2) {
     710              :                 return true;
     711              :             }
     712              :         }
     713       291399 :         for (const MSLane* lane2 : other.myBidi) {
     714       258297 :             if (lane == lane2) {
     715         4614 :                 if (bidiBlockedBy(other)) {
     716              :                     // it's only a deadlock if both trains block symmetrically
     717              :                     return true;
     718              :                 }
     719              :             }
     720              :         }
     721       261389 :         for (const MSLane* lane2 : other.myBidiExtended) {
     722       230613 :             if (lane == lane2) {
     723         7877 :                 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        13040 : MSDriveWay::crossingConflict(const MSDriveWay& other) const {
     736        36570 :     for (const MSLane* lane : myForward) {
     737       122080 :         for (const MSLane* lane2 : other.myForward) {
     738        98550 :             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        12770 : MSDriveWay::bidiBlockedBy(const MSDriveWay& other) const {
     749        88126 :     for (const MSLane* lane : myBidi) {
     750       308612 :         for (const MSLane* lane2 : other.myForward) {
     751       233256 :             if (lane == lane2) {
     752              :                 return true;
     753              :             }
     754              :         }
     755              :     }
     756        42952 :     for (const MSLane* lane : myBidiExtended) {
     757       147833 :         for (const MSLane* lane2 : other.myForward) {
     758       114662 :             if (lane == lane2) {
     759         1883 :                 if (overlap(other)) {
     760              :                     return true;
     761              :                 }
     762              :             }
     763              :         }
     764              :     }
     765              :     return false;
     766              : }
     767              : 
     768              : 
     769              : bool
     770         7129 : MSDriveWay::bidiBlockedByEnd(const MSDriveWay& other) const {
     771         7129 :     const MSLane* end = other.myForward.back();
     772        48220 :     for (const MSLane* lane : myBidi) {
     773        43755 :         if (lane == end) {
     774              :             return true;
     775              :         }
     776              :     }
     777        18186 :     for (const MSLane* lane : myBidiExtended) {
     778        14927 :         if (lane == end) {
     779         1206 :             if (overlap(other)) {
     780              :                 return true;
     781              :             }
     782              :         }
     783              :     }
     784              :     return false;
     785              : }
     786              : 
     787              : bool
     788         1064 : MSDriveWay::forwardRouteConflict(std::set<const MSEdge*> forward, const MSDriveWay& other, bool secondCheck) {
     789              :     int i = 0;
     790         4365 :     for (const MSEdge* edge2 : other.myRoute) {
     791         4250 :         if (i == other.myCoreSize) {
     792              :             return false;
     793              :         }
     794         4250 :         i++;
     795         4250 :         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         7207 : MSDriveWay::writeBlocks(OutputDevice& od) const {
     808        12622 :     od.openTag(myIsSubDriveway ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
     809         7207 :     od.writeAttr(SUMO_ATTR_ID, myID);
     810         7207 :     od.writeAttr(SUMO_ATTR_VEHICLE, myFirstVehicle);
     811         7207 :     od.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
     812         7207 :     if (myCoreSize != (int)myRoute.size()) {
     813            0 :         od.writeAttr("core", myCoreSize);
     814              :     }
     815         7207 :     od.openTag("forward");
     816         7207 :     od.writeAttr(SUMO_ATTR_LANES, toString(myForward));
     817         7207 :     od.closeTag();
     818         7207 :     if (!myIsSubDriveway) {
     819         5415 :         od.openTag("bidi");
     820        10830 :         od.writeAttr(SUMO_ATTR_LANES, toString(myBidi));
     821         5415 :         if (myBidiExtended.size() > 0) {
     822         1074 :             od.lf();
     823         1074 :             od << "                   ";
     824         2148 :             od.writeAttr("deadlockCheck", toString(myBidiExtended));
     825              :         }
     826         5415 :         od.closeTag();
     827         5415 :         od.openTag("flank");
     828         5415 :         od.writeAttr(SUMO_ATTR_LANES, toString(myFlank));
     829         5415 :         od.closeTag();
     830              : 
     831        10830 :         od.openTag("conflictLinks");
     832              : 
     833              :         std::vector<std::string> signals;
     834         8636 :         for (MSLink* link : myConflictLinks) {
     835         6442 :             signals.push_back(getTLLinkID(link));
     836              :         }
     837        10830 :         od.writeAttr("signals", joinToStringSorting(signals, " "));
     838        10830 :         od.closeTag();
     839              : 
     840              :         std::vector<std::string> foes;
     841        18469 :         for (MSDriveWay* dw : myFoes) {
     842        13054 :             foes.push_back(dw->myID);
     843              :         }
     844         5415 :         if (foes.size() > 0) {
     845         5376 :             od.openTag("foes");
     846        10752 :             od.writeAttr("driveWays", joinToStringSorting(foes, " "));
     847        10752 :             od.closeTag();
     848              :         }
     849         5802 :         for (auto item : mySidings) {
     850          387 :             od.openTag("sidings");
     851          774 :             od.writeAttr("foe", item.first->getID());
     852          927 :             for (auto siding : item.second) {
     853          540 :                 od.openTag("siding");
     854          540 :                 od.writeAttr("start", myRoute[siding.start]->getID());
     855          540 :                 od.writeAttr("end", myRoute[siding.end]->getID());
     856          540 :                 od.writeAttr("length", siding.length);
     857         1080 :                 od.closeTag();
     858              :             }
     859          774 :             od.closeTag();
     860              :         }
     861         5496 :         for (auto item : myDeadlocks) {
     862           81 :             od.openTag("deadlock");
     863          162 :             od.writeAttr("foes", joinNamedToStringSorting(item, " "));
     864          162 :             od.closeTag();
     865              :         }
     866         5415 :     }
     867        14414 :     od.closeTag(); // driveWay
     868              : 
     869         8999 :     for (const MSDriveWay* sub : mySubDriveWays) {
     870         1792 :         sub->writeBlocks(od);
     871              :     }
     872              : #ifdef DRIVEWAY_SANITY_CHECK
     873         7207 :     std::set<MSDriveWay*> uFoes(myFoes.begin(), myFoes.end());
     874         7207 :     if (uFoes.size() != myFoes.size()) {
     875            0 :         WRITE_WARNINGF("Duplicate foes in driveway '%'", getID());
     876              : 
     877              :     }
     878              : #endif
     879         7207 : }
     880              : 
     881              : 
     882              : void
     883          753 : MSDriveWay::writeBlockVehicles(OutputDevice& od) const {
     884         1330 :     od.openTag(myIsSubDriveway ? "subDriveWay" : "driveWay");
     885          753 :     od.writeAttr(SUMO_ATTR_ID, myID);
     886         2536 :     for (const VehicleEvent& ve : myVehicleEvents) {
     887         2672 :         od.openTag(ve.isEntry ? "entry" : "exit");
     888         1783 :         od.writeAttr(SUMO_ATTR_ID, ve.id);
     889         1783 :         od.writeAttr(SUMO_ATTR_TIME, time2string(ve.time));
     890         1783 :         od.writeAttr("reason", Notifications.getString(ve.reason));
     891         3566 :         od.closeTag(); // event
     892              :     }
     893         1506 :     od.closeTag(); // driveWay
     894              : 
     895          929 :     for (const MSDriveWay* sub : mySubDriveWays) {
     896          176 :         sub->writeBlockVehicles(od);
     897              :     }
     898          753 : }
     899              : 
     900              : 
     901              : void
     902         7206 : MSDriveWay::buildRoute(const MSLink* origin,
     903              :                        MSRouteIterator next, MSRouteIterator end,
     904              :                        LaneVisitedMap& visited,
     905              :                        std::set<MSLink*>& flankSwitches) {
     906         7206 :     double length = 0;
     907              :     bool seekForwardSignal = true;
     908              :     bool seekBidiSwitch = true;
     909              :     bool foundUnsafeSwitch = false;
     910         7206 :     MSLane* toLane = origin ? origin->getViaLaneOrLane() : (*next)->getLanes()[0];
     911        14412 :     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        46492 :     while ((seekForwardSignal || seekBidiSwitch)) {
     918        43759 :         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           56 :             if (myRoute.size() == 0 || myBlockLengthWarnings.count(myRoute.front()) == 0) {
     924          168 :                 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           56 :             myAbortedBuild = true;
     929              :             // length exceeded
     930              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     931              :             if (gDebugFlag4) {
     932              :                 std::cout << " abort: length=" << length << "\n";
     933              :             }
     934              : #endif
     935         4473 :             return;
     936              :         }
     937              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     938              :         if (gDebugFlag4) {
     939              :             std::cout << "   toLane=" << toLane->getID() << " visited=" << formatVisitedMap(visited) << "\n";
     940              :         }
     941              : #endif
     942        43703 :         const MSEdge* current = &toLane->getEdge();
     943        43703 :         if (current->isNormal()) {
     944        28268 :             myRoute.push_back(current);
     945        28268 :             if (next != end) {
     946              :                 next++;
     947              :             }
     948              :         }
     949        43703 :         appendMapIndex(visited, toLane);
     950        43703 :         length += toLane->getLength();
     951        43703 :         MSLane* bidi = toLane->getBidiLane();
     952        43703 :         if (seekForwardSignal) {
     953        24350 :             if (!foundUnsafeSwitch) {
     954        24350 :                 myForward.push_back(toLane);
     955        24350 :                 if (toLane->isNormal()) {
     956        16307 :                     myForwardEdgeCount++;
     957              :                 }
     958        24350 :                 if (myForward.size() == 1) {
     959         7206 :                     myLane = toLane;
     960         7206 :                     if (MSGlobals::gUseMesoSim) {
     961         2112 :                         MESegment* s = MSGlobals::gMesoNet->getSegmentForEdge(myLane->getEdge());
     962         2112 :                         s->addDetector(this, myLane->getIndex());
     963              :                     } else {
     964         5094 :                         toLane->addMoveReminder(this, false);
     965              :                     }
     966              :                 }
     967              :             }
     968        19353 :         } else if (bidi == nullptr) {
     969          737 :             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        43703 :         if (bidi != nullptr) {
     985        32223 :             if (foundUnsafeSwitch) {
     986         7983 :                 myBidiExtended.push_back(bidi);
     987              :             } else {
     988        24240 :                 myBidi.push_back(bidi);
     989              :             }
     990        32223 :             if (!seekForwardSignal) {
     991              :                 // look for switch that could protect from oncoming vehicles
     992        38622 :                 for (const auto& ili : bidi->getIncomingLanes()) {
     993        20006 :                     if (ili.viaLink->getDirection() == LinkDirection::TURN) {
     994         1465 :                         continue;
     995              :                     }
     996        40189 :                     for (const MSLink* const link : ili.lane->getLinkCont()) {
     997        21648 :                         if (link->getDirection() == LinkDirection::TURN) {
     998          985 :                             continue;
     999              :                         }
    1000        20663 :                         if (link->getViaLaneOrLane() != bidi) {
    1001         2122 :                             myCoreSize = (int)myRoute.size();
    1002              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1003              :                             if (gDebugFlag4) {
    1004              :                                 const MSEdge* const bidiNext = bidi->getNextNormal();
    1005              :                                 std::cout << "      found unsafe switch " << ili.viaLink->getDescription() << " (used=" << bidiNext->getID() << ")\n";
    1006              :                             }
    1007              : #endif
    1008              :                             // trains along our route beyond this switch might create deadlock
    1009              :                             foundUnsafeSwitch = true;
    1010              :                             // the switch itself must still be guarded to ensure safety
    1011         4446 :                             for (const auto& ili2 : bidi->getIncomingLanes()) {
    1012         2324 :                                 if (ili2.viaLink->getDirection() != LinkDirection::TURN) {
    1013         2218 :                                     flankSwitches.insert(ili.viaLink);
    1014              :                                 }
    1015              :                             }
    1016              :                         }
    1017              :                     }
    1018              :                 }
    1019              :             }
    1020              :         }
    1021              :         const std::vector<MSLink*>& links = toLane->getLinkCont();
    1022        43703 :         toLane = nullptr;
    1023        47331 :         for (const MSLink* const link : links) {
    1024        41433 :             if ((next != end && &link->getLane()->getEdge() == *next)
    1025        82223 :                     && isRailwayOrShared(link->getViaLaneOrLane()->getPermissions())) {
    1026        64420 :                 toLane = link->getViaLaneOrLane();
    1027        39286 :                 if (link->getTLLogic() != nullptr && link->getTLIndex() >= 0) {
    1028         7807 :                     if (link == origin) {
    1029            0 :                         WRITE_WARNINGF(TL("Found circular block after % (% edges, length %)"), warnID, toString(myRoute.size()), toString(length));
    1030              :                         //std::cout << getClickableTLLinkID(origin) << " circularBlock2=" << toString(myRoute) << "\n";
    1031            0 :                         myAbortedBuild = true;
    1032              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1033              :                         if (gDebugFlag4) {
    1034              :                             std::cout << " abort: found circle\n";
    1035              :                         }
    1036              : #endif
    1037            0 :                         return;
    1038              :                     }
    1039              :                     seekForwardSignal = false;
    1040         7807 :                     myFoundSignal = true;
    1041         7807 :                     seekBidiSwitch = bidi != nullptr;
    1042              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1043              :                     if (gDebugFlag4) {
    1044              :                         std::cout << "      found forwardSignal " << link->getTLLogic()->getID() << " seekBidiSwitch=" << seekBidiSwitch << "\n";
    1045              :                     }
    1046              : #endif
    1047              :                 }
    1048              :                 //if (links.size() > 1 && !foundUnsafeSwitch) {
    1049        39286 :                 if (isSwitch(link)) {
    1050              :                     // switch on driveway
    1051              :                     //std::cout << "mySwitchDriveWays " << getID() << " link=" << link->getDescription() << "\n";
    1052        22161 :                     mySwitchDriveWays[link].push_back(this);
    1053              :                 }
    1054        39286 :                 if (link->getLane()->getBidiLane() != nullptr && &link->getLane()->getEdge() == current->getBidiEdge()) {
    1055              :                     // reversal on driveway
    1056         1075 :                     myReversalDriveWays[current].push_back(this);
    1057         1075 :                     myReversals.push_back(current);
    1058              :                 }
    1059              :                 break;
    1060              :             }
    1061              :         }
    1062        43703 :         if (toLane == nullptr) {
    1063         4417 :             if (next != end) {
    1064              :                 // no connection found, jump to next route edge
    1065              :                 toLane = (*next)->getLanes()[0];
    1066              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1067              :                 if (gDebugFlag4) {
    1068              :                     std::cout << "      abort: turn-around or jump\n";
    1069              :                 }
    1070              : #endif
    1071           79 :                 myFoundJump = true;
    1072           79 :                 return;
    1073              :             } else {
    1074              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1075              :                 if (gDebugFlag4) {
    1076              :                     std::cout << "      abort: no next lane available\n";
    1077              :                 }
    1078              : #endif
    1079         4338 :                 myTerminateRoute = true;
    1080         4338 :                 return;
    1081              :             }
    1082              :         }
    1083              :     }
    1084         2733 :     myBidiEnded = !seekBidiSwitch;
    1085              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1086              :     if (gDebugFlag4) {
    1087              :         std::cout << " normalEnd myBidiEnded=" << myBidiEnded << "\n";
    1088              :     }
    1089              : #endif
    1090              : }
    1091              : 
    1092              : 
    1093              : bool
    1094        47151 : MSDriveWay::isSwitch(const MSLink* link) {
    1095        90996 :     for (const MSLink* other : link->getLaneBefore()->getNormalPredecessorLane()->getLinkCont()) {
    1096        52396 :         if (other->getLane() != link->getLane() && !other->isTurnaround()) {
    1097              :             return true;
    1098              :         }
    1099              :     }
    1100        63582 :     for (auto ili : link->getLane()->getIncomingLanes()) {
    1101        44715 :         if (ili.viaLink != link && !ili.viaLink->isTurnaround()) {
    1102              :             return true;
    1103              :         }
    1104              :     }
    1105        18867 :     const MSLane* bidi = link->getLane()->getBidiLane();
    1106        18867 :     if (bidi != nullptr) {
    1107        30444 :         for (const MSLink* other : bidi->getLinkCont()) {
    1108        16076 :             if (other->getLane() != link->getLaneBefore()->getNormalPredecessorLane()->getBidiLane() && !other->isTurnaround()) {
    1109              :                 return true;
    1110              :             }
    1111              :         }
    1112              :     }
    1113              :     return false;
    1114              : }
    1115              : 
    1116              : 
    1117              : void
    1118        28824 : MSDriveWay::checkFlanks(const MSLink* originLink, const std::vector<const MSLane*>& lanes, const LaneVisitedMap& visited, bool allFoes, std::set<MSLink*>& flankSwitches) const {
    1119              : #ifdef DEBUG_CHECK_FLANKS
    1120              :     std::cout << " checkFlanks lanes=" << toString(lanes) << " allFoes=" << allFoes << "\n";
    1121              : #endif
    1122        17320 :     const MSLink* reverseOriginLink = originLink != nullptr && originLink->getLane()->getBidiLane() != nullptr && originLink->getLaneBefore()->getBidiLane() != nullptr
    1123        37660 :                                       ? originLink->getLane()->getBidiLane()->getLinkTo(originLink->getLaneBefore()->getBidiLane())
    1124              :                                       : nullptr;
    1125              :     //std::cout << "   originLink=" << originLink->getDescription() << "\n";
    1126         8836 :     if (reverseOriginLink != nullptr) {
    1127         8836 :         reverseOriginLink = reverseOriginLink->getCorrespondingExitLink();
    1128              :         //std::cout << "   reverseOriginLink=" << reverseOriginLink->getDescription() << "\n";
    1129              :     }
    1130        87679 :     for (int i = 0; i < (int)lanes.size(); i++) {
    1131        58855 :         const MSLane* lane = lanes[i];
    1132        58855 :         const MSLane* prev = i > 0 ? lanes[i - 1] : nullptr;
    1133        58855 :         const MSLane* next = i + 1 < (int)lanes.size() ? lanes[i + 1] : nullptr;
    1134        58855 :         if (lane->isInternal()) {
    1135        19263 :             continue;
    1136              :         }
    1137        83334 :         for (auto ili : lane->getIncomingLanes()) {
    1138        52779 :             if (ili.viaLink == originLink
    1139        41592 :                     || ili.viaLink == reverseOriginLink
    1140        39358 :                     || ili.viaLink->getDirection() == LinkDirection::TURN
    1141        78447 :                     || ili.viaLink->getDirection() == LinkDirection::TURN_LEFTHAND) {
    1142         9037 :                 continue;
    1143              :             }
    1144        34705 :             if (ili.lane != prev && ili.lane != next) {
    1145              : #ifdef DEBUG_CHECK_FLANKS
    1146              :                 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";
    1147              : #endif
    1148              :                 flankSwitches.insert(ili.viaLink);
    1149        26288 :             } else if (allFoes) {
    1150              :                 // link is part of the driveway, find foes that cross the driveway without entering
    1151        10568 :                 checkCrossingFlanks(ili.viaLink, visited, flankSwitches);
    1152              :             }
    1153              :         }
    1154              :     }
    1155        28824 : }
    1156              : 
    1157              : 
    1158              : void
    1159        10568 : MSDriveWay::checkCrossingFlanks(MSLink* dwLink, const LaneVisitedMap& visited, std::set<MSLink*>& flankSwitches) const {
    1160              : #ifdef DEBUG_CHECK_FLANKS
    1161              :     std::cout << "  checkCrossingFlanks  dwLink=" << dwLink->getDescription() << " visited=" << formatVisitedMap(visited) << "\n";
    1162              : #endif
    1163              :     const MSJunction* junction = dwLink->getJunction();
    1164        10568 :     if (junction == nullptr) {
    1165              :         return; // unregulated junction;
    1166              :     }
    1167        10494 :     const MSJunctionLogic* logic = junction->getLogic();
    1168        10494 :     if (logic == nullptr) {
    1169              :         return; // unregulated junction;
    1170              :     }
    1171        54044 :     for (const MSEdge* in : junction->getIncoming()) {
    1172        43610 :         if (in->isInternal()) {
    1173        22263 :             continue;
    1174              :         }
    1175        42748 :         for (MSLane* inLane : in->getLanes()) {
    1176        21401 :             const MSLane* inBidi = inLane->getBidiLane();
    1177        30089 :             if (isRailwayOrShared(inLane->getPermissions()) && visited.count(inLane) == 0 && (inBidi == nullptr || visited.count(inBidi) == 0)) {
    1178         7522 :                 for (MSLink* link : inLane->getLinkCont()) {
    1179         3910 :                     if (link->getIndex() >= 0 && logic->getFoesFor(dwLink->getIndex()).test(link->getIndex())
    1180        10180 :                             && visited.count(link->getLane()) == 0) {
    1181              : #ifdef DEBUG_CHECK_FLANKS
    1182              :                         std::cout << " add crossing flankSwitch junction=" << junction->getID() << " index=" << link->getIndex() << "\n";
    1183              : #endif
    1184          533 :                         if (link->getViaLane() == nullptr) {
    1185              :                             flankSwitches.insert(link);
    1186              :                         } else {
    1187              :                             flankSwitches.insert(link->getViaLane()->getLinkCont().front());
    1188              :                         }
    1189              :                     }
    1190              :                 }
    1191              :             }
    1192              :         }
    1193              :     }
    1194              : }
    1195              : 
    1196              : void
    1197        11107 : MSDriveWay::findFlankProtection(MSLink* link, MSLink* origLink, std::vector<const MSLane*>& flank) {
    1198              : #ifdef DEBUG_CHECK_FLANKS
    1199              :     std::cout << "  findFlankProtection link=" << link->getDescription() << " origLink=" << origLink->getDescription() << "\n";
    1200              : #endif
    1201        11107 :     if (link->getCorrespondingEntryLink()->getTLLogic() != nullptr && link->getJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1202         2397 :         MSLink* entry = const_cast<MSLink*>(link->getCorrespondingEntryLink());
    1203              :         // guarded by signal
    1204              : #ifdef DEBUG_CHECK_FLANKS
    1205              :         std::cout << "   flank guarded by " << entry->getTLLogic()->getID() << "\n";
    1206              : #endif
    1207              :         // @note, technically it's enough to collect links from foe driveways
    1208              :         // but this also adds "unused" conflict links which may aid comprehension
    1209         2397 :         myConflictLinks.push_back(entry);
    1210         2397 :         addFoes(entry);
    1211              :     } else {
    1212              :         const MSLane* lane = link->getLaneBefore();
    1213              :         std::vector<MSLink*> predLinks;
    1214        17342 :         for (auto ili : lane->getIncomingLanes()) {
    1215         8632 :             if (!ili.viaLink->isTurnaround()) {
    1216         8375 :                 predLinks.push_back(ili.viaLink);
    1217              :             }
    1218              :         }
    1219         8710 :         if (predLinks.size() > 1) {
    1220              :             // this is a switch
    1221              : #ifdef DEBUG_ADD_FOES
    1222              :             std::cout << "    predecessors of " << link->getDescription() << " isSwitch\n";
    1223              : #endif
    1224          765 :             for (MSLink* pred : predLinks) {
    1225          510 :                 addSwitchFoes(pred);
    1226              :             }
    1227         8455 :         } else if (predLinks.size() == 1) {
    1228         7865 :             if (isSwitch(link)) {
    1229         6730 :                 addSwitchFoes(link);
    1230              :             } else {
    1231              :                 // continue upstream via single predecessor
    1232         1135 :                 findFlankProtection(predLinks.front(), origLink, flank);
    1233              :             }
    1234              :         }
    1235              :         // check for insertions
    1236         8710 :         if (myDepartureDriveways.count(&lane->getEdge()) != 0) {
    1237          337 :             for (MSDriveWay* foe : myDepartureDriveways[&lane->getEdge()]) {
    1238          167 :                 if (flankConflict(*foe) || crossingConflict(*foe)) {
    1239              : #ifdef DEBUG_ADD_FOES
    1240              :                     std::cout << "  foe " << foe->getID() << " departs on flank=" << lane->getID() << "\n";
    1241              : #endif
    1242          127 :                     myFoes.push_back(foe);
    1243              :                 } else {
    1244              : #ifdef DEBUG_ADD_FOES
    1245              :                     std::cout << "  cand foe " << foe->getID() << " departs on flank=" << lane->getID() << " rejected\n";
    1246              : #endif
    1247              :                 }
    1248              :             }
    1249              :         }
    1250         8710 :     }
    1251        11107 : }
    1252              : 
    1253              : 
    1254              : void
    1255         7240 : MSDriveWay::addSwitchFoes(MSLink* link) {
    1256              :     auto it = mySwitchDriveWays.find(link);
    1257         7240 :     if (it != mySwitchDriveWays.end()) {
    1258              : #ifdef DEBUG_ADD_FOES
    1259              :         std::cout << "   driveway " << myID << " addSwitchFoes for link " << link->getDescription() << "\n";
    1260              : #endif
    1261         7797 :         for (MSDriveWay* foe : it->second) {
    1262         5313 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1263              : #ifdef DEBUG_ADD_FOES
    1264              :                 std::cout << "   foe=" << foe->myID
    1265              :                           << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this)
    1266              :                           << " cc1=" << crossingConflict(*foe) << " cc2=" << foe->crossingConflict(*this) << "\n";
    1267              : #endif
    1268         2909 :                 myFoes.push_back(foe);
    1269              :             } else {
    1270              : #ifdef DEBUG_ADD_FOES
    1271              :                 std::cout << "   cand=" << foe->myID << "\n";
    1272              : #endif
    1273              :             }
    1274              :         }
    1275              :     }
    1276         7240 : }
    1277              : 
    1278              : 
    1279              : MSDriveWay*
    1280         7206 : MSDriveWay::buildDriveWay(const std::string& id, const MSLink* link, MSRouteIterator first, MSRouteIterator end) {
    1281              :     // collect lanes and links that are relevant for setting this signal for the current driveWay
    1282              :     // For each driveway we collect
    1283              :     //   - conflictLanes (signal must be red if any conflict lane is occupied)
    1284              :     //   - conflictLinks (signal must be red if any conflict link is approached by a vehicle
    1285              :     //      - that cannot break in time (arrivalSpeedBraking > 0)
    1286              :     //      - approached by a vehicle with higher switching priority (see #3941)
    1287              :     // These objects are construct in steps:
    1288              :     //
    1289              :     // forwardBlock
    1290              :     // - search forward recursive from outgoing lane until controlled railSignal link found
    1291              :     //   -> add all found lanes to conflictLanes
    1292              :     //
    1293              :     // bidiBlock (if any forwardBlock edge has bidi edge)
    1294              :     // - search bidi backward recursive until first switch
    1295              :     //   - from switch search backward recursive all other incoming until controlled rail signal link
    1296              :     //     -> add final links to conflictLinks
    1297              :     //
    1298              :     // flanks
    1299              :     // - search backward recursive from flanking switches
    1300              :     //   until controlled railSignal link or protecting switch is found
    1301              :     //   -> add all found lanes to conflictLanes
    1302              :     //   -> add final links to conflictLinks
    1303         7206 :     MSDriveWay* dw = new MSDriveWay(link, id);
    1304              :     LaneVisitedMap visited;
    1305              :     std::vector<const MSLane*> before;
    1306         7206 :     MSLane* fromBidi = nullptr;
    1307         7206 :     if (link != nullptr) {
    1308         4330 :         appendMapIndex(visited, link->getLaneBefore());
    1309         4330 :         fromBidi = link->getLaneBefore()->getBidiLane();
    1310              :     }
    1311              :     std::set<MSLink*> flankSwitches; // list of switches that threaten the driveway and for which protection must be found
    1312              : 
    1313         7206 :     if (fromBidi != nullptr) {
    1314         2282 :         before.push_back(fromBidi);
    1315              :     }
    1316         7206 :     dw->buildRoute(link, first, end, visited, flankSwitches);
    1317         7206 :     dw->myCoreSize = (int)dw->myRoute.size();
    1318         7206 :     dw->checkFlanks(link, dw->myForward, visited, true, flankSwitches);
    1319         7206 :     dw->checkFlanks(link, dw->myBidi, visited, false, flankSwitches);
    1320         7206 :     dw->checkFlanks(link, before, visited, true, flankSwitches);
    1321        15763 :     for (MSLink* fsLink : flankSwitches) {
    1322              : #ifdef DEBUG_ADD_FOES
    1323              :         if (DEBUG_COND_DW) {
    1324              :             std::cout << " fsLink=" << fsLink->getDescription() << "\n";
    1325              :         }
    1326              : #endif
    1327         8557 :         dw->findFlankProtection(fsLink, fsLink, dw->myFlank);
    1328              :     }
    1329              :     std::set<MSLink*> flankSwitchesBidiExtended;
    1330         7206 :     dw->checkFlanks(link, dw->myBidiExtended, visited, false, flankSwitchesBidiExtended);
    1331         8621 :     for (MSLink* const flink : flankSwitchesBidiExtended) {
    1332              : #ifdef DEBUG_ADD_FOES
    1333              :         if (DEBUG_COND_DW) {
    1334              :             std::cout << " fsLinkExtended=" << flink->getDescription() << "\n";
    1335              :         }
    1336              : #endif
    1337         1415 :         dw->findFlankProtection(flink, flink, dw->myBidiExtended);
    1338              :     }
    1339         7206 :     MSRailSignal* rs = link ? const_cast<MSRailSignal*>(static_cast<const MSRailSignal*>(link->getTLLogic())) : nullptr;
    1340        11536 :     const bool movingBlock = (rs && rs->isMovingBlock()) || (!rs && OptionsCont::getOptions().getBool("railsignal-moving-block"));
    1341              : #ifdef DEBUG_BUILD_DRIVEWAY
    1342              :     if (DEBUG_COND_DW) {
    1343              :         std::cout << SIMTIME << " buildDriveWay " << dw->myID << " link=" << (link == nullptr ? "NULL" : link->getDescription())
    1344              :                   << "\n    route=" << toString(dw->myRoute)
    1345              :                   << "\n    forward=" << toString(dw->myForward)
    1346              :                   << "\n    bidi=" << toString(dw->myBidi)
    1347              :                   << "\n    bidiEx=" << toString(dw->myBidiExtended)
    1348              :                   << "\n    flank=" << toString(dw->myFlank)
    1349              :                   << "\n    flankSwitch=" << MSRailSignal::describeLinks(std::vector<MSLink*>(flankSwitches.begin(), flankSwitches.end()))
    1350              :                   << "\n    coreSize=" << dw->myCoreSize
    1351              :                   << "\n";
    1352              :     }
    1353              : #endif
    1354         7206 :     if (!rs || !rs->isMovingBlock()) {
    1355         7151 :         dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myForward.begin(), dw->myForward.end());
    1356              :     }
    1357         7206 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myBidi.begin(), dw->myBidi.end());
    1358         7206 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myFlank.begin(), dw->myFlank.end());
    1359         7206 :     dw->addBidiFoes(rs, false);
    1360         7206 :     dw->addBidiFoes(rs, true);
    1361              :     // add driveways that start on the same signal / lane
    1362         7206 :     dw->addParallelFoes(link, *first);
    1363              :     // add driveways that reverse along this driveways route
    1364         7206 :     dw->addReversalFoes(movingBlock);
    1365              :     // make foes unique and symmetrical
    1366         7206 :     std::set<MSDriveWay*, ComparatorNumericalIdLess> uniqueFoes(dw->myFoes.begin(), dw->myFoes.end());
    1367              :     dw->myFoes.clear();
    1368              :     // check for self-intersecting forward-section in movingBlock mode
    1369         7206 :     if (movingBlock && uniqueFoes.count(dw) == 0) {
    1370              :         std::set<const MSJunction*> forwardJunctions;
    1371          964 :         for (const MSLane* fw : dw->myForward) {
    1372          877 :             if (fw->isNormal()) {
    1373          501 :                 const MSJunction* fwTo = fw->getEdge().getToJunction();
    1374              :                 if (forwardJunctions.count(fwTo) == 1) {
    1375           15 :                     dw->myFoes.push_back(dw);
    1376              : #ifdef DEBUG_ADD_FOES
    1377              :                     if (DEBUG_COND_DW) {
    1378              :                         std::cout << " self-intersecting movingBlock for dw=" << dw->getID() << "\n";
    1379              :                     }
    1380              : #endif
    1381           15 :                     break;
    1382              :                 }
    1383              :                 forwardJunctions.insert(fwTo);
    1384              :             }
    1385              :         }
    1386              :     }
    1387         7206 :     std::set<MSLink*> uniqueCLink(dw->myConflictLinks.begin(), dw->myConflictLinks.end());
    1388         7206 :     const MSEdge* lastEdge = &dw->myForward.back()->getEdge();
    1389        11820 :     for (MSDriveWay* foe : uniqueFoes) {
    1390         4614 :         const MSEdge* foeLastEdge = &foe->myForward.back()->getEdge();
    1391         4614 :         const bool sameLast = foeLastEdge == lastEdge;
    1392         4614 :         if (sameLast && !movingBlock) {
    1393         1279 :             dw->myFoes.push_back(foe);
    1394         1279 :             if (foe != dw) {
    1395         1279 :                 foe->myFoes.push_back(dw);
    1396              :             }
    1397              :         } else {
    1398         3335 :             if (foe->bidiBlockedByEnd(*dw)) {
    1399              : #ifdef DEBUG_ADD_FOES
    1400              :                 if (DEBUG_COND_DW) {
    1401              :                     std::cout << " setting " << dw->getID() << " as foe of " << foe->getID() << "\n";
    1402              :                 }
    1403              : #endif
    1404         1733 :                 foe->myFoes.push_back(dw);
    1405         1733 :                 foe->addSidings(dw);
    1406              :             } else {
    1407         1602 :                 dw->buildSubFoe(foe, movingBlock);
    1408              :             }
    1409         3335 :             if (foe != dw) { // check for movingBlock
    1410         3312 :                 if (dw->bidiBlockedByEnd(*foe)) {
    1411              : #ifdef DEBUG_ADD_FOES
    1412              :                     if (DEBUG_COND_DW) {
    1413              :                         std::cout << " addFoeCheckSiding " << foe->getID() << "\n";
    1414              :                     }
    1415              : #endif
    1416         1858 :                     dw->myFoes.push_back(foe);
    1417         1858 :                     dw->addSidings(foe);
    1418              :                 } else  {
    1419         1454 :                     foe->buildSubFoe(dw, movingBlock);
    1420              :                 }
    1421              :             }
    1422              :         }
    1423         4614 :         if (link) {
    1424         3182 :             foe->addConflictLink(link);
    1425              :         }
    1426              :         // ignore links that have the same start junction
    1427         4614 :         if (foe->myRoute.front()->getFromJunction() != dw->myRoute.front()->getFromJunction()) {
    1428         6802 :             for (auto ili : foe->myForward.front()->getIncomingLanes()) {
    1429         3098 :                 if (ili.viaLink->getTLLogic() != nullptr) {
    1430              :                     // ignore links that originate on myBidi
    1431         2735 :                     const MSLane* origin = ili.viaLink->getLaneBefore();
    1432         2735 :                     if (std::find(dw->myBidi.begin(), dw->myBidi.end(), origin) == dw->myBidi.end()) {
    1433              :                         uniqueCLink.insert(ili.viaLink);
    1434              :                     }
    1435              :                 }
    1436              :             }
    1437              :         }
    1438              :     }
    1439              :     dw->myConflictLinks.clear();
    1440         7206 :     dw->myConflictLinks.insert(dw->myConflictLinks.begin(), uniqueCLink.begin(), uniqueCLink.end());
    1441         7206 :     myEndingDriveways[lastEdge].push_back(dw);
    1442         7206 :     if (!movingBlock) {
    1443              :         // every driveway is it's own foe (also all driveways that depart in the same block)
    1444        15581 :         for (MSDriveWay* sameEnd : myEndingDriveways[lastEdge]) {
    1445              :             if (uniqueFoes.count(sameEnd) == 0) {
    1446         7221 :                 dw->myFoes.push_back(sameEnd);
    1447         7221 :                 if (sameEnd != dw) {
    1448          140 :                     sameEnd->myFoes.push_back(dw);
    1449              :                 }
    1450              :             }
    1451              :         }
    1452              :     }
    1453              : #ifdef DEBUG_BUILD_DRIVEWAY
    1454              :     if (DEBUG_COND_DW) {
    1455              :         std::cout << dw->myID << " finalFoes " << toString(dw->myFoes) << "\n";
    1456              :     }
    1457              : #endif
    1458         7206 :     return dw;
    1459         7206 : }
    1460              : 
    1461              : std::string
    1462         3221 : MSDriveWay::getTLLinkID(const MSLink* link) {
    1463         6442 :     return link->getTLLogic()->getID() + "_" + toString(link->getTLIndex());
    1464              : }
    1465              : 
    1466              : std::string
    1467            0 : MSDriveWay::getJunctionLinkID(const MSLink* link) {
    1468            0 :     return link->getJunction()->getID() + "_" + toString(link->getIndex());
    1469              : }
    1470              : 
    1471              : std::string
    1472         4342 : MSDriveWay::getClickableTLLinkID(const MSLink* link) {
    1473        13026 :     return "junction '" +  link->getTLLogic()->getID() + "', link " + toString(link->getTLIndex());
    1474              : }
    1475              : 
    1476              : std::string
    1477            0 : MSDriveWay::formatVisitedMap(const LaneVisitedMap& visited) {
    1478              :     UNUSED_PARAMETER(visited);
    1479              :     /*
    1480              :     std::vector<const MSLane*> lanes(visited.size(), nullptr);
    1481              :     for (auto item : visited) {
    1482              :         lanes[item.second] = item.first;
    1483              :     }
    1484              :     for (auto it = lanes.begin(); it != lanes.end();) {
    1485              :         if (*it == nullptr) {
    1486              :             it = lanes.erase(it);
    1487              :         } else {
    1488              :             it++;
    1489              :         }
    1490              :     }
    1491              :     return toString(lanes);
    1492              :     */
    1493            0 :     return "dummy";
    1494              : }
    1495              : 
    1496              : 
    1497              : void
    1498        48033 : MSDriveWay::appendMapIndex(LaneVisitedMap& map, const MSLane* lane) {
    1499              :     // avoid undefined behavior from evaluation order
    1500        48033 :     const int tmp = (int)map.size();
    1501        48033 :     map[lane] = tmp;
    1502        48033 : }
    1503              : 
    1504              : bool
    1505      4040468 : MSDriveWay::match(MSRouteIterator firstIt, MSRouteIterator endIt) const {
    1506              :     // @todo optimize: it is sufficient to check for specific edges (after each switch)
    1507      4040468 :     auto itRoute = firstIt;
    1508              :     auto itDwRoute = myRoute.begin();
    1509              :     bool match = true;
    1510     23929915 :     while (itRoute != endIt && itDwRoute != myRoute.end()) {
    1511     19916417 :         if (*itRoute != *itDwRoute) {
    1512              :             match = false;
    1513              : #ifdef DEBUG_MATCH
    1514              :             std::cout << "  check dw=" << getID() << " match failed at vehEdge=" << (*itRoute)->getID() << " dwEdge=" << (*itDwRoute)->getID() << "\n";
    1515              : #endif
    1516              :             break;
    1517              :         }
    1518              :         itRoute++;
    1519              :         itDwRoute++;
    1520              :     }
    1521              :     // if the vehicle arrives before the end of this driveway,
    1522              :     // we'd rather build a new driveway to avoid superfluous restrictions
    1523      4013498 :     if (match && itDwRoute == myRoute.end()
    1524      8041615 :             && (itRoute == endIt || myAbortedBuild || myBidiEnded || myFoundJump || myIsSubDriveway)) {
    1525              :         //std::cout << "  using dw=" << "\n";
    1526      3996857 :         if (itRoute != endIt) {
    1527              :             // check whether the current route requires an extended driveway
    1528       449767 :             const MSEdge* next = *itRoute;
    1529       449767 :             const MSEdge* prev = myRoute.back();
    1530          510 :             if (myFoundJump && prev->getBidiEdge() != next && prev->getBidiEdge() != nullptr
    1531       450030 :                     && prev->isConnectedTo(*next, (SUMOVehicleClass)(SVC_RAIL_CLASSES & prev->getPermissions()))) {
    1532              : #ifdef DEBUG_MATCH
    1533              :                 std::cout << "  check dw=" << getID() << " prev=" << prev->getID() << " next=" << next->getID() << "\n";
    1534              : #endif
    1535              :                 return false;
    1536              :             }
    1537       449756 :             if (!myFoundJump && prev->getBidiEdge() == next && prev == &myForward.back()->getEdge()) {
    1538              :                 assert(myIsSubDriveway || myBidiEnded);
    1539              :                 // must not leave driveway via reversal
    1540              : #ifdef DEBUG_MATCH
    1541              :                 std::cout << getID() << " back=" << myForward.back()->getID() << " noMatch route " << toString(ConstMSEdgeVector(firstIt, endIt)) << "\n";
    1542              : #endif
    1543              :                 return false;
    1544              :             }
    1545              :         }
    1546      3996831 :         return true;
    1547              :     }
    1548              :     return false;
    1549              : }
    1550              : 
    1551              : void
    1552        10186 : MSDriveWay::addFoes(const MSLink* link) {
    1553              : #ifdef DEBUG_ADD_FOES
    1554              :     std::cout << "driveway " << myID << " addFoes for link " << link->getDescription() << "\n";
    1555              : #endif
    1556        10186 :     const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(link->getTLLogic());
    1557        10186 :     if (rs != nullptr) {
    1558        12684 :         for (MSDriveWay* foe : rs->retrieveDriveWays(link->getTLIndex())) {
    1559              : #ifdef DEBUG_ADD_FOES
    1560              :             std::cout << "  cand foe=" << foe->myID << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this) << " cc1=" << crossingConflict(*foe) << " cc2=" <<  foe->crossingConflict(*this) << "\n";
    1561              : #endif
    1562         2498 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1563              : #ifdef DEBUG_ADD_FOES
    1564              :                 std::cout << "   foe=" << foe->myID << "\n";
    1565              : #endif
    1566         2132 :                 myFoes.push_back(foe);
    1567              :             }
    1568        10186 :         }
    1569              :     }
    1570        10186 : }
    1571              : 
    1572              : 
    1573              : void
    1574        14412 : MSDriveWay::addBidiFoes(const MSRailSignal* ownSignal, bool extended) {
    1575              : #ifdef DEBUG_ADD_FOES
    1576              :     std::cout << "driveway " << myID << " addBidiFoes extended=" << extended << "\n";
    1577              : #endif
    1578        14412 :     const std::vector<const MSLane*>& bidiLanes = extended ? myBidiExtended : myBidi;
    1579        46635 :     for (const MSLane* bidi : bidiLanes) {
    1580        67017 :         for (auto ili : bidi->getIncomingLanes()) {
    1581        34794 :             const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(ili.viaLink->getTLLogic());
    1582        34794 :             if (rs != nullptr && rs != ownSignal &&
    1583        34794 :                     std::find(bidiLanes.begin(), bidiLanes.end(), ili.lane) != bidiLanes.end()) {
    1584         3459 :                 addFoes(ili.viaLink);
    1585              :             }
    1586              :         }
    1587        32223 :         const MSEdge* bidiEdge = &bidi->getEdge();
    1588              :         if (myDepartureDriveways.count(bidiEdge) != 0) {
    1589         2285 :             for (MSDriveWay* foe : myDepartureDriveways[bidiEdge]) {
    1590         1015 :                 if (flankConflict(*foe)) {
    1591              : #ifdef DEBUG_ADD_FOES
    1592              :                     std::cout << "  foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << "\n";
    1593              : #endif
    1594          773 :                     myFoes.push_back(foe);
    1595              :                 } else {
    1596              : #ifdef DEBUG_ADD_FOES
    1597              :                     std::cout << "  cand foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << " rejected\n";
    1598              : #endif
    1599              :                 }
    1600              :             }
    1601              :         }
    1602              :         if (myDepartureDrivewaysEnds.count(bidiEdge) != 0) {
    1603         1865 :             for (MSDriveWay* foe : myDepartureDrivewaysEnds[bidiEdge]) {
    1604          979 :                 if (flankConflict(*foe)) {
    1605              : #ifdef DEBUG_ADD_FOES
    1606              :                     std::cout << "  foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << "\n";
    1607              : #endif
    1608          771 :                     myFoes.push_back(foe);
    1609              :                 } else {
    1610              : #ifdef DEBUG_ADD_FOES
    1611              :                     std::cout << "  cand foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << " rejected\n";
    1612              : #endif
    1613              :                 }
    1614              :             }
    1615              :         }
    1616              :     }
    1617        14412 : }
    1618              : 
    1619              : 
    1620              : void
    1621         7206 : MSDriveWay::addParallelFoes(const MSLink* link, const MSEdge* first) {
    1622              : #ifdef DEBUG_ADD_FOES
    1623              :     std::cout << "driveway " << myID << " addParallelFoes\n";
    1624              : #endif
    1625         7206 :     if (link) {
    1626         4330 :         addFoes(link);
    1627              :     } else {
    1628              :         auto it = myDepartureDriveways.find(first);
    1629         2876 :         if (it != myDepartureDriveways.end()) {
    1630         3015 :             for (MSDriveWay* foe : it->second) {
    1631              : #ifdef DEBUG_ADD_FOES
    1632              :                 std::cout << "  foe " << foe->getID() << " departs on first=" << first->getID() << "\n";
    1633              : #endif
    1634          139 :                 myFoes.push_back(foe);
    1635              :             }
    1636              :         }
    1637              :     }
    1638         7206 : }
    1639              : 
    1640              : 
    1641              : void
    1642         7206 : MSDriveWay::addReversalFoes(bool movingBlock) {
    1643              : #ifdef DEBUG_ADD_FOES
    1644              :     std::cout << "driveway " << myID << " addReversalFoes\n";
    1645              : #endif
    1646              :     std::set<const MSEdge*> forward;
    1647        31556 :     for (const MSLane* lane : myForward) {
    1648        24350 :         if (lane->isNormal()) {
    1649        16307 :             forward.insert(&lane->getEdge());
    1650              :         }
    1651              :     }
    1652              :     int i = 0;
    1653        35474 :     for (const MSEdge* e : myRoute) {
    1654        16317 :         if (forward.count(e) != 0 && !movingBlock) {
    1655              :             // reversals in our own forward can be ignored because each driveway
    1656              :             // is automatically a foe of itself by default
    1657              :             continue;
    1658              :         }
    1659        12570 :         if (i == myCoreSize) {
    1660              :             break;
    1661              :         }
    1662        12570 :         i++;
    1663              :         auto it = myReversalDriveWays.find(e);
    1664        12570 :         if (it != myReversalDriveWays.end()) {
    1665         1359 :             for (MSDriveWay* foe : it->second) {
    1666              :                 // check whether the foe reverses into our own forward section
    1667              :                 // (it might reverse again or disappear via arrival)
    1668              : #ifdef DEBUG_ADD_FOES
    1669              :                 //std::cout << "  candidate foe " << foe->getID() << " reverses on edge=" << e->getID() << " forward=" << joinNamedToString(forward, " ") << " foeRoute=" << toString(foe->myRoute) << "\n";
    1670              : #endif
    1671         1816 :                 if (forwardRouteConflict(forward, *foe)) {
    1672              :                     std::set<const MSEdge*> foeForward;
    1673         1009 :                     for (const MSLane* lane : foe->myForward) {
    1674          853 :                         if (lane->isNormal()) {
    1675          541 :                             foeForward.insert(&lane->getEdge());
    1676          541 :                             if (lane->getBidiLane() != nullptr) {
    1677          541 :                                 foeForward.insert(lane->getEdge().getBidiEdge());
    1678              :                             }
    1679              :                         }
    1680              :                     }
    1681              : #ifdef DEBUG_ADD_FOES
    1682              :                     std::cout << "  reversal cand=" << foe->getID() << " foeForward " << toString(foeForward) << "\n";
    1683              : #endif
    1684          312 :                     if (foe->forwardRouteConflict(foeForward, *this, true)) {
    1685              : #ifdef DEBUG_ADD_FOES
    1686              :                         std::cout << "  foe " << foe->getID() << " reverses on edge=" << e->getID() << "\n";
    1687              : #endif
    1688          121 :                         myFoes.push_back(foe);
    1689              :                     }
    1690          752 :                 } else if (movingBlock && foe == this) {
    1691              : #ifdef DEBUG_ADD_FOES
    1692              :                     std::cout << "  dw " << getID() << " reverses on forward edge=" << e->getID() << " (movingBlock)\n";
    1693              : #endif
    1694           23 :                     myFoes.push_back(foe);
    1695              :                 }
    1696              :             }
    1697              :         }
    1698              :     }
    1699         7206 : }
    1700              : 
    1701              : 
    1702              : bool
    1703         3056 : MSDriveWay::buildSubFoe(MSDriveWay* foe, bool movingBlock) {
    1704              :     // Subdriveways (Teilfahrstraße) model the resolution of a driving conflict
    1705              :     // before a vehicle has left the driveway. This is possible when the driveway diverges from the foe
    1706              :     // driveway at an earlier point (switch or crossing).
    1707              :     //
    1708              :     // We already know that the last edge of this driveway doesn't impact the foe (unless the driveway ends within the block).
    1709              :     // Remove further edges from the end of the driveway (myForward) until the point of conflict is found.
    1710              :     //
    1711              :     // For movingBlock the logic is changed:
    1712              :     // We remove the conflict-free part as before but then keep removing the the conflict part until the
    1713              :     // another non-conconflit part is found
    1714         3056 :     if (myForward.size() < foe->myForward.size() &&
    1715         3056 :             myForward == std::vector<const MSLane*>(foe->myForward.begin(), foe->myForward.begin() + myForward.size())) {
    1716              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1717              :         std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " is subpart of foe=" << foe->getID() << "\n";
    1718              : #endif
    1719           25 :         foe->myFoes.push_back(this);
    1720           25 :         return true;
    1721              :     }
    1722         3031 :     int subLast = (int)myForward.size() - 2;
    1723              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1724              :     if (subLast < 0) {
    1725              :         std::cout << "  " << getID() << " cannot build subDriveWay for foe " << foe->getID() << " because myForward has only a single lane\n";
    1726              :     }
    1727              : #endif
    1728              :     bool foundConflict = false;
    1729         9471 :     while (subLast >= 0) {
    1730         8949 :         const MSLane* lane = myForward[subLast];
    1731         8949 :         MSDriveWay tmp(myOrigin, "tmp", true);
    1732         8949 :         tmp.myForward.push_back(lane);
    1733              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1734              :         std::cout << "  subLast=" << subLast << " lane=" << lane->getID() << " fc=" << tmp.flankConflict(*foe) << " cc=" << tmp.crossingConflict(*foe)
    1735              :                   << " bc=" << (std::find(foe->myBidi.begin(), foe->myBidi.end(), lane) != foe->myBidi.end()) << "\n";
    1736              : #endif
    1737         8949 :         const bool bidiConflict = std::find(foe->myBidi.begin(), foe->myBidi.end(), lane) != foe->myBidi.end();
    1738         8949 :         if (tmp.flankConflict(*foe) || tmp.crossingConflict(*foe) || bidiConflict) {
    1739              :             foundConflict = true;
    1740         2526 :             if (!movingBlock || bidiConflict) {
    1741              :                 break;
    1742              :             }
    1743         6423 :         } else if (foundConflict) {
    1744              :             break;
    1745              :         }
    1746         6440 :         subLast--;
    1747         8949 :     }
    1748         3031 :     if (subLast < 0) {
    1749          522 :         if (foe->myTerminateRoute) {
    1750          482 :             if (bidiBlockedByEnd(*foe) && bidiBlockedBy(*this) && foe->forwardEndOnRoute(this)) {
    1751           62 :                 foe->myFoes.push_back(this);
    1752              :                 // foe will get the sidings
    1753           62 :                 addSidings(foe, true);
    1754              :             }
    1755              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1756              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " terminates\n";
    1757              : #endif
    1758           40 :         } else if (myTerminateRoute && myBidi.size() <= myForward.size()) {
    1759            5 :             foe->myFoes.push_back(this);
    1760              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1761              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " terminates, foe=" << foe->getID() << "\n";
    1762              : #endif
    1763            5 :             return true;
    1764              :         } else if (foe->myReversals.size() % 2 == 1) {
    1765              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1766              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " has " << foe->myReversals.size() << " reversals\n";
    1767              : #endif
    1768              :         } else {
    1769              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1770              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " failed\n";
    1771              : #endif
    1772              : #ifdef SUBDRIVEWAY_WARN_NOCONFLICT
    1773              :             WRITE_WARNINGF("No point of conflict found between driveway '%' and driveway '%' when creating sub-driveway", getID(), foe->getID());
    1774              : #endif
    1775              :         }
    1776          517 :         return false;
    1777              :     }
    1778         2509 :     int subSize = subLast + 1;
    1779         3149 :     for (MSDriveWay* cand : mySubDriveWays) {
    1780         1153 :         if ((int)cand->myForward.size() == subSize) {
    1781              :             // can re-use existing sub-driveway
    1782          513 :             foe->myFoes.push_back(cand);
    1783          513 :             cand->myFoes.push_back(foe);
    1784              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1785              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " useExisting=" << cand->getID() << "\n";
    1786              : #endif
    1787          513 :             return true;
    1788              :         }
    1789              :     }
    1790         1996 :     std::vector<const MSLane*> forward(myForward.begin(), myForward.begin() + subSize);
    1791              :     std::vector<const MSEdge*> route;
    1792         9126 :     for (const MSLane* lane : forward) {
    1793         7130 :         if (lane->isNormal()) {
    1794         4255 :             route.push_back(&lane->getEdge());
    1795              :         }
    1796              :     }
    1797         1996 :     if (route.empty()) {
    1798              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1799              :         std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID() << " empty subRoute\n";
    1800              : #endif
    1801              :         return false;
    1802              :     }
    1803         1996 :     if (myRoute.size() > route.size()) {
    1804              :         // route continues. make sure the subDriveway does not end with a reversal
    1805         1996 :         const MSEdge* lastNormal = route.back();
    1806         1996 :         const MSEdge* nextNormal = myRoute[route.size()];
    1807         1996 :         if (lastNormal->getBidiEdge() == nextNormal) {
    1808              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1809              :             std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID()
    1810              :                       << " lastNormal=" << lastNormal->getID() << " nextNormal=" << nextNormal->getID() << " endWithReversal\n";
    1811              : #endif
    1812              :             return false;
    1813              :         }
    1814              :     }
    1815         3952 :     MSDriveWay* sub = new MSDriveWay(myOrigin, getID() + "." + toString(mySubDriveWays.size()));
    1816         1976 :     sub->myLane = myLane;
    1817         1976 :     sub->myIsSubDriveway = true;
    1818         1976 :     sub->myForward = forward;
    1819         1976 :     sub->myRoute = route;
    1820         1976 :     sub->myCoreSize = (int)sub->myRoute.size();
    1821         1976 :     myLane->addMoveReminder(sub, false);
    1822              : 
    1823              :     // copy trains that are currently on this driveway (and associated entry events)
    1824         2098 :     for (SUMOVehicle* veh : myTrains) {
    1825          122 :         auto itOnSub = std::find(sub->myRoute.begin(), sub->myRoute.end(), veh->getEdge());
    1826          122 :         if (itOnSub != sub->myRoute.end()) {
    1827              :             sub->myTrains.insert(veh);
    1828              :             // non-zero is enough to avoid superfluous activation via activateReminders (and removal)
    1829          109 :             const double pos = itOnSub == sub->myRoute.begin() ? 0 : sub->myRoute.front()->getLength();
    1830          109 :             dynamic_cast<MSBaseVehicle*>(veh)->addReminder(sub, pos);
    1831          116 :             for (const VehicleEvent& ve : myVehicleEvents) {
    1832            7 :                 if (ve.id == veh->getID()) {
    1833            7 :                     sub->myVehicleEvents.push_back(ve);
    1834              :                 }
    1835              :             }
    1836              :         }
    1837              :     }
    1838              : 
    1839         1976 :     foe->myFoes.push_back(sub);
    1840         1976 :     sub->myFoes.push_back(foe);
    1841         1976 :     mySubDriveWays.push_back(sub);
    1842              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1843              :     std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " sub=" << sub->getID() << " route=" << toString(sub->myRoute) << "\n";
    1844              : #endif
    1845              :     return true;
    1846         1996 : }
    1847              : 
    1848              : void
    1849         3653 : MSDriveWay::addSidings(MSDriveWay* foe, bool addToFoe) {
    1850         3653 :     const MSEdge* foeEndBidi = foe->myForward.back()->getEdge().getBidiEdge();
    1851              :     int forwardNormals = 0;
    1852        16743 :     for (auto lane : foe->myForward) {
    1853        13090 :         if (lane->isNormal()) {
    1854         8761 :             forwardNormals++;
    1855              :         }
    1856              :     }
    1857         3653 :     if (forwardNormals == (int)foe->myRoute.size()) {
    1858              : #ifdef DEBUG_BUILD_SIDINGS
    1859              :         std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " forwardNormals=" << forwardNormals << " frSize=" << foe->myRoute.size() <<  " aborted\n";
    1860              : #endif
    1861          792 :         return;
    1862              :     }
    1863              :     auto foeSearchBeg = foe->myRoute.begin() + forwardNormals;
    1864              :     auto foeSearchEnd = foe->myRoute.end();
    1865         2861 :     if (foeEndBidi == nullptr) {
    1866            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " noBidi\n");
    1867              :     }
    1868              :     int i;
    1869              :     std::vector<int> start;
    1870              :     std::vector<double> length;
    1871        16767 :     for (i = 0; i < (int)myRoute.size(); i++) {
    1872        16767 :         if (myRoute[i] == foeEndBidi) {
    1873              :             break;
    1874              :         }
    1875              :     }
    1876         2861 :     if (i == (int)myRoute.size()) {
    1877            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " foeEndBidi=" + foeEndBidi->getID() + " not on route\n");
    1878              :     }
    1879         2861 :     const MSEdge* next = myRoute[i];
    1880              : #ifdef DEBUG_BUILD_SIDINGS
    1881              :     std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " next=" << next->getID() << " forwardNormals=" << forwardNormals << " frSize=" << foe->myRoute.size() << " foeSearchBeg=" << (*foeSearchBeg)->getID() << "\n";
    1882              : #endif
    1883         2861 :     i--;
    1884        16767 :     for (; i >= 0; i--) {
    1885        13906 :         const MSEdge* cur = myRoute[i];
    1886        13906 :         if (hasRS(cur, next)) {
    1887         2752 :             if (std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge()) == foeSearchEnd) {
    1888          655 :                 start.push_back(i);
    1889          655 :                 length.push_back(0);
    1890              :             }
    1891              :         }
    1892        13906 :         if (!start.empty()) {
    1893         1843 :             auto itFind = std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge());
    1894         1843 :             if (itFind != foeSearchEnd) {
    1895              : #ifdef DEBUG_BUILD_SIDINGS
    1896              :                 std::cout << "endSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " curBidi=" << Named::getIDSecure(cur->getBidiEdge()) << " length=" << toString(length) << "\n";
    1897              : #endif
    1898          413 :                 const int firstIndex = i + 1;
    1899          413 :                 if (addToFoe) {
    1900           34 :                     auto& foeSidings = foe->mySidings[this];
    1901              :                     // indices must be mapped onto foe route;
    1902           34 :                     const MSEdge* first = myRoute[firstIndex];
    1903           34 :                     auto itFirst = std::find(foe->myRoute.begin(), foe->myRoute.end(), first);
    1904           34 :                     if (itFirst != foe->myRoute.end()) {
    1905           87 :                         for (int j = 0; j < (int)length.size(); j++) {
    1906           53 :                             const MSEdge* last = myRoute[start[j]];
    1907           53 :                             auto itLast = std::find(itFirst, foe->myRoute.end(), last);
    1908           53 :                             if (itLast != foe->myRoute.end()) {
    1909           53 :                                 foeSidings.insert(foeSidings.begin(), Siding((int)(itFirst - foe->myRoute.begin()), (int)(itLast - foe->myRoute.begin()), length[j]));
    1910              :                             }
    1911              :                         }
    1912              :                     }
    1913              :                 } else {
    1914          379 :                     auto& foeSidings = mySidings[foe];
    1915          870 :                     for (int j = 0; j < (int)length.size(); j++) {
    1916          491 :                         foeSidings.insert(foeSidings.begin(), Siding(firstIndex, start[j], length[j]));
    1917              :                     }
    1918              :                 }
    1919              :                 start.clear();
    1920              :                 length.clear();
    1921          413 :                 foeSearchBeg = itFind;
    1922              :             } else {
    1923         3204 :                 for (int j = 0; j < (int)length.size(); j++) {
    1924         1774 :                     length[j] += cur->getLength();
    1925              :                 }
    1926              :             }
    1927              :         }
    1928              :         next = cur;
    1929              :     }
    1930         2861 : }
    1931              : 
    1932              : 
    1933              : bool
    1934        13906 : MSDriveWay::hasRS(const MSEdge* cur, const MSEdge* next) {
    1935        13906 :     if (cur->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1936              :         // check if there is a controlled link between cur and next
    1937        10832 :         for (auto lane : cur->getLanes()) {
    1938        11218 :             for (const MSLink* link : lane->getLinkCont()) {
    1939         7178 :                 if (&link->getLane()->getEdge() == next && link->getTLLogic() != nullptr) {
    1940              :                     return true;
    1941              :                 }
    1942              :             }
    1943              :         }
    1944              :     }
    1945              :     return false;
    1946              : }
    1947              : 
    1948              : 
    1949              : bool
    1950           72 : MSDriveWay::forwardEndOnRoute(const MSDriveWay* foe) const {
    1951           72 :     const MSEdge* foeForwardEnd = &foe->myForward.back()->getNormalPredecessorLane()->getEdge();
    1952           72 :     return std::find(myRoute.begin(), myRoute.end(), foeForwardEnd) != myRoute.end();
    1953              : }
    1954              : 
    1955              : void
    1956         3182 : MSDriveWay::addConflictLink(const MSLink* link) {
    1957         3182 :     if (link->getTLLogic() != nullptr) {
    1958              :         // ignore links that originate on myBidi
    1959              :         // and also links from the same junction as my own link
    1960         3182 :         const MSLane* origin = link->getLaneBefore();
    1961         3182 :         if (std::find(myBidi.begin(), myBidi.end(), origin) == myBidi.end()) {
    1962         2296 :             if (link->getJunction() != myRoute.front()->getFromJunction()) {
    1963         1819 :                 if (std::find(myConflictLinks.begin(), myConflictLinks.end(), link) == myConflictLinks.end()) {
    1964         1350 :                     myConflictLinks.push_back(const_cast<MSLink*>(link));
    1965              :                 }
    1966              :             }
    1967              :         }
    1968              :     }
    1969         3182 : }
    1970              : 
    1971              : void
    1972          278 : MSDriveWay::addDWDeadlock(const std::vector<const MSDriveWay*>& deadlockFoes) {
    1973              :     std::set<const MSDriveWay*> filtered;
    1974         1457 :     for (const MSDriveWay* foe : deadlockFoes) {
    1975         1179 :         if (std::find(myFoes.begin(), myFoes.end(), foe) == myFoes.end()) {
    1976              :             filtered.insert(foe);
    1977              :         }
    1978              :     }
    1979          278 :     if (std::find(myDeadlocks.begin(), myDeadlocks.end(), filtered) == myDeadlocks.end()) {
    1980           81 :         myDeadlocks.push_back(filtered);
    1981              :         //std::cout << getID() << " deadlockFoes=" << toString(deadlockFoes) << "\n";
    1982              :     }
    1983          278 : }
    1984              : 
    1985              : const MSDriveWay*
    1986        58420 : MSDriveWay::getDepartureDriveway(const SUMOVehicle* veh, bool init) {
    1987        58420 :     const MSEdge* edge = init ? veh->getRoute().getEdges()[veh->getDepartEdge()] : veh->getEdge();
    1988              :     assert(isRailwayOrShared(edge->getPermissions()));
    1989        58420 :     if (edge->getFromJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1990         4290 :         for (const MSLane* lane : edge->getLanes()) {
    1991         4652 :             for (auto ili : lane->getIncomingLanes()) {
    1992         2830 :                 const MSLink* entry = ili.viaLink->getCorrespondingEntryLink();
    1993         2830 :                 const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(entry->getTLLogic());
    1994         2084 :                 if (rs != nullptr) {
    1995         2084 :                     const MSDriveWay* dw = &const_cast<MSRailSignal*>(rs)->retrieveDriveWayForVeh(entry->getTLIndex(), veh);
    1996         2084 :                     if (&dw->myForward.front()->getEdge() == edge) {
    1997              :                         return dw;
    1998              :                     }
    1999              :                 }
    2000              :             }
    2001              :         }
    2002              :     }
    2003        60979 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    2004        58103 :         auto matchStart = init ? veh->getRoute().begin() + veh->getDepartEdge() : veh->getCurrentRouteEdge();
    2005        58103 :         if (dw->match(matchStart, veh->getRoute().end())) {
    2006        54898 :             return dw;
    2007              :         }
    2008              :     }
    2009         5752 :     const std::string id = edge->getFromJunction()->getID() + ".d" + toString(myDepartDrivewayIndex[edge->getFromJunction()]++);
    2010         2876 :     MSDriveWay* dw = buildDriveWay(id, nullptr, veh->getCurrentRouteEdge(), veh->getRoute().end());
    2011         2876 :     myDepartureDriveways[edge].push_back(dw);
    2012         2876 :     myDepartureDrivewaysEnds[&dw->myForward.back()->getEdge()].push_back(dw);
    2013              :     dw->setVehicle(veh->getID());
    2014              :     return dw;
    2015              : }
    2016              : 
    2017              : 
    2018              : void
    2019         1317 : MSDriveWay::writeDepatureBlocks(OutputDevice& od, bool writeVehicles) {
    2020         3320 :     for (auto item  : myDepartureDriveways) {
    2021         2003 :         const MSEdge* edge = item.first;
    2022         2003 :         if (item.second.size() > 0) {
    2023         4006 :             od.openTag("departJunction");
    2024         2003 :             od.writeAttr(SUMO_ATTR_ID, edge->getFromJunction()->getID());
    2025         4137 :             for (const MSDriveWay* dw : item.second) {
    2026         2134 :                 if (writeVehicles) {
    2027          209 :                     dw->writeBlockVehicles(od);
    2028              :                 } else {
    2029         1925 :                     dw->writeBlocks(od);
    2030              :                 }
    2031              :             }
    2032         4006 :             od.closeTag(); // departJunction
    2033              :         }
    2034              :     }
    2035         1317 : }
    2036              : 
    2037              : void
    2038          476 : MSDriveWay::saveState(OutputDevice& out) {
    2039              :     // all driveways are in myEndingDriveways which makes it convenient
    2040          551 :     for (auto item : myEndingDriveways) {
    2041          158 :         for (MSDriveWay* dw : item.second) {
    2042           83 :             dw->_saveState(out);
    2043          101 :             for (MSDriveWay* sub : dw->mySubDriveWays) {
    2044           18 :                 sub->_saveState(out);
    2045              :             }
    2046              :         }
    2047              :     }
    2048          476 : }
    2049              : 
    2050              : void
    2051          101 : MSDriveWay::_saveState(OutputDevice& out) const {
    2052          101 :     if (!myTrains.empty() || haveSubTrains()) {
    2053           73 :         out.openTag(myIsSubDriveway ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
    2054           38 :         out.writeAttr(SUMO_ATTR_ID, getID());
    2055           76 :         out.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
    2056           38 :         if (!myTrains.empty()) {
    2057           76 :             out.writeAttr(SUMO_ATTR_VEHICLES, toString(myTrains));
    2058              :         }
    2059           76 :         out.closeTag();
    2060              :     }
    2061          101 : }
    2062              : 
    2063              : 
    2064              : bool
    2065           63 : MSDriveWay::haveSubTrains() const {
    2066           70 :     for (MSDriveWay* sub : mySubDriveWays) {
    2067            7 :         if (!sub->myTrains.empty()) {
    2068              :             return true;
    2069              :         }
    2070              :     }
    2071              :     return false;
    2072              : }
    2073              : 
    2074              : void
    2075           41 : MSDriveWay::loadState(const SUMOSAXAttributes& attrs, int tag) {
    2076           41 :     if ((int)myDriveWayRouteLookup.size() < myGlobalDriveWayIndex) {
    2077          106 :         for (auto item : myEndingDriveways) {
    2078          168 :             for (MSDriveWay* dw : item.second) {
    2079           86 :                 myDriveWayRouteLookup[dw->myRoute] = dw;
    2080              :             }
    2081              :         }
    2082              :     }
    2083           41 :     MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
    2084              :     bool ok;
    2085           41 :     const std::string id  = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    2086           41 :     const std::string edges = attrs.get<std::string>(SUMO_ATTR_EDGES, id.c_str(), ok);
    2087              :     ConstMSEdgeVector route;
    2088           41 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
    2089           41 :         MSEdge::parseEdgesList(edges, route, id);
    2090              :     }
    2091              :     MSDriveWay* dw = nullptr;
    2092           41 :     if (tag == SUMO_TAG_DRIVEWAY) {
    2093              :         auto it = myDriveWayRouteLookup.find(route);
    2094           38 :         if (it == myDriveWayRouteLookup.end()) {
    2095              :             //WRITE_WARNING(TLF("Unknown driveWay '%' with route '%'", id, edges));
    2096              :             //return;
    2097            0 :             throw ProcessError(TLF("Unknown driveWay '%' with route '%'", id, edges));
    2098              :         }
    2099           38 :         dw = it->second;
    2100           38 :         myDriveWayLookup[id] = dw;
    2101              :     } else {
    2102            3 :         std::string parentID = id.substr(0, id.rfind('.'));
    2103              :         auto it = myDriveWayLookup.find(parentID);
    2104            3 :         if (it == myDriveWayLookup.end()) {
    2105              :             //WRITE_WARNING(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
    2106              :             //return;
    2107            0 :             throw ProcessError(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
    2108              :         }
    2109            3 :         MSDriveWay* parent = it->second;
    2110            3 :         for (MSDriveWay* sub : parent->mySubDriveWays) {
    2111            1 :             if (sub->myRoute == route) {
    2112              :                 dw = sub;
    2113              :                 break;
    2114              :             }
    2115              :         }
    2116            3 :         if (dw == nullptr) {
    2117              :             // missing subdriveways can be ignored. They may have been created
    2118              :             // as foes for driveways that are not relevant at state loading time
    2119              :             return;
    2120              :         }
    2121              :     }
    2122           78 :     const std::string vehicles = attrs.getOpt<std::string>(SUMO_ATTR_VEHICLES, id.c_str(), ok, "");
    2123          117 :     for (const std::string& vehID : StringTokenizer(vehicles).getVector()) {
    2124           39 :         MSBaseVehicle* veh = dynamic_cast<MSBaseVehicle*>(c.getVehicle(vehID));
    2125           39 :         if (veh == nullptr) {
    2126            0 :             throw ProcessError(TLF("Unknown vehicle '%' in driveway '%'", vehID, id));
    2127              :         }
    2128           39 :         if (!dw->hasTrain(veh)) {
    2129            5 :             dw->myTrains.insert(veh);
    2130            5 :             veh->addReminder(dw);
    2131              :         }
    2132           39 :     }
    2133           41 : }
    2134              : 
    2135              : const MSDriveWay*
    2136            0 : MSDriveWay::retrieveDepartDriveWay(const MSEdge* edge, const std::string& id) {
    2137            0 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    2138            0 :         if (dw->getID() == id) {
    2139              :             return dw;
    2140              :         }
    2141              :     }
    2142              :     return nullptr;
    2143              : }
    2144              : 
    2145              : 
    2146              : bool
    2147        18274 : MSDriveWay::hasTrain(SUMOVehicle* veh) const {
    2148        18274 :     return myTrains.count(veh) != 0;
    2149              : }
    2150              : 
    2151              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1