LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSDriveWay.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 96.8 % 955 924
Test Date: 2026-07-05 15:55:58 Functions: 95.1 % 61 58

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2026 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_MOVEREMINDER
      50              : //#define DEBUG_MATCH
      51              : 
      52              : #define DEBUG_HELPER(obj) ((obj) != nullptr && (obj)->isSelected())
      53              : //#define DEBUG_HELPER(obj) ((obj)->getID() == "")
      54              : //#define DEBUG_HELPER(obj) (true)
      55              : 
      56              : #define DEBUG_DW_ID ""
      57              : #define DEBUG_COND_DW(obj) (obj->getID() == DEBUG_DW_ID || DEBUG_DW_ID == std::string("ALL"))
      58              : #define DEBUG_COND_DW2 (getID() == DEBUG_DW_ID || DEBUG_DW_ID == std::string("ALL"))
      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              : double MSDriveWay::myMovingBlockMaxDist(1e10);
      67              : std::map<const MSLink*, std::vector<MSDriveWay*> > MSDriveWay::mySwitchDriveWays;
      68              : std::map<const MSEdge*, std::vector<MSDriveWay*> > MSDriveWay::myReversalDriveWays;
      69              : std::map<const MSEdge*, std::vector<MSDriveWay*>, ComparatorNumericalIdLess> MSDriveWay::myDepartureDriveways;
      70              : std::map<const MSJunction*, int> MSDriveWay::myDepartDrivewayIndex;
      71              : std::map<const MSEdge*, std::vector<MSDriveWay*> > MSDriveWay::myDepartureDrivewaysEnds;
      72              : std::map<const MSEdge*, std::vector<MSDriveWay*>, ComparatorNumericalIdLess> MSDriveWay::myEndingDriveways;
      73              : std::map<ConstMSEdgeVector, MSDriveWay*> MSDriveWay::myDriveWayRouteLookup;
      74              : std::map<std::string, MSDriveWay*> MSDriveWay::myDriveWayLookup;
      75              : 
      76              : // ---------------------------------------------------------------------------
      77              : // static initialisation methods
      78              : // ---------------------------------------------------------------------------
      79              : void
      80        42726 : MSDriveWay::init() {
      81        42726 :     myWriteVehicles = OptionsCont::getOptions().isSet("railsignal-vehicle-output");
      82        42726 :     myMovingBlockMaxDist = OptionsCont::getOptions().getFloat("railsignal.moving-block.max-dist");
      83        42726 : }
      84              : 
      85              : // ===========================================================================
      86              : // MSDriveWay method definitions
      87              : // ===========================================================================
      88              : 
      89              : 
      90        26816 : MSDriveWay::MSDriveWay(const MSLink* origin, const std::string& id, bool temporary) :
      91        53632 :     MSMoveReminder("DriveWay_" + (temporary ? "tmp" : id)),
      92              :     Named(id),
      93        11856 :     myNumericalID(temporary ? -1 : myGlobalDriveWayIndex++),
      94        26816 :     myOrigin(origin),
      95        26816 :     myActive(nullptr),
      96        26816 :     myCoreSize(0),
      97        26816 :     myForwardEdgeCount(0),
      98        26816 :     myFoundSignal(false),
      99        26816 :     myFoundJump(false),
     100        26816 :     myTerminateRoute(false),
     101        26816 :     myAbortedBuild(false),
     102        26816 :     myBidiEnded(false),
     103        92304 :     myParent(nullptr)
     104        26816 : {}
     105              : 
     106              : 
     107        38672 : MSDriveWay::~MSDriveWay() {
     108        29389 :     for (const MSDriveWay* sub : mySubDriveWays) {
     109         2573 :         delete sub;
     110              :     }
     111              :     mySubDriveWays.clear();
     112       119120 : }
     113              : 
     114              : void
     115        42395 : MSDriveWay::cleanup() {
     116        42395 :     myGlobalDriveWayIndex = 0;
     117              :     myBlockLengthWarnings.clear();
     118        42395 :     myWriteVehicles = false;
     119              : 
     120        45881 :     for (auto item : myDepartureDriveways) {
     121         7283 :         for (MSDriveWay* dw : item.second) {
     122         3797 :             delete dw;
     123              :         }
     124              :     }
     125              :     MSDriveWay::mySwitchDriveWays.clear();
     126              :     MSDriveWay::myReversalDriveWays.clear();
     127              :     MSDriveWay::myDepartureDriveways.clear();
     128              :     MSDriveWay::myDepartDrivewayIndex.clear();
     129              :     MSDriveWay::myDepartureDrivewaysEnds.clear();
     130              :     MSDriveWay::myEndingDriveways.clear();
     131        42395 : }
     132              : 
     133              : void
     134          167 : MSDriveWay::clearState() {
     135          192 :     for (auto item : myEndingDriveways) {
     136           50 :         for (MSDriveWay* dw : item.second) {
     137              :             dw->myTrains.clear();
     138              :         }
     139              :     }
     140          167 : }
     141              : 
     142              : 
     143              : bool
     144        40290 : MSDriveWay::notifyEnter(SUMOTrafficObject& veh, Notification reason, const MSLane* enteredLane) {
     145              : #ifdef DEBUG_MOVEREMINDER
     146              :     std::cout << SIMTIME << " notifyEnter " << getDescription() << " veh=" << veh.getID() << " lane=" << (MSGlobals::gUseMesoSim ? veh.getCurrentEdge()->getID() : Named::getIDSecure(enteredLane)) << " reason=" << reason << "\n";
     147              : #endif
     148              : 
     149        40290 :     if (veh.isVehicle() && MSRailSignalControl::isUsingDriveWays(veh.getVClass())) {
     150        13658 :         if ((enteredLane == myLane || (MSGlobals::gUseMesoSim && veh.getCurrentEdge() == &myLane->getEdge()))
     151        52459 :                 && (reason == NOTIFICATION_DEPARTED || reason == NOTIFICATION_JUNCTION || reason == NOTIFICATION_PARKING)) {
     152        38696 :             SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     153        38696 :             MSRouteIterator firstIt = std::find(sveh.getCurrentRouteEdge(), sveh.getRoute().end(), myLane->getNextNormal());
     154        38696 :             if (match(firstIt, sveh.getRoute().end())) {
     155        22952 :                 if (myTrains.count(&sveh) == 0) {
     156        22880 :                     enterDriveWay(sveh, reason);
     157              :                 }
     158        22916 :                 return true;
     159              :             }
     160         1536 :         } else if (reason == NOTIFICATION_REROUTE) {
     161         1473 :             SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     162              :             assert(myTrains.count(&sveh) == 0);
     163         1473 :             int movedPast = matchesPastRoute(sveh);
     164              :             // vehicle must still be one the drivway
     165         1473 :             if (movedPast >= 0 && movedPast < myForwardEdgeCount) {
     166          770 :                 enterDriveWay(sveh, reason);
     167          770 :                 return true;
     168              :             }
     169              :         }
     170              :     }
     171              :     return false;
     172              : }
     173              : 
     174              : 
     175              : bool
     176       151361 : MSDriveWay::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/, Notification reason, const MSLane* enteredLane) {
     177              :     UNUSED_PARAMETER(enteredLane);
     178              : #ifdef DEBUG_MOVEREMINDER
     179              :     std::cout << SIMTIME << " notifyLeave " << getDescription() << " veh=" << veh.getID() << " lane=" << Named::getIDSecure(enteredLane) << " reason=" << toString(reason) << "\n";
     180              : #endif
     181       151361 :     if (veh.isVehicle() && MSRailSignalControl::isUsingDriveWays(veh.getVClass())) {
     182              :         // leaving network with departure, teleport etc
     183       151361 :         if (reason != MSMoveReminder::NOTIFICATION_JUNCTION && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     184         7409 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     185         7409 :             if (myWriteVehicles) {
     186          804 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     187              :             }
     188         7409 :             return false;
     189       143952 :         } else if (MSGlobals::gUseMesoSim && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     190              :             // notifyLeave is called before moving the route iterator
     191        21500 :             const MSLane* leftLane = (*(dynamic_cast<SUMOVehicle&>(veh).getCurrentRouteEdge()))->getLanes().front();
     192        21500 :             return notifyLeaveBack(veh, reason, leftLane);
     193              :         } else {
     194              :             return true;
     195              :         }
     196              :     } else {
     197            0 :         return false;
     198              :     }
     199              : }
     200              : 
     201              : 
     202              : bool
     203        94168 : MSDriveWay::notifyLeaveBack(SUMOTrafficObject& veh, Notification reason, const MSLane* leftLane) {
     204              : #ifdef DEBUG_MOVEREMINDER
     205              :     std::cout << SIMTIME << " notifyLeaveBack " << getDescription() << " veh=" << veh.getID() << " lane=" << Named::getIDSecure(leftLane) << " reason=" << toString(reason) << "\n";
     206              : #endif
     207        94168 :     if (veh.isVehicle() && MSRailSignalControl::isUsingDriveWays(veh.getVClass())) {
     208        94168 :         if (leftLane == myForward.back() && (veh.getBackLane() != leftLane->getBidiLane() || MSGlobals::gUseMesoSim)) {
     209        15505 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     210        15505 :             if (myWriteVehicles) {
     211         1308 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     212              :             }
     213        15505 :             return false;
     214              :         } else {
     215        78663 :             return true;
     216              :         }
     217              :     } else {
     218            0 :         return false;
     219              :     }
     220              : }
     221              : 
     222              : 
     223              : bool
     224         1557 : MSDriveWay::notifyReroute(SUMOTrafficObject& veh) {
     225              : #ifdef DEBUG_MOVEREMINDER
     226              :     std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << "\n";
     227              : #endif
     228              :     assert(veh.isVehicle());
     229         1557 :     if (MSRailSignalControl::isUsingDriveWays(veh.getVClass())) {
     230         1555 :         SUMOVehicle* sveh = dynamic_cast<SUMOVehicle*>(&veh);
     231              :         assert(myTrains.count(sveh) != 0);
     232         1555 :         if (matchesPastRoute(*sveh) >= 0) {
     233              :             //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " valid\n";
     234          774 :             return true;
     235              :         }
     236              :         // no match found, remove
     237              :         myTrains.erase(sveh);
     238          781 :         if (myWriteVehicles) {
     239          142 :             myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), NOTIFICATION_REROUTE));
     240              :         }
     241              :         //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " invalid\n";
     242              :     }
     243              :     return false;
     244              : }
     245              : 
     246              : 
     247              : int
     248         3028 : MSDriveWay::matchesPastRoute(SUMOVehicle& sveh) const {
     249              :     // look backwards along the route to find the driveway lane
     250         3028 :     const ConstMSEdgeVector& routeEdges = sveh.getRoute().getEdges();
     251        18908 :     for (int i = sveh.getRoutePosition(); i >= 0; i--) {
     252        18490 :         if (routeEdges[i] == myLane->getNextNormal()) {
     253              :             MSRouteIterator firstIt = routeEdges.begin() + i;
     254         2610 :             if (match(firstIt, sveh.getRoute().end())) {
     255              :                 // driveway is still valid after rerouting
     256              :                 //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " valid\n";
     257         1634 :                 return sveh.getRoutePosition() - i;
     258              :             }
     259          976 :             break;
     260              :         }
     261              :     }
     262              :     return -1;
     263              : }
     264              : 
     265              : 
     266              : void
     267        23650 : MSDriveWay::enterDriveWay(SUMOVehicle& sveh, Notification reason) {
     268        23650 :     myTrains.insert(&sveh);
     269        23650 :     if (myOrigin != nullptr) {
     270        15799 :         MSRailSignalControl::getInstance().notifyApproach(myOrigin);
     271              :     }
     272        78251 :     for (const MSDriveWay* foe : myFoes) {
     273        54601 :         if (foe->myOrigin != nullptr) {
     274        42476 :             MSRailSignalControl::getInstance().notifyApproach(foe->myOrigin);
     275              :         }
     276              :     }
     277        23650 :     if (myWriteVehicles) {
     278         2234 :         myVehicleEvents.push_back(VehicleEvent(SIMSTEP, true, sveh.getID(), reason));
     279              :     }
     280        23650 : }
     281              : 
     282              : bool
     283       722102 : MSDriveWay::reserve(const Approaching& closest, MSEdgeVector& occupied) {
     284       722102 :     if (foeDriveWayOccupied(true, closest.first, occupied)) {
     285              :         return false;
     286              :     }
     287       619775 :     for (MSLink* foeLink : myConflictLinks) {
     288       150532 :         if (hasLinkConflict(closest, foeLink)) {
     289              : #ifdef DEBUG_SIGNALSTATE
     290              :             if (gDebugFlag4 || DEBUG_HELPER(closest.first)) {
     291              :                 std::cout << getID() << " linkConflict with " << getTLLinkID(foeLink) << "\n";
     292              :             }
     293              : #endif
     294              :             return false;
     295              :         }
     296              :     }
     297       469243 :     myActive = closest.first;
     298       469243 :     return true;
     299              : }
     300              : 
     301              : 
     302              : bool
     303       150532 : MSDriveWay::hasLinkConflict(const Approaching& veh, const MSLink* foeLink) const {
     304              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     305              :     if (gDebugFlag4) {
     306              :         std::cout << "   checkLinkConflict foeLink=" << getTLLinkID(foeLink) << " ego=" << Named::getIDSecure(veh.first) << "\n";
     307              :     }
     308              : #endif
     309       150532 :     if (foeLink->getApproaching().size() > 0) {
     310        51598 :         Approaching foe = foeLink->getClosest();
     311              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     312              :         if (gDebugFlag4) {
     313              :             std::cout << "     approaching foe=" << foe.first->getID() << "\n";
     314              :         }
     315              : #endif
     316        51598 :         if (foe.first == veh.first) {
     317        51598 :             return false;
     318              :         }
     319              :         const MSTrafficLightLogic* foeTLL = foeLink->getTLLogic();
     320              :         assert(foeTLL != nullptr);
     321        46526 :         const MSRailSignal* constFoeRS = dynamic_cast<const MSRailSignal*>(foeTLL);
     322              :         MSRailSignal* foeRS = const_cast<MSRailSignal*>(constFoeRS);
     323        46526 :         if (foeRS != nullptr) {
     324        46526 :             const MSDriveWay& foeDriveWay = foeRS->retrieveDriveWayForVeh(foeLink->getTLIndex(), foe.first);
     325              :             MSEdgeVector occupied;
     326        71405 :             if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied) ||
     327        41402 :                     !foeRS->constraintsAllow(foe.first) ||
     328        32765 :                     !overlap(foeDriveWay) ||
     329        62768 :                     getFoeOrSubFoe(&foeDriveWay) == nullptr ||
     330        13443 :                     canUseSiding(veh.first, getFoeOrSubFoe(&foeDriveWay)).first) {
     331              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     332              :                 if (gDebugFlag4 || veh.first->isSelected()) {
     333              :                     if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied)) {
     334              :                         std::cout << "     foe blocked\n";
     335              :                     } else if (!foeRS->constraintsAllow(foe.first)) {
     336              :                         std::cout << "     foe constrained\n";
     337              :                     } else if (!overlap(foeDriveWay)) {
     338              :                         std::cout << "     no overlap with foeDW=" << foeDriveWay.getID() << "\n";
     339              :                     } else if (getFoeOrSubFoe(&foeDriveWay) == nullptr) {
     340              :                         std::cout << "     foeDW=" << foeDriveWay.getID() << " is not a foe to " << getID() << "\n";
     341              :                     } else if (canUseSiding(veh.first, &foeDriveWay).first) {
     342              :                         std::cout << "     use siding\n";
     343              :                     }
     344              :                 }
     345              : #endif
     346        34533 :                 return false;
     347              :             }
     348              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     349              :             if (gDebugFlag4) {
     350              :                 std::cout
     351              :                         << "  aSB=" << veh.second.arrivalSpeedBraking << " foeASB=" << foe.second.arrivalSpeedBraking
     352              :                         << "  aT=" << veh.second.arrivalTime << " foeAT=" << foe.second.arrivalTime
     353              :                         << "  aS=" << veh.first->getSpeed() << " foeS=" << foe.first->getSpeed()
     354              :                         << "  aD=" << veh.second.dist << " foeD=" << foe.second.dist
     355              :                         << "  aW=" << veh.first->getWaitingTime() << " foeW=" << foe.first->getWaitingTime()
     356              :                         << "  aN=" << veh.first->getNumericalID() << " foeN=" << foe.first->getNumericalID()
     357              :                         << "\n";
     358              :             }
     359              : #endif
     360        11993 :             const bool yield = mustYield(veh, foe);
     361        11993 :             if (MSRailSignal::storeVehicles()) {
     362          540 :                 MSRailSignal::rivalVehicles().push_back(foe.first);
     363          540 :                 if (yield) {
     364          315 :                     MSRailSignal::priorityVehicles().push_back(foe.first);
     365              :                 }
     366              :             }
     367        11993 :             return yield;
     368        46526 :         }
     369              :     }
     370              :     return false;
     371              : }
     372              : 
     373              : 
     374              : const MSDriveWay*
     375        75573 : MSDriveWay::getFoeOrSubFoe(const MSDriveWay* dw) const {
     376        75573 :     if (std::find(myFoes.begin(), myFoes.end(), dw) != myFoes.end()) {
     377        44954 :         return dw;
     378              :     }
     379        35409 :     for (const MSDriveWay* sub : dw->mySubDriveWays) {
     380        27820 :         const MSDriveWay* foe = getFoeOrSubFoe(sub);
     381        27820 :         if (foe != nullptr) {
     382              :             return foe;
     383              :         }
     384              :     }
     385              :     return nullptr;
     386              : }
     387              : 
     388              : 
     389              : bool
     390        11993 : MSDriveWay::mustYield(const Approaching& veh, const Approaching& foe) {
     391        11993 :     if (foe.second.arrivalSpeedBraking == veh.second.arrivalSpeedBraking) {
     392         8222 :         if (foe.second.arrivalTime == veh.second.arrivalTime) {
     393          562 :             if (foe.first->getSpeed() == veh.first->getSpeed()) {
     394          538 :                 if (foe.second.dist == veh.second.dist) {
     395          502 :                     if (foe.first->getWaitingTime() == veh.first->getWaitingTime()) {
     396          484 :                         return foe.first->getNumericalID() < veh.first->getNumericalID();
     397              :                     } else {
     398           18 :                         return foe.first->getWaitingTime() > veh.first->getWaitingTime();
     399              :                     }
     400              :                 } else {
     401           36 :                     return foe.second.dist < veh.second.dist;
     402              :                 }
     403              :             } else {
     404           24 :                 return foe.first->getSpeed() > veh.first->getSpeed();
     405              :             }
     406              :         } else {
     407         7660 :             return foe.second.arrivalTime < veh.second.arrivalTime;
     408              :         }
     409              :     } else {
     410         3771 :         return foe.second.arrivalSpeedBraking > veh.second.arrivalSpeedBraking;
     411              :     }
     412              : }
     413              : 
     414              : 
     415              : bool
     416        16035 : MSDriveWay::conflictLaneOccupied(bool store, const SUMOVehicle* ego) const {
     417       210513 :     for (const MSLane* lane : myConflictLanes) {
     418              :         if (!lane->isEmpty()) {
     419         4410 :             std::string joinVehicle = "";
     420         4410 :             if (ego != nullptr && !MSGlobals::gUseMesoSim) {
     421            0 :                 const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
     422            0 :                 if (stop != nullptr) {
     423            0 :                     joinVehicle = stop->join;
     424              :                 }
     425              :             }
     426              : #ifdef DEBUG_SIGNALSTATE
     427              :             if (gDebugFlag4) {
     428              :                 std::cout << SIMTIME << " conflictLane " << lane->getID() << " occupied ego=" << Named::getIDSecure(ego) << " vehNumber=" << lane->getVehicleNumber() << "\n";
     429              :                 if (joinVehicle != "") {
     430              :                     std::cout << "  joinVehicle=" << joinVehicle << " occupant=" << toString(lane->getVehiclesSecure()) << "\n";
     431              :                     lane->releaseVehicles();
     432              :                 }
     433              :             }
     434              : #endif
     435         4410 :             if (lane->getVehicleNumberWithPartials() == 1) {
     436         4410 :                 MSVehicle* foe = lane->getLastAnyVehicle();
     437         4410 :                 if (joinVehicle != "") {
     438            0 :                     if (foe->getID() == joinVehicle && foe->isStopped()) {
     439              : #ifdef DEBUG_SIGNALSTATE
     440              :                         if (gDebugFlag4) {
     441              :                             std::cout << "    ignore join-target '" << joinVehicle << "\n";
     442              :                         }
     443              : #endif
     444            0 :                         continue;
     445              :                     }
     446              :                 }
     447         4410 :                 if (ego != nullptr) {
     448            0 :                     if (foe == ego && std::find(myForward.begin(), myForward.end(), lane) == myForward.end()) {
     449              : #ifdef DEBUG_SIGNALSTATE
     450              :                         if (gDebugFlag4) {
     451              :                             std::cout << "    ignore ego as oncoming '" << ego->getID() << "\n";
     452              :                         }
     453              : #endif
     454            0 :                         continue;
     455              :                     }
     456            0 :                     if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
     457              : #ifdef DEBUG_SIGNALSTATE
     458              :                         if (gDebugFlag4) {
     459              :                             std::cout << "    ignore " << foe->getID() << " for which ego is join-target\n";
     460              :                         }
     461              : #endif
     462            0 :                         continue;
     463              :                     }
     464              :                 }
     465              :             }
     466         4410 :             if (MSRailSignal::storeVehicles() && store) {
     467         4410 :                 MSRailSignal::blockingVehicles().push_back(lane->getLastAnyVehicle());
     468              :             }
     469              :             return true;
     470              :         }
     471              :     }
     472        11625 :     return false;
     473              : }
     474              : 
     475              : 
     476              : bool
     477      8089119 : MSDriveWay::foeDriveWayApproached() const {
     478     16321601 :     for (const MSDriveWay* foeDW : myFoes) {
     479     16244395 :         if (foeDW->myOrigin != nullptr && foeDW->myOrigin->getApproaching().size() > 0) {
     480              : #ifdef DEBUG_SIGNALSTATE
     481              :             if (gDebugFlag4) {
     482              :                 std::cout << SIMTIME << " foeLink=" << foeDW->myOrigin->getDescription() << " approachedBy=" << foeDW->myOrigin->getApproaching().begin()->first->getID() << "\n";
     483              :             }
     484              : #endif
     485              :             return true;
     486              :         }
     487              :     }
     488              :     return false;
     489              : }
     490              : 
     491              : 
     492              : bool
     493     17591252 : MSDriveWay::foeDriveWayOccupied(bool store, const SUMOVehicle* ego, MSEdgeVector& occupied) const {
     494     36203794 :     for (const MSDriveWay* foeDW : myFoes) {
     495     27616952 :         if (!foeDW->myTrains.empty()) {
     496              : #ifdef DEBUG_SIGNALSTATE
     497              :             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     498              :                 std::cout << SIMTIME << " " << getID() << " foeDriveWay " << foeDW->getID() << " occupied ego=" << Named::getIDSecure(ego) << " foeVeh=" << toString(foeDW->myTrains) << "\n";
     499              :             }
     500              : #endif
     501      9054238 :             if (foeDW->myTrains.size() == 1) {
     502      9052012 :                 SUMOVehicle* foe = *foeDW->myTrains.begin();
     503      9052012 :                 if (foe == ego) {
     504              : #ifdef DEBUG_SIGNALSTATE
     505              :                     if (gDebugFlag4 || DEBUG_HELPER(ego)) {
     506              :                         std::cout << "    ignore ego as foe '" << Named::getIDSecure(ego) << "\n";
     507              :                     }
     508              : #endif
     509        49916 :                     continue;
     510              :                 }
     511      9028192 :                 if (hasJoin(ego, foe)) {
     512           36 :                     continue;
     513              :                 }
     514              :             }
     515      9030382 :             std::pair<bool, const MSDriveWay*> useSiding = canUseSiding(ego, foeDW);
     516              : #ifdef DEBUG_SIGNALSTATE
     517              :             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     518              :                 auto it = mySidings.find(foeDW);
     519              :                 int numSidings = 0;
     520              :                 if (it != mySidings.end()) {
     521              :                     numSidings = it->second.size();
     522              :                 }
     523              :                 std::cout << " ego=" << Named::getIDSecure(ego) << " useSiding=" << useSiding.first << " sidingFoe=" << Named::getIDSecure(useSiding.second) << " numSidings=" << numSidings << "\n";
     524              :             }
     525              : #endif
     526      9030382 :             if (useSiding.first) {
     527        26060 :                 continue;
     528              :             } else {
     529      9004322 :                 if (MSRailSignal::storeVehicles() && store) {
     530          740 :                     for (SUMOVehicle* foe : foeDW->myTrains) {
     531          370 :                         MSRailSignal::blockingVehicles().push_back(foe);
     532              :                     }
     533          370 :                     MSRailSignal::blockingDriveWays().push_back(foeDW);
     534              :                 }
     535     18011341 :                 for (const SUMOVehicle* foe : foeDW->myTrains) {
     536      9007019 :                     occupied.push_back(const_cast<MSEdge*>(foe->getEdge()));
     537      9007019 :                     MSEdge* bidi = const_cast<MSEdge*>(foe->getEdge()->getBidiEdge());
     538      9007019 :                     if (bidi != nullptr) {
     539      8491649 :                         occupied.push_back(bidi);
     540              :                     }
     541              :                     /// @todo: if foe occupies more than one edge we should add all of them to the occupied vector
     542              :                 }
     543      8292785 :                 if (ego != nullptr && MSGlobals::gTimeToTeleportRSDeadlock > 0
     544      9282589 :                         && (ego->getWaitingTime() > ego->getVehicleType().getCarFollowModel().getStartupDelay() || !ego->isOnRoad())) {
     545              :                     // if there is an occupied siding, it becomes part of the waitRelation
     546       147820 :                     SUMOVehicle* foe = *(useSiding.second == nullptr ? foeDW : useSiding.second)->myTrains.begin();
     547       147820 :                     const MSRailSignal* rs = myOrigin != nullptr ? dynamic_cast<const MSRailSignal*>(myOrigin->getTLLogic()) : nullptr;
     548       147820 :                     MSRailSignalControl::getInstance().addWaitRelation(ego, rs, foe);
     549              :                 }
     550      9004322 :                 return true;
     551              :             }
     552     18562714 :         } else if (foeDW != this && isDepartDriveway() && !foeDW->isDepartDriveway()) {
     553        16336 :             if (foeDW->myOrigin->getApproaching().size() > 0) {
     554         3359 :                 Approaching foeA = foeDW->myOrigin->getClosest();
     555         3359 :                 const SUMOVehicle* foe = foeA.first;
     556         3359 :                 if (foeA.second.dist < foe->getBrakeGap(true)) {
     557          254 :                     MSRouteIterator firstIt = std::find(foe->getCurrentRouteEdge(), foe->getRoute().end(), foeDW->myRoute.front());
     558          254 :                     if (firstIt != foe->getRoute().end()) {
     559          254 :                         if (foeDW->match(firstIt, foe->getRoute().end())) {
     560          203 :                             bool useSiding = canUseSiding(ego, foeDW).first;
     561              : #ifdef DEBUG_SIGNALSTATE
     562              :                             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     563              :                                 std::cout << SIMTIME << " " << getID() << " blocked by " << foeDW->getID() << " (approached by " << foe->getID() << ") useSiding=" << useSiding << "\n";
     564              :                             }
     565              : #endif
     566          203 :                             if (useSiding) {
     567              :                                 //std::cout << SIMTIME << " " << getID() << " ego=" << ego->getID() << " foeDW=" << foeDW->getID() << " myFoes=" << toString(myFoes) << "\n";
     568          115 :                                 continue;
     569              :                             } else {
     570           88 :                                 return true;
     571              :                             }
     572              :                         }
     573              :                     }
     574              :                 }
     575              :             }
     576              :         }
     577              :     }
     578      8587938 :     for (const std::set<const MSDriveWay*>& dlFoes : myDeadlocks) {
     579              :         bool allOccupied = true;
     580        10551 :         for (const MSDriveWay* dlFoe : dlFoes) {
     581         7836 :             if (dlFoe->myTrains.empty()) {
     582              :                 allOccupied = false;
     583              :                 //std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << "  deadlockCheck clear " << dlFoe->getID() << "\n";
     584              :                 break;
     585              :             }
     586              :         }
     587         3811 :         if (allOccupied) {
     588              : #ifdef DEBUG_SIGNALSTATE
     589              :             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     590              :                 std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " deadlockCheck " << joinNamedToString(dlFoes, " ") << "\n";
     591              :             }
     592              : #endif
     593         8974 :             for (const MSDriveWay* dlFoe : dlFoes) {
     594         6259 :                 MSRailSignal::blockingDriveWays().push_back(dlFoe);
     595              :             }
     596              :             return true;
     597              :         }
     598              :     }
     599              :     return false;
     600              : }
     601              : 
     602              : 
     603              : bool
     604      9028192 : MSDriveWay::hasJoin(const SUMOVehicle* ego, const SUMOVehicle* foe) {
     605      9028192 :     if (ego != nullptr && !MSGlobals::gUseMesoSim) {
     606       277635 :         std::string joinVehicle = "";
     607       277635 :         const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
     608       277635 :         if (stop != nullptr) {
     609       119114 :             joinVehicle = stop->join;
     610              :         }
     611       277635 :         if (joinVehicle == "" && !ego->hasDeparted() && ego->getStops().size() > 1) {
     612              :             // check one more stop
     613         5019 :             auto it = ego->getStops().begin();
     614              :             std::advance(it, 1);
     615         5019 :             joinVehicle = it->pars.join;
     616              :         }
     617       277635 :         if (joinVehicle != "") {
     618              : #ifdef DEBUG_SIGNALSTATE
     619              :             if (gDebugFlag4 || DEBUG_COND_DW(ego) || DEBUG_COND_DW(foe)) {
     620              :                 std::cout << "  joinVehicle=" << joinVehicle << "\n";
     621              :             }
     622              : #endif
     623          345 :             if (foe->getID() == joinVehicle && foe->isStopped()) {
     624              : #ifdef DEBUG_SIGNALSTATE
     625              :                 if (gDebugFlag4 || DEBUG_COND_DW(ego) || DEBUG_COND_DW(foe)) {
     626              :                     std::cout << "    ignore join-target '" << joinVehicle << "\n";
     627              :                 }
     628              : #endif
     629              :                 return true;
     630              :             }
     631              :         }
     632              : 
     633       277611 :         if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
     634              : #ifdef DEBUG_SIGNALSTATE
     635              :             if (gDebugFlag4 || DEBUG_COND_DW(ego) || DEBUG_COND_DW(foe)) {
     636              :                 std::cout << "    ignore " << foe->getID() << " for which ego is join-target\n";
     637              :             }
     638              : #endif
     639              :             return true;
     640              :         }
     641              :     }
     642              :     return false;
     643              : }
     644              : 
     645              : 
     646              : std::pair<bool, const MSDriveWay*>
     647      9062096 : MSDriveWay::canUseSiding(const SUMOVehicle* ego, const MSDriveWay* foe, const MSEdge* recurseSidingEnd) const {
     648              :     auto it = mySidings.find(foe);
     649      9062096 :     if (it != mySidings.end()) {
     650        69329 :         for (auto siding : it->second) {
     651              :             // assume siding is usuable when computing state for unapproached signal (ego == nullptr)
     652        69129 :             if (ego == nullptr || siding.length >= ego->getLength()) {
     653              :                 // if the siding is already "reserved" by another vehicle we cannot use it here
     654        59204 :                 const MSEdge* sidingEnd = myRoute[siding.end];
     655              :                 bool checkNext = false;
     656       138368 :                 for (MSDriveWay* sidingApproach : myEndingDriveways[sidingEnd]) {
     657        97861 :                     if (!sidingApproach->myTrains.empty() && *sidingApproach->myTrains.begin() == ego && sidingEnd == recurseSidingEnd) {
     658              :                         // check next siding if if exists
     659              :                         checkNext = true;
     660              : #ifdef DEBUG_SIGNALSTATE
     661              :                         if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     662              :                             std::cout << "   checkNext\n";
     663              :                         }
     664              : #endif
     665         3606 :                         continue;
     666              :                     }
     667        94255 :                     if (!sidingApproach->myTrains.empty() && *sidingApproach->myTrains.begin() != ego) {
     668              :                         // possibly the foe vehicle can use the other part of the siding
     669        24739 :                         if (recurseSidingEnd == nullptr) {
     670              :                             const SUMOVehicle* foeVeh = nullptr;
     671        17217 :                             if (!foe->myTrains.empty()) {
     672        17088 :                                 foeVeh = *foe->myTrains.begin();
     673          129 :                             } else if (foe->myOrigin != nullptr && foe->myOrigin->getApproaching().size() > 0) {
     674          129 :                                 foeVeh = foe->myOrigin->getClosest().first;
     675              :                             }
     676        17217 :                             if (foeVeh == nullptr) {
     677            0 :                                 WRITE_WARNINGF("Invalid call to canUseSiding dw=% foe=% ego=% time=%", getID(), foe->getID(), Named::getIDSecure(ego), time2string(SIMSTEP));
     678            0 :                                 continue;
     679              :                             }
     680        17217 :                             const MSDriveWay* foe2 = foe->isSubDriveWay() ? foe->myParent : foe;
     681        17217 :                             const MSDriveWay* this2 = foe2->getFoeOrSubFoe(this);
     682              : #ifdef DEBUG_SIGNALSTATE
     683              :                             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     684              :                                 std::cout << "   foe2=" << foe2->getID() << " this2=" << this2->getID() << "\n";
     685              :                             }
     686              : #endif
     687        17217 :                             if (this2 != nullptr && foe2->canUseSiding(foeVeh, this2, sidingEnd).first) {
     688         6042 :                                 continue;
     689              :                             }
     690              :                         }
     691              :                         // possibly the foe vehicle
     692              :                         // @todo: in principle it might still be possible to continue if vehicle that approaches the siding can safely leave the situation
     693              : #ifdef DEBUG_SIGNALSTATE
     694              :                         if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     695              :                             std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
     696              :                                       << " foeVeh=" << toString(foe->myTrains)
     697              :                                       << " sidingEnd=" << sidingEnd->getID() << " sidingApproach=" << sidingApproach->getID() << " approaching=" << toString(sidingApproach->myTrains) << "\n";
     698              :                         }
     699              : #endif
     700        18697 :                         return std::make_pair(false, sidingApproach);
     701              :                     }
     702              :                 }
     703              :                 // vehicles approaching intermediate driveways could also make the siding unusable but would not show up as sidingApproaches
     704        45756 :                 for (int i : siding.intermediateEnds) {
     705         8283 :                     const MSEdge* intermediateEnd = myRoute[i];
     706        15528 :                     for (MSDriveWay* intermediateApproach : myEndingDriveways[intermediateEnd]) {
     707              :                         if (!intermediateApproach->myTrains.empty()
     708        10279 :                                 && (*intermediateApproach->myTrains.begin() != ego || intermediateEnd == recurseSidingEnd)) {
     709              :                             SUMOVehicle* onApproach = *intermediateApproach->myTrains.begin();
     710         3335 :                             if (std::find(onApproach->getCurrentRouteEdge(), onApproach->getRoute().end(), sidingEnd) == onApproach->getRoute().end()) {
     711              :                                 // intermediate vehicle does not make use of the siding
     712          101 :                                 continue;
     713              :                             }
     714              :                             // possibly the foe vehicle can use the other part of the siding
     715         3234 :                             if (recurseSidingEnd == nullptr) {
     716              :                                 const SUMOVehicle* foeVeh = nullptr;
     717          851 :                                 if (!foe->myTrains.empty()) {
     718          822 :                                     foeVeh = *foe->myTrains.begin();
     719           29 :                                 } else if (foe->myOrigin != nullptr && foe->myOrigin->getApproaching().size() > 0) {
     720           29 :                                     foeVeh = foe->myOrigin->getClosest().first;
     721              :                                 }
     722          851 :                                 if (foeVeh == nullptr) {
     723            0 :                                     WRITE_WARNINGF("Invalid call to canUseSiding dw=% foe=% ego=% time=%", getID(), foe->getID(), Named::getIDSecure(ego), time2string(SIMSTEP));
     724            0 :                                     continue;
     725              :                                 }
     726          851 :                                 const MSDriveWay* foe2 = foe->isSubDriveWay() ? foe->myParent : foe;
     727          851 :                                 const MSDriveWay* this2 = foe2->getFoeOrSubFoe(this);
     728          851 :                                 if (this2 != nullptr && foe2->canUseSiding(foeVeh, this2, intermediateEnd).first) {
     729          200 :                                     continue;
     730              :                                 }
     731              :                             }
     732              :                             // @todo: in principle it might still be possible to continue if vehicle that approaches the siding can safely leave the situation
     733              : #ifdef DEBUG_SIGNALSTATE
     734              :                             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     735              :                                 std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
     736              :                                           << " foeVeh=" << toString(foe->myTrains)
     737              :                                           << " sidingEnd=" << sidingEnd->getID() << " intermediateApproach=" << intermediateApproach->getID() << " approaching=" << toString(intermediateApproach->myTrains) << "\n";
     738              :                             }
     739              : #endif
     740         3034 :                             return std::make_pair(false, intermediateApproach);
     741              :                         }
     742              :                     }
     743              :                 }
     744        37473 :                 if (checkNext) {
     745         3606 :                     continue;
     746              :                 }
     747              : #ifdef DEBUG_SIGNALSTATE
     748              :                 if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     749              :                     std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
     750              :                               << " foeVeh=" << toString(foe->myTrains)
     751              :                               << " sidingEnd=" << sidingEnd->getID() << " usable\n";
     752              :                 }
     753              : #endif
     754        33867 :                 return std::make_pair(true, nullptr);
     755              :             }
     756              :         }
     757              :     }
     758              : #ifdef DEBUG_SIGNALSTATE
     759              :     if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     760              :         std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID() << " noSidings\n";
     761              :     }
     762              : #endif
     763      9006498 :     return std::make_pair(false, nullptr);
     764              : }
     765              : 
     766              : bool
     767        23783 : MSDriveWay::overlap(const MSDriveWay& other) const {
     768        43418 :     for (int i = 0; i < myCoreSize; i++) {
     769       415345 :         for (int j = 0; j < other.myCoreSize; j++) {
     770       395710 :             const MSEdge* edge = myRoute[i];
     771       395710 :             const MSEdge* edge2 = other.myRoute[j];
     772              :             if (edge->getToJunction() == edge2->getToJunction()
     773       374507 :                     || edge->getToJunction() == edge2->getFromJunction()
     774       768201 :                     || edge->getFromJunction() == edge2->getFromJunction()) {
     775              :                 // XXX might be rail_crossing with parallel tracks
     776              :                 return true;
     777              :             }
     778              :         }
     779              :     }
     780              :     return false;
     781              : }
     782              : 
     783              : 
     784              : bool
     785        80184 : MSDriveWay::flankConflict(const MSDriveWay& other) const {
     786       276830 :     for (const MSLane* lane : myForward) {
     787      1139952 :         for (const MSLane* lane2 : other.myForward) {
     788       933280 :             if (lane == lane2) {
     789              :                 return true;
     790              :             }
     791              :         }
     792      3014719 :         for (const MSLane* lane2 : other.myBidi) {
     793      2813396 :             if (lane == lane2) {
     794         6066 :                 if (bidiBlockedBy(other)) {
     795              :                     // it's only a deadlock if both trains block symmetrically
     796              :                     return true;
     797              :                 }
     798              :             }
     799              :         }
     800      7215761 :         for (const MSLane* lane2 : other.myBidiExtended) {
     801      7019115 :             if (lane == lane2) {
     802        23264 :                 if (bidiBlockedBy(other)) {
     803              :                     // it's only a deadlock if both trains block symmetrically
     804              :                     return true;
     805              :                 }
     806              :             }
     807              :         }
     808              :     }
     809              :     return false;
     810              : }
     811              : 
     812              : 
     813              : bool
     814        68289 : MSDriveWay::crossingConflict(const MSDriveWay& other) const {
     815       251582 :     for (const MSLane* lane : myForward) {
     816       860197 :         for (const MSLane* lane2 : other.myForward) {
     817       676904 :             if (lane->isNormal() && lane2->isNormal() && lane->getEdge().getToJunction() == lane2->getEdge().getToJunction()) {
     818              :                 return true;
     819              :             }
     820              :         }
     821              :     }
     822        60086 :     if (myOrigin != nullptr && other.myOrigin != nullptr
     823        52238 :             && myOrigin->getJunction() == other.myOrigin->getJunction()
     824              :             //&& myForward.front()->isInternal() && other.myForward.front()->isInternal()
     825         1862 :             && myOrigin->getJunction()->getLogic() != nullptr
     826        67946 :             && myOrigin->getJunction()->getLogic()->getFoesFor(myOrigin->getIndex()).test(other.myOrigin->getIndex())) {
     827              :         // switch/crossing is also a rail_signal (direct control)
     828          133 :         if (!(myForward.front()->isInternal() && other.myForward.front()->isInternal())) {
     829           34 :             return false;
     830              :         }
     831              :         return true;
     832              :     }
     833        65951 :     if (other.myOrigin != nullptr && other.myForward.front()->isInternal()) {
     834       154897 :         for (int i = 0; i < (int)myForward.size() - 1; i++) {
     835        99182 :             const MSLane* lane = myForward[i];
     836        99182 :             if (lane->getToJunction() == other.myOrigin->getJunction()) {
     837           58 :                 const MSLane* next = myForward[i + 1];
     838           58 :                 const MSLink* link = lane->getLinkTo(next);
     839           58 :                 if (link && link->getTLLogic() == nullptr) {
     840              :                     // switch/crossing is also a rail_signal (direct control) but own link is uncontrolled
     841           58 :                     if (lane->getToJunction()->getLogic() != nullptr
     842           58 :                             && lane->getToJunction()->getLogic()->getFoesFor(link->getIndex()).test(other.myOrigin->getIndex())) {
     843              :                         // and links are in conflict
     844              :                         return true;
     845              :                     }
     846              :                 }
     847              :             }
     848              :         }
     849              :     }
     850              :     return false;
     851              : }
     852              : 
     853              : 
     854              : bool
     855        30493 : MSDriveWay::bidiBlockedBy(const MSDriveWay& other) const {
     856       537446 :     for (const MSLane* lane : myBidi) {
     857      1962778 :         for (const MSLane* lane2 : other.myForward) {
     858      1455825 :             if (lane == lane2) {
     859              :                 return true;
     860              :             }
     861              :         }
     862              :     }
     863       542821 :     for (const MSLane* lane : myBidiExtended) {
     864      1851871 :         for (const MSLane* lane2 : other.myForward) {
     865      1333496 :             if (lane == lane2) {
     866         4330 :                 if (overlap(other)) {
     867              :                     return true;
     868              :                 }
     869              :             }
     870              :         }
     871              :     }
     872              :     return false;
     873              : }
     874              : 
     875              : 
     876              : bool
     877        17551 : MSDriveWay::bidiBlockedByEnd(const MSDriveWay& other) const {
     878        17551 :     const MSLane* end = other.myForward.back();
     879       152095 :     for (const MSLane* lane : myBidi) {
     880       140503 :         if (lane == end) {
     881              :             return true;
     882              :         }
     883              :     }
     884        98963 :     for (const MSLane* lane : myBidiExtended) {
     885        90301 :         if (lane == end) {
     886         2930 :             if (overlap(other)) {
     887              :                 return true;
     888              :             }
     889              :         }
     890              :     }
     891              :     return false;
     892              : }
     893              : 
     894              : bool
     895         6114 : MSDriveWay::forwardRouteConflict(std::set<const MSEdge*> forward, const MSDriveWay& other, bool secondCheck) {
     896              :     int i = 0;
     897        64809 :     for (const MSEdge* edge2 : other.myRoute) {
     898        63989 :         if (i == other.myCoreSize) {
     899              :             return false;
     900              :         }
     901        63989 :         i++;
     902        63989 :         if (edge2 == myForward.front()->getNextNormal() && !secondCheck) {
     903              :             // foe should not pass from behind through our own forward section
     904              :             return false;
     905              :         }
     906              :         if (forward.count(edge2->getBidiEdge()) != 0) {
     907              :             return true;
     908              :         }
     909              :     }
     910              :     return false;
     911              : }
     912              : 
     913              : void
     914         9380 : MSDriveWay::writeBlocks(OutputDevice& od) const {
     915        16463 :     od.openTag(isSubDriveWay() ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
     916         9380 :     od.writeAttr(SUMO_ATTR_ID, myID);
     917         9380 :     od.writeAttr(SUMO_ATTR_VEHICLE, myFirstVehicle);
     918         9380 :     od.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
     919         9380 :     if (myCoreSize != (int)myRoute.size()) {
     920            0 :         od.writeAttr("core", myCoreSize);
     921              :     }
     922         9380 :     od.openTag("forward");
     923         9380 :     od.writeAttr(SUMO_ATTR_LANES, toString(myForward));
     924        18760 :     od.closeTag();
     925         9380 :     if (!isSubDriveWay()) {
     926         7083 :         od.openTag("bidi");
     927         7083 :         od.writeAttr(SUMO_ATTR_LANES, toString(myBidi));
     928         7083 :         if (myBidiExtended.size() > 0) {
     929         2182 :             od.lf();
     930         2182 :             od << "                   ";
     931         4364 :             od.writeAttr("deadlockCheck", toString(myBidiExtended));
     932              :         }
     933         7083 :         od.closeTag();
     934         7083 :         od.openTag("flank");
     935         7083 :         od.writeAttr(SUMO_ATTR_LANES, toString(myFlank));
     936         7083 :         od.closeTag();
     937              : 
     938        14166 :         od.openTag("conflictLinks");
     939              : 
     940              :         std::vector<std::string> signals;
     941        11582 :         for (MSLink* link : myConflictLinks) {
     942         8998 :             signals.push_back(getTLLinkID(link));
     943              :         }
     944         7083 :         od.writeAttr("signals", joinToStringSorting(signals, " "));
     945        14166 :         od.closeTag();
     946              : 
     947              :         std::vector<std::string> foes;
     948        27147 :         for (MSDriveWay* dw : myFoes) {
     949        20064 :             foes.push_back(dw->myID);
     950              :         }
     951         7083 :         if (foes.size() > 0) {
     952         6916 :             od.openTag("foes");
     953         6916 :             od.writeAttr("driveWays", joinToStringSorting(foes, " "));
     954        13832 :             od.closeTag();
     955              :         }
     956         8751 :         for (auto item : mySidings) {
     957         1668 :             od.openTag("sidings");
     958         1668 :             od.writeAttr("foe", item.first->getID());
     959         4056 :             for (auto siding : item.second) {
     960         2388 :                 od.openTag("siding");
     961         2388 :                 od.writeAttr("start", myRoute[siding.start]->getID());
     962         2388 :                 od.writeAttr("end", myRoute[siding.end]->getID());
     963         2388 :                 od.writeAttr("length", siding.length);
     964         2388 :                 if (siding.intermediateEnds.size() > 0) {
     965              :                     std::vector<std::string> endEdges;
     966         1770 :                     for (int i : siding.intermediateEnds) {
     967          983 :                         endEdges.push_back(myRoute[i]->getID());
     968              :                     }
     969          787 :                     od.writeAttr("intermediateEnds", endEdges);
     970          787 :                 }
     971         4776 :                 od.closeTag();
     972              :             }
     973         3336 :             od.closeTag();
     974              :         }
     975         7176 :         for (auto item : myDeadlocks) {
     976           93 :             od.openTag("deadlock");
     977           93 :             od.writeAttr("foes", joinNamedToStringSorting(item, " "));
     978          186 :             od.closeTag();
     979              :         }
     980         7083 :     }
     981        18760 :     od.closeTag(); // driveWay
     982              : 
     983        11677 :     for (const MSDriveWay* sub : mySubDriveWays) {
     984         2297 :         sub->writeBlocks(od);
     985              :     }
     986              : #ifdef DRIVEWAY_SANITY_CHECK
     987         9380 :     std::set<MSDriveWay*> uFoes(myFoes.begin(), myFoes.end());
     988         9380 :     if (uFoes.size() != myFoes.size()) {
     989            0 :         WRITE_WARNINGF("Duplicate foes in driveway '%'", getID());
     990              : 
     991              :     }
     992              : #endif
     993         9380 : }
     994              : 
     995              : 
     996              : void
     997         1122 : MSDriveWay::writeBlockVehicles(OutputDevice& od) const {
     998         2006 :     od.openTag(isSubDriveWay() ? "subDriveWay" : "driveWay");
     999         1122 :     od.writeAttr(SUMO_ATTR_ID, myID);
    1000         3387 :     for (const VehicleEvent& ve : myVehicleEvents) {
    1001         3392 :         od.openTag(ve.isEntry ? "entry" : "exit");
    1002         2265 :         od.writeAttr(SUMO_ATTR_ID, ve.id);
    1003         2265 :         od.writeAttr(SUMO_ATTR_TIME, time2string(ve.time));
    1004         2265 :         od.writeAttr("reason", Notifications.getString(ve.reason));
    1005         4530 :         od.closeTag(); // event
    1006              :     }
    1007         2244 :     od.closeTag(); // driveWay
    1008              : 
    1009         1360 :     for (const MSDriveWay* sub : mySubDriveWays) {
    1010          238 :         sub->writeBlockVehicles(od);
    1011              :     }
    1012         1122 : }
    1013              : 
    1014              : 
    1015              : void
    1016         9283 : MSDriveWay::buildRoute(const MSLink* origin,
    1017              :                        MSRouteIterator next, MSRouteIterator end,
    1018              :                        LaneVisitedMap& visited,
    1019              :                        std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess>& flankSwitches) {
    1020         9283 :     double length = 0;
    1021              :     bool seekForwardSignal = true;
    1022              :     bool seekBidiSwitch = true;
    1023              :     bool foundUnsafeSwitch = false;
    1024         9283 :     MSLane* toLane = origin ? origin->getViaLaneOrLane() : (*next)->getLanes()[0];
    1025        24052 :     const std::string warnID = origin ? "rail signal " + getClickableTLLinkID(origin) : "insertion lane '" + toLane->getID() + "'";
    1026              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1027              :     if (gDebugFlag4) std::cout << "buildRoute origin=" << warnID << " vehRoute=" << toString(ConstMSEdgeVector(next, end))
    1028              :                                    << " visited=" << formatVisitedMap(visited) << "\n";
    1029              : #endif
    1030              :     while (true) {
    1031       130246 :         if (length > MSGlobals::gMaxRailSignalBlockLength) {
    1032              :             // typical block length in germany on main lines is 3-5km on branch lines up to 7km
    1033              :             // special branches that are used by one train exclusively could also be up to 20km in length
    1034              :             // minimum block size in germany is 37.5m (LZB)
    1035              :             // larger countries (USA, Russia) might see blocks beyond 20km)
    1036          242 :             if (seekForwardSignal && myBlockLengthWarnings.count(myRoute.front()) == 0) {
    1037          282 :                 WRITE_WARNINGF("Block after % exceeds maximum length (stopped searching after edge '%' (length=%m).",
    1038              :                                warnID, toLane->getEdge().getID(), length);
    1039              :                 myBlockLengthWarnings.insert(myRoute.front());
    1040              :             }
    1041          242 :             myAbortedBuild = true;
    1042              :             // length exceeded
    1043              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1044              :             if (gDebugFlag4) {
    1045              :                 std::cout << " abort: length=" << length << "\n";
    1046              :             }
    1047              : #endif
    1048         9283 :             return;
    1049              :         }
    1050              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1051              :         if (gDebugFlag4) {
    1052              :             std::cout << "   toLane=" << toLane->getID() << " visited=" << formatVisitedMap(visited) << "\n";
    1053              :         }
    1054              : #endif
    1055       130004 :         const MSEdge* current = &toLane->getEdge();
    1056       130004 :         if (current->isNormal()) {
    1057        69894 :             myRoute.push_back(current);
    1058        69894 :             if (next != end) {
    1059              :                 next++;
    1060              :             }
    1061              :         }
    1062       130004 :         appendMapIndex(visited, toLane);
    1063       130004 :         length += toLane->getLength();
    1064       130004 :         MSLane* bidi = toLane->getBidiLane();
    1065       130004 :         if (seekForwardSignal) {
    1066        37417 :             if (!foundUnsafeSwitch) {
    1067        37417 :                 myForward.push_back(toLane);
    1068        37417 :                 if (toLane->isNormal()) {
    1069        21916 :                     myForwardEdgeCount++;
    1070              :                 }
    1071        37417 :                 if (myForward.size() == 1) {
    1072         9283 :                     myLane = toLane;
    1073         9283 :                     if (MSGlobals::gUseMesoSim) {
    1074         3030 :                         MESegment* s = MSGlobals::gMesoNet->getSegmentForEdge(myLane->getEdge());
    1075         3030 :                         s->addDetector(this, myLane->getIndex());
    1076              :                     } else {
    1077         6253 :                         toLane->addMoveReminder(this, false);
    1078              :                     }
    1079              :                 }
    1080              :             }
    1081        92587 :         } else if (bidi == nullptr) {
    1082        29774 :             if (toLane->isInternal() && toLane->getIncomingLanes().front().viaLink->isTurnaround()) {
    1083              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1084              :                 if (gDebugFlag4) {
    1085              :                     std::cout << "      continue bidiSearch beyond turnaround\n";
    1086              :                 }
    1087              : #endif
    1088              :             } else {
    1089              :                 seekBidiSwitch = false;
    1090              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1091              :                 if (gDebugFlag4) {
    1092              :                     std::cout << "      noBidi, abort search for bidiSwitch\n";
    1093              :                 }
    1094              : #endif
    1095              :             }
    1096              :         }
    1097       130004 :         if (bidi != nullptr) {
    1098        83953 :             if (!seekForwardSignal && !foundUnsafeSwitch && bidi->isNormal()) {
    1099              :                 // look for switch that could protect from oncoming vehicles
    1100        24546 :                 for (const MSLink* const link : bidi->getLinkCont()) {
    1101        13803 :                     if (link->getDirection() == LinkDirection::TURN) {
    1102          531 :                         continue;
    1103              :                     }
    1104        26386 :                     if (!myBidi.empty() && link->getViaLaneOrLane() != myBidi.back()) {
    1105         2426 :                         myCoreSize = (int)myRoute.size() - 1;
    1106         2426 :                         MSLink* used = const_cast<MSLink*>(bidi->getLinkTo(myBidi.back()));
    1107              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1108              :                         if (gDebugFlag4) {
    1109              :                             std::cout << "      found unsafe switch " << link->getDescription() << " (used=" << (used == nullptr ? "NULL" : used->getDescription()) << ")\n";
    1110              :                         }
    1111              : #endif
    1112              :                         // trains along our route beyond this switch might create deadlock
    1113              :                         foundUnsafeSwitch = true;
    1114              :                         // the switch itself must still be guarded to ensure safety
    1115         2426 :                         if (used != nullptr) {
    1116              :                             // possibly nullptr if there was an intermediate section of unidirectional edges
    1117              :                             flankSwitches.insert(used);
    1118              :                         }
    1119              :                         break;
    1120              :                     }
    1121              :                 }
    1122              :             }
    1123        81527 :             if (foundUnsafeSwitch) {
    1124        40582 :                 myBidiExtended.push_back(bidi);
    1125              :             } else {
    1126        43371 :                 myBidi.push_back(bidi);
    1127              :             }
    1128              :         }
    1129       130004 :         const std::vector<MSLink*>& links = toLane->getLinkCont();
    1130       130004 :         toLane = nullptr;
    1131       137072 :         for (const MSLink* const link : links) {
    1132       128419 :             if ((next != end && &link->getLane()->getEdge() == *next)) {
    1133       121351 :                 toLane = link->getViaLaneOrLane();
    1134       121351 :                 if (link->getTLLogic() != nullptr && link->getTLIndex() >= 0 && link->getTLLogic()->getLogicType() == TrafficLightType::RAIL_SIGNAL) {
    1135        28477 :                     if (link == origin) {
    1136          388 :                         if (seekForwardSignal) {
    1137            0 :                             WRITE_WARNINGF(TL("Found circular block after % (% edges, length %)"), warnID, toString(myRoute.size()), toString(length));
    1138              :                         }
    1139              :                         //std::cout << getClickableTLLinkID(origin) << " circularBlock2=" << toString(myRoute) << "\n";
    1140          388 :                         myAbortedBuild = true;
    1141              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1142              :                         if (gDebugFlag4) {
    1143              :                             std::cout << " abort: found circle\n";
    1144              :                         }
    1145              : #endif
    1146          388 :                         return;
    1147              :                     }
    1148              :                     seekForwardSignal = false;
    1149        28089 :                     myFoundSignal = true;
    1150              :                     seekBidiSwitch = bidi != nullptr;
    1151              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1152              :                     if (gDebugFlag4) {
    1153              :                         std::cout << "      found forwardSignal " << link->getTLLogic()->getID() << " seekBidiSwitch=" << seekBidiSwitch << "\n";
    1154              :                     }
    1155              : #endif
    1156              :                 }
    1157              :                 //if (links.size() > 1 && !foundUnsafeSwitch) {
    1158       120963 :                 if (isSwitch(link)) {
    1159              :                     // switch on driveway
    1160              :                     //std::cout << "mySwitchDriveWays " << getID() << " link=" << link->getDescription() << "\n";
    1161        74863 :                     mySwitchDriveWays[link].push_back(this);
    1162              :                 }
    1163       120963 :                 if (link->getLane()->getBidiLane() != nullptr && &link->getLane()->getEdge() == current->getBidiEdge()) {
    1164              :                     // reversal on driveway
    1165         1777 :                     myReversalDriveWays[current].push_back(this);
    1166         1777 :                     myReversals.push_back(current);
    1167              :                 }
    1168              :                 break;
    1169              :             }
    1170              :         }
    1171       129616 :         if (toLane == nullptr) {
    1172         8653 :             if (next != end) {
    1173              :                 // no connection found, jump to next route edge
    1174              :                 toLane = (*next)->getLanes()[0];
    1175              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1176              :                 if (gDebugFlag4) {
    1177              :                     std::cout << "      abort: turn-around or jump\n";
    1178              :                 }
    1179              : #endif
    1180           90 :                 myFoundJump = true;
    1181           90 :                 return;
    1182              :             } else {
    1183              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1184              :                 if (gDebugFlag4) {
    1185              :                     std::cout << "      abort: no next lane available\n";
    1186              :                 }
    1187              : #endif
    1188         8563 :                 myTerminateRoute = true;
    1189         8563 :                 return;
    1190              :             }
    1191              :         }
    1192       120963 :     }
    1193              :     myBidiEnded = !seekBidiSwitch;
    1194              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1195              :     if (gDebugFlag4) {
    1196              :         std::cout << " normalEnd myBidiEnded=" << myBidiEnded << "\n";
    1197              :     }
    1198              : #endif
    1199              : }
    1200              : 
    1201              : 
    1202              : bool
    1203       133884 : MSDriveWay::isSwitch(const MSLink* link) {
    1204       260137 :     for (const MSLink* other : link->getLaneBefore()->getNormalPredecessorLane()->getLinkCont()) {
    1205       146779 :         if (other->getLane() != link->getLane() && !other->isTurnaround()) {
    1206              :             return true;
    1207              :         }
    1208              :     }
    1209       176353 :     for (auto ili : link->getLane()->getIncomingLanes()) {
    1210       127595 :         if (ili.viaLink != link && !ili.viaLink->isTurnaround()) {
    1211              :             return true;
    1212              :         }
    1213              :     }
    1214        48758 :     const MSLane* bidi = link->getLane()->getBidiLane();
    1215        48758 :     if (bidi != nullptr) {
    1216        71762 :         for (const MSLink* other : bidi->getLinkCont()) {
    1217        37083 :             if (other->getLane() != link->getLaneBefore()->getNormalPredecessorLane()->getBidiLane() && !other->isTurnaround()) {
    1218              :                 return true;
    1219              :             }
    1220              :         }
    1221              :     }
    1222              :     return false;
    1223              : }
    1224              : 
    1225              : 
    1226              : void
    1227        37132 : MSDriveWay::checkFlanks(const MSLink* originLink, const std::vector<const MSLane*>& lanes, const LaneVisitedMap& visited,
    1228              :                         bool allFoes, bool movingBlock, std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess>& flankSwitches) const {
    1229              : #ifdef DEBUG_CHECK_FLANKS
    1230              :     if (gDebugFlag4) {
    1231              :         std::cout << " checkFlanks lanes=" << toString(lanes) << " allFoes=" << allFoes << "\n";
    1232              :     }
    1233              : #endif
    1234        21944 :     const MSLink* reverseOriginLink = originLink != nullptr && originLink->getLane()->getBidiLane() != nullptr && originLink->getLaneBefore()->getBidiLane() != nullptr
    1235        49000 :                                       ? originLink->getLane()->getBidiLane()->getLinkTo(originLink->getLaneBefore()->getBidiLane())
    1236              :                                       : nullptr;
    1237              :     //std::cout << "   originLink=" << originLink->getDescription() << "\n";
    1238        11868 :     if (reverseOriginLink != nullptr) {
    1239        11868 :         reverseOriginLink = reverseOriginLink->getCorrespondingExitLink();
    1240              :         //std::cout << "   reverseOriginLink=" << reverseOriginLink->getDescription() << "\n";
    1241              :     }
    1242       161551 :     for (int i = 0; i < (int)lanes.size(); i++) {
    1243       124419 :         const MSLane* lane = lanes[i];
    1244       124419 :         const MSLane* prev = i > 0 ? lanes[i - 1] : nullptr;
    1245       124419 :         const MSLane* next = i + 1 < (int)lanes.size() ? lanes[i + 1] : nullptr;
    1246       124419 :         if (lane->isInternal()) {
    1247        53369 :             continue;
    1248              :         }
    1249       147455 :         for (auto ili : lane->getIncomingLanes()) {
    1250        86942 :             if (ili.viaLink == originLink
    1251        75457 :                     || ili.viaLink == reverseOriginLink
    1252        72153 :                     || ili.viaLink->getDirection() == LinkDirection::TURN
    1253        65987 :                     || ili.viaLink->getDirection() == LinkDirection::TURN_LEFTHAND
    1254       142392 :                     || (originLink == nullptr && i == 0 && movingBlock)) {
    1255        10537 :                 continue;
    1256              :             }
    1257        65868 :             if (ili.lane != prev && ili.lane != next) {
    1258              : #ifdef DEBUG_CHECK_FLANKS
    1259              :                 if (gDebugFlag4) {
    1260              :                     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";
    1261              :                 }
    1262              : #endif
    1263              :                 flankSwitches.insert(ili.viaLink);
    1264        52984 :             } else if (allFoes) {
    1265              :                 // link is part of the driveway, find foes that cross the driveway without entering
    1266        16078 :                 checkCrossingFlanks(ili.viaLink, visited, flankSwitches);
    1267              :             }
    1268              :         }
    1269              :     }
    1270        37132 : }
    1271              : 
    1272              : 
    1273              : void
    1274        16078 : MSDriveWay::checkCrossingFlanks(MSLink* dwLink, const LaneVisitedMap& visited, std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess>& flankSwitches) const {
    1275              : #ifdef DEBUG_CHECK_FLANKS
    1276              :     if (gDebugFlag4) {
    1277              :         std::cout << "  checkCrossingFlanks  dwLink=" << dwLink->getDescription() << " visited=" << formatVisitedMap(visited) << "\n";
    1278              :     }
    1279              : #endif
    1280              :     const MSJunction* junction = dwLink->getJunction();
    1281        16078 :     if (junction == nullptr) {
    1282              :         return; // unregulated junction;
    1283              :     }
    1284        15998 :     const MSJunctionLogic* logic = junction->getLogic();
    1285        15998 :     if (logic == nullptr) {
    1286              :         return; // unregulated junction;
    1287              :     }
    1288        84713 :     for (const MSEdge* in : junction->getIncoming()) {
    1289        68783 :         if (in->isInternal()) {
    1290        34855 :             continue;
    1291              :         }
    1292        68274 :         for (MSLane* inLane : in->getLanes()) {
    1293        34346 :             const MSLane* inBidi = inLane->getBidiLane();
    1294        46784 :             if (isRailwayOrShared(inLane->getPermissions()) && visited.count(inLane) == 0 && (inBidi == nullptr || visited.count(inBidi) == 0)) {
    1295         9495 :                 for (MSLink* link : inLane->getLinkCont()) {
    1296         4943 :                     if (link->getIndex() >= 0 && logic->getFoesFor(dwLink->getIndex()).test(link->getIndex())
    1297        12764 :                             && visited.count(link->getLane()) == 0) {
    1298              : #ifdef DEBUG_CHECK_FLANKS
    1299              :                         if (gDebugFlag4) {
    1300              :                             std::cout << " add crossing flankSwitch junction=" << junction->getID() << " index=" << link->getIndex() << "\n";
    1301              :                         }
    1302              : #endif
    1303          672 :                         if (link->getViaLane() == nullptr) {
    1304              :                             flankSwitches.insert(link);
    1305              :                         } else {
    1306              :                             flankSwitches.insert(link->getViaLane()->getLinkCont().front());
    1307              :                         }
    1308              :                     }
    1309              :                 }
    1310              :             }
    1311              :         }
    1312              :     }
    1313              : }
    1314              : 
    1315              : void
    1316        16042 : MSDriveWay::findFlankProtection(MSLink* link, MSLink* origLink, std::vector<const MSLane*>& flank) {
    1317              : #ifdef DEBUG_CHECK_FLANKS
    1318              :     if (gDebugFlag4) {
    1319              :         std::cout << "  findFlankProtection link=" << link->getDescription() << " origLink=" << origLink->getDescription() << "\n";
    1320              :     }
    1321              : #endif
    1322        16042 :     if (link->getCorrespondingEntryLink()->getTLLogic() != nullptr && link->getJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1323         2286 :         MSLink* entry = const_cast<MSLink*>(link->getCorrespondingEntryLink());
    1324              :         // guarded by signal
    1325              : #ifdef DEBUG_CHECK_FLANKS
    1326              :         if (gDebugFlag4) {
    1327              :             std::cout << "   flank guarded by " << entry->getTLLogic()->getID() << "\n";
    1328              :         }
    1329              : #endif
    1330              :         // @note, technically it's enough to collect links from foe driveways
    1331              :         // but this also adds "unused" conflict links which may aid comprehension
    1332         2286 :         myConflictLinks.push_back(entry);
    1333         2286 :         addFoes(entry);
    1334              :     } else {
    1335              :         const MSLane* lane = link->getLaneBefore();
    1336              :         std::vector<MSLink*> predLinks;
    1337        27522 :         for (auto ili : lane->getIncomingLanes()) {
    1338        13766 :             if (!ili.viaLink->isTurnaround()) {
    1339        13363 :                 predLinks.push_back(ili.viaLink);
    1340              :             }
    1341              :         }
    1342        13756 :         if (predLinks.size() > 1) {
    1343              :             // this is a switch
    1344              : #ifdef DEBUG_ADD_FOES
    1345              :             if (gDebugFlag4) {
    1346              :                 std::cout << "    predecessors of " << link->getDescription() << " isSwitch\n";
    1347              :             }
    1348              : #endif
    1349          663 :             for (MSLink* pred : predLinks) {
    1350          442 :                 addSwitchFoes(pred);
    1351              :             }
    1352        13535 :         } else if (predLinks.size() == 1) {
    1353        12921 :             if (isSwitch(link)) {
    1354        11419 :                 addSwitchFoes(link);
    1355              :             } else {
    1356              :                 // continue upstream via single predecessor
    1357         1502 :                 findFlankProtection(predLinks.front(), origLink, flank);
    1358              :             }
    1359              :         }
    1360              :         // check for insertions
    1361        13756 :         if (myDepartureDriveways.count(&lane->getEdge()) != 0) {
    1362          354 :             for (MSDriveWay* foe : myDepartureDriveways[&lane->getEdge()]) {
    1363          183 :                 if (flankConflict(*foe) || crossingConflict(*foe)) {
    1364              : #ifdef DEBUG_ADD_FOES
    1365              :                     if (gDebugFlag4) {
    1366              :                         std::cout << "  foe " << foe->getID() << " departs on flank=" << lane->getID() << "\n";
    1367              :                     }
    1368              : #endif
    1369          129 :                     myFoes.push_back(foe);
    1370              :                 } else {
    1371              : #ifdef DEBUG_ADD_FOES
    1372              :                     if (gDebugFlag4) {
    1373              :                         std::cout << "  cand foe " << foe->getID() << " departs on flank=" << lane->getID() << " rejected\n";
    1374              :                     }
    1375              : #endif
    1376              :                 }
    1377              :             }
    1378              :         }
    1379        13756 :     }
    1380        16042 : }
    1381              : 
    1382              : 
    1383              : void
    1384        11861 : MSDriveWay::addSwitchFoes(MSLink* link) {
    1385              :     auto it = mySwitchDriveWays.find(link);
    1386        11861 :     if (it != mySwitchDriveWays.end()) {
    1387              : #ifdef DEBUG_ADD_FOES
    1388              :         if (gDebugFlag4) {
    1389              :             std::cout << "   driveway " << myID << " addSwitchFoes for link " << link->getDescription() << "\n";
    1390              :         }
    1391              : #endif
    1392        36511 :         for (MSDriveWay* foe : it->second) {
    1393        32101 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1394              : #ifdef DEBUG_ADD_FOES
    1395              :                 if (gDebugFlag4) std::cout << "   foe=" << foe->myID
    1396              :                                                << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this)
    1397              :                                                << " cc1=" << crossingConflict(*foe) << " cc2=" << foe->crossingConflict(*this) << "\n";
    1398              : #endif
    1399         4757 :                 myFoes.push_back(foe);
    1400              :             } else {
    1401              : #ifdef DEBUG_ADD_FOES
    1402              :                 if (gDebugFlag4) {
    1403              :                     std::cout << "   cand=" << foe->myID << "\n";
    1404              :                 }
    1405              : #endif
    1406              :             }
    1407              :         }
    1408              :     }
    1409        11861 : }
    1410              : 
    1411              : 
    1412              : MSDriveWay*
    1413         9283 : MSDriveWay::buildDriveWay(const std::string& id, const MSLink* link, MSRouteIterator first, MSRouteIterator end) {
    1414              :     // collect lanes and links that are relevant for setting this signal for the current driveWay
    1415              :     // For each driveway we collect
    1416              :     //   - conflictLanes (signal must be red if any conflict lane is occupied)
    1417              :     //   - conflictLinks (signal must be red if any conflict link is approached by a vehicle
    1418              :     //      - that cannot break in time (arrivalSpeedBraking > 0)
    1419              :     //      - approached by a vehicle with higher switching priority (see #3941)
    1420              :     // These objects are construct in steps:
    1421              :     //
    1422              :     // forwardBlock
    1423              :     // - search forward recursive from outgoing lane until controlled railSignal link found
    1424              :     //   -> add all found lanes to conflictLanes
    1425              :     //
    1426              :     // bidiBlock (if any forwardBlock edge has bidi edge)
    1427              :     // - search bidi backward recursive until first switch
    1428              :     //   - from switch search backward recursive all other incoming until controlled rail signal link
    1429              :     //     -> add final links to conflictLinks
    1430              :     //
    1431              :     // flanks
    1432              :     // - search backward recursive from flanking switches
    1433              :     //   until controlled railSignal link or protecting switch is found
    1434              :     //   -> add all found lanes to conflictLanes
    1435              :     //   -> add final links to conflictLinks
    1436         9283 :     MSDriveWay* dw = new MSDriveWay(link, id);
    1437              :     LaneVisitedMap visited;
    1438              :     std::vector<const MSLane*> before;
    1439         9283 :     MSLane* fromBidi = nullptr;
    1440         9283 :     if (link != nullptr) {
    1441         5486 :         appendMapIndex(visited, link->getLaneBefore());
    1442         5486 :         fromBidi = link->getLaneBefore()->getBidiLane();
    1443              :     }
    1444              :     std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess> flankSwitches; // list of switches that threaten the driveway and for which protection must be found
    1445              : 
    1446         9283 :     if (fromBidi != nullptr) {
    1447         3049 :         before.push_back(fromBidi);
    1448              :     }
    1449              : #ifdef DEBUG_BUILD_DRIVEWAY
    1450              :     gDebugFlag4 = DEBUG_COND_DW(dw);
    1451              : #endif
    1452         9283 :     dw->buildRoute(link, first, end, visited, flankSwitches);
    1453         9283 :     dw->myCoreSize = (int)dw->myRoute.size();
    1454              : 
    1455         9283 :     MSRailSignal* rs = link ? const_cast<MSRailSignal*>(static_cast<const MSRailSignal*>(link->getTLLogic())) : nullptr;
    1456         9283 :     const bool movingBlock = (rs && rs->isMovingBlock()) || (!rs &&
    1457        13080 :                              (OptionsCont::getOptions().getBool("railsignal-moving-block")
    1458         3668 :                               || MSRailSignalControl::isMovingBlock((*first)->getPermissions())));
    1459              : 
    1460         9283 :     dw->checkFlanks(link, dw->myForward, visited, true, movingBlock, flankSwitches);
    1461         9283 :     dw->checkFlanks(link, dw->myBidi, visited, false, movingBlock, flankSwitches);
    1462         9283 :     dw->checkFlanks(link, before, visited, true, movingBlock, flankSwitches);
    1463        19494 :     for (MSLink* fsLink : flankSwitches) {
    1464              : #ifdef DEBUG_ADD_FOES
    1465              :         if (DEBUG_COND_DW(dw)) {
    1466              :             std::cout << " fsLink=" << fsLink->getDescription() << "\n";
    1467              :         }
    1468              : #endif
    1469        10211 :         dw->findFlankProtection(fsLink, fsLink, dw->myFlank);
    1470              :     }
    1471              :     std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess> flankSwitchesBidiExtended;
    1472         9283 :     dw->checkFlanks(link, dw->myBidiExtended, visited, false, movingBlock, flankSwitchesBidiExtended);
    1473        13612 :     for (MSLink* const flink : flankSwitchesBidiExtended) {
    1474              : #ifdef DEBUG_ADD_FOES
    1475              :         if (DEBUG_COND_DW(dw)) {
    1476              :             std::cout << " fsLinkExtended=" << flink->getDescription() << "\n";
    1477              :         }
    1478              : #endif
    1479         4329 :         dw->findFlankProtection(flink, flink, dw->myBidiExtended);
    1480              :     }
    1481              : #ifdef DEBUG_BUILD_DRIVEWAY
    1482              :     if (DEBUG_COND_DW(dw)) {
    1483              :         std::cout << SIMTIME << " buildDriveWay " << dw->myID << " link=" << (link == nullptr ? "NULL" : link->getDescription())
    1484              :                   << "\n    route=" << toString(dw->myRoute)
    1485              :                   << "\n    forward=" << toString(dw->myForward)
    1486              :                   << "\n    bidi=" << toString(dw->myBidi)
    1487              :                   << "\n    bidiEx=" << toString(dw->myBidiExtended)
    1488              :                   << "\n    flank=" << toString(dw->myFlank)
    1489              :                   << "\n    flankSwitch=" << MSRailSignal::describeLinks(std::vector<MSLink*>(flankSwitches.begin(), flankSwitches.end()))
    1490              :                   << "\n    coreSize=" << dw->myCoreSize
    1491              :                   << "\n";
    1492              :     }
    1493              : #endif
    1494         9283 :     if (!rs || !rs->isMovingBlock()) {
    1495         9172 :         dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myForward.begin(), dw->myForward.end());
    1496              :     }
    1497         9283 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myBidi.begin(), dw->myBidi.end());
    1498         9283 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myFlank.begin(), dw->myFlank.end());
    1499         9283 :     dw->addBidiFoes(rs, false);
    1500         9283 :     dw->addBidiFoes(rs, true);
    1501         9283 :     if (!movingBlock) {
    1502              :         // add driveways that start on the same signal / lane
    1503         8959 :         dw->addParallelFoes(link, *first);
    1504              :     }
    1505              :     // add driveways that reverse along this driveways route
    1506         9283 :     dw->addReversalFoes(movingBlock);
    1507              :     // make foes unique and symmetrical
    1508         9283 :     std::set<MSDriveWay*, ComparatorNumericalIdLess> uniqueFoes(dw->myFoes.begin(), dw->myFoes.end());
    1509              :     dw->myFoes.clear();
    1510              :     // check for self-intersecting forward-section in movingBlock mode
    1511         9283 :     if (movingBlock && uniqueFoes.count(dw) == 0) {
    1512              :         std::map<const MSJunction*, std::vector<const MSLink*> > forwardJunctions;
    1513          299 :         int iLast = (int)dw->myForward.size() - 1;
    1514              :         bool selfIntersect = false;
    1515         2971 :         for (int i = 0; i < iLast && !selfIntersect; i++) {
    1516         2672 :             const MSLane* fw = dw->myForward[i];
    1517         2672 :             if (fw->isNormal()) {
    1518         1290 :                 const MSJunction* fwTo = fw->getEdge().getToJunction();
    1519         1290 :                 const MSLink* fwLink = fw->getLinkTo(dw->myForward[i + 1]);
    1520         1290 :                 if (fwLink != nullptr) {
    1521         1301 :                     for (const MSLink* link2 : forwardJunctions[fwTo]) {
    1522              :                         const std::vector<MSLink*>& foeLinks = fwLink->getFoeLinks();
    1523              :                         const std::vector<MSLink*>& foeLinks2 = link2->getFoeLinks();
    1524           26 :                         if (std::find(foeLinks.begin(), foeLinks.end(), link2) != foeLinks.end()
    1525           32 :                                 || std::find(foeLinks2.begin(), foeLinks2.end(), fwLink) != foeLinks2.end()
    1526           16 :                                 || fwLink->getLane()->getBidiLane() == link2->getLaneBefore()
    1527           37 :                                 || link2->getLane()->getBidiLane() == fwLink->getLaneBefore()) {
    1528           15 :                             dw->myFoes.push_back(dw);
    1529              :                             selfIntersect = true;
    1530              : #ifdef DEBUG_ADD_FOES
    1531              :                             if (DEBUG_COND_DW(dw)) {
    1532              :                                 std::cout << " self-intersecting movingBlock for dw=" << dw->getID() << " at junction " << fwTo->getID() << " fwLink=" << fwLink->getDescription() << " link2=" << link2->getDescription() << "\n";
    1533              :                             }
    1534              : #endif
    1535              :                             break;
    1536              :                         }
    1537              :                     }
    1538         1290 :                     forwardJunctions[fwTo].push_back(fwLink);
    1539              :                 }
    1540              :             }
    1541              :         }
    1542              :     }
    1543         9283 :     std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess> uniqueCLink(dw->myConflictLinks.begin(), dw->myConflictLinks.end());
    1544         9283 :     const MSEdge* lastEdge = &dw->myForward.back()->getEdge();
    1545        16536 :     for (MSDriveWay* foe : uniqueFoes) {
    1546         7253 :         const MSEdge* foeLastEdge = &foe->myForward.back()->getEdge();
    1547         7253 :         const bool sameLast = foeLastEdge == lastEdge;
    1548         7253 :         if (sameLast && !movingBlock) {
    1549         2718 :             dw->myFoes.push_back(foe);
    1550         2718 :             if (foe != dw) {
    1551         2718 :                 foe->myFoes.push_back(dw);
    1552              :             }
    1553              :         } else {
    1554         4535 :             if (foe->bidiBlockedByEnd(*dw)) {
    1555              : #ifdef DEBUG_ADD_FOES
    1556              :                 if (DEBUG_COND_DW(dw)) {
    1557              :                     std::cout << " setting " << dw->getID() << " as foe of " << foe->getID() << "\n";
    1558              :                 }
    1559              : #endif
    1560         2193 :                 foe->myFoes.push_back(dw);
    1561         2193 :                 foe->addSidings(dw);
    1562              :             } else {
    1563         2342 :                 dw->buildSubFoe(foe, movingBlock);
    1564              :             }
    1565         4535 :             if (foe != dw) { // check for movingBlock
    1566         4510 :                 if (dw->bidiBlockedByEnd(*foe)) {
    1567              : #ifdef DEBUG_ADD_FOES
    1568              :                     if (DEBUG_COND_DW(dw)) {
    1569              :                         std::cout << " addFoeCheckSiding " << foe->getID() << "\n";
    1570              :                     }
    1571              : #endif
    1572         2425 :                     dw->myFoes.push_back(foe);
    1573         2425 :                     dw->addSidings(foe);
    1574              :                 } else  {
    1575         2085 :                     foe->buildSubFoe(dw, movingBlock);
    1576              :                 }
    1577              :             }
    1578              :         }
    1579         7253 :         if (link) {
    1580         5186 :             foe->addConflictLink(link);
    1581              :         }
    1582              :         // ignore links that have the same start junction
    1583         7253 :         if (foe->myRoute.front()->getFromJunction() != dw->myRoute.front()->getFromJunction()) {
    1584         8955 :             for (auto ili : foe->myForward.front()->getIncomingLanes()) {
    1585         4026 :                 if (ili.viaLink->getTLLogic() != nullptr) {
    1586              :                     // ignore links that originate on myBidi
    1587         3563 :                     const MSLane* origin = ili.viaLink->getLaneBefore();
    1588         3563 :                     if (std::find(dw->myBidi.begin(), dw->myBidi.end(), origin) == dw->myBidi.end()) {
    1589              :                         uniqueCLink.insert(ili.viaLink);
    1590              :                     }
    1591              :                 }
    1592              :             }
    1593              :         }
    1594              :     }
    1595              :     dw->myConflictLinks.clear();
    1596         9283 :     dw->myConflictLinks.insert(dw->myConflictLinks.begin(), uniqueCLink.begin(), uniqueCLink.end());
    1597         9283 :     myEndingDriveways[lastEdge].push_back(dw);
    1598         9283 :     if (!movingBlock) {
    1599              :         // every driveway is it's own foe (also all driveways that depart in the same block)
    1600        20929 :         for (MSDriveWay* sameEnd : myEndingDriveways[lastEdge]) {
    1601              :             if (uniqueFoes.count(sameEnd) == 0) {
    1602         9252 :                 dw->myFoes.push_back(sameEnd);
    1603         9252 :                 if (sameEnd != dw) {
    1604          293 :                     sameEnd->myFoes.push_back(dw);
    1605              :                 }
    1606              :             }
    1607              :         }
    1608              :     }
    1609              : #ifdef DEBUG_BUILD_DRIVEWAY
    1610              :     if (DEBUG_COND_DW(dw)) {
    1611              :         std::cout << dw->myID << " mb=" << movingBlock << " finalFoes " << toString(dw->myFoes) << "\n";
    1612              :     }
    1613              :     gDebugFlag4 = false;
    1614              : #endif
    1615         9283 :     return dw;
    1616         9283 : }
    1617              : 
    1618              : std::string
    1619         4499 : MSDriveWay::getTLLinkID(const MSLink* link) {
    1620         8998 :     return link->getTLLogic()->getID() + "_" + toString(link->getTLIndex());
    1621              : }
    1622              : 
    1623              : std::string
    1624            0 : MSDriveWay::getJunctionLinkID(const MSLink* link) {
    1625            0 :     return link->getJunction()->getID() + "_" + toString(link->getIndex());
    1626              : }
    1627              : 
    1628              : std::string
    1629         5486 : MSDriveWay::getClickableTLLinkID(const MSLink* link) {
    1630        16458 :     return "junction '" +  link->getTLLogic()->getID() + "', link " + toString(link->getTLIndex());
    1631              : }
    1632              : 
    1633              : std::string
    1634            0 : MSDriveWay::formatVisitedMap(const LaneVisitedMap& visited) {
    1635              :     UNUSED_PARAMETER(visited);
    1636              :     /*
    1637              :     std::vector<const MSLane*> lanes(visited.size(), nullptr);
    1638              :     for (auto item : visited) {
    1639              :         lanes[item.second] = item.first;
    1640              :     }
    1641              :     for (auto it = lanes.begin(); it != lanes.end();) {
    1642              :         if (*it == nullptr) {
    1643              :             it = lanes.erase(it);
    1644              :         } else {
    1645              :             it++;
    1646              :         }
    1647              :     }
    1648              :     return toString(lanes);
    1649              :     */
    1650            0 :     return "dummy";
    1651              : }
    1652              : 
    1653              : 
    1654              : void
    1655       135490 : MSDriveWay::appendMapIndex(LaneVisitedMap& map, const MSLane* lane) {
    1656              :     // avoid undefined behavior from evaluation order
    1657       135490 :     const int tmp = (int)map.size();
    1658       135490 :     map[lane] = tmp;
    1659       135490 : }
    1660              : 
    1661              : bool
    1662     17097585 : MSDriveWay::match(MSRouteIterator firstIt, MSRouteIterator endIt) const {
    1663              :     // @todo optimize: it is sufficient to check for specific edges (after each switch)
    1664     17097585 :     auto itRoute = firstIt;
    1665              :     auto itDwRoute = myRoute.begin();
    1666              :     bool match = true;
    1667    120502968 :     while (itRoute != endIt && itDwRoute != myRoute.end()) {
    1668    103603410 :         if (*itRoute != *itDwRoute) {
    1669              :             match = false;
    1670              : #ifdef DEBUG_MATCH
    1671              :             std::cout << "  check dw=" << getID() << " match failed at vehEdge=" << (*itRoute)->getID() << " dwEdge=" << (*itDwRoute)->getID() << "\n";
    1672              : #endif
    1673              :             break;
    1674              :         }
    1675              :         itRoute++;
    1676              :         itDwRoute++;
    1677              :     }
    1678              :     // if the vehicle arrives before the end of this driveway,
    1679              :     // we'd rather build a new driveway to avoid superfluous restrictions
    1680     16899558 :     if (match && itDwRoute == myRoute.end()
    1681     33968511 :             && (itRoute == endIt || myAbortedBuild || myBidiEnded || myFoundJump || isSubDriveWay())) {
    1682              :         //std::cout << "  using dw=" << "\n";
    1683     16856474 :         if (itRoute != endIt) {
    1684              :             // check whether the current route requires an extended driveway
    1685       244365 :             const MSEdge* next = *itRoute;
    1686       244365 :             const MSEdge* prev = myRoute.back();
    1687          425 :             if (myFoundJump && prev->getBidiEdge() != next && prev->getBidiEdge() != nullptr
    1688       244630 :                     && prev->isConnectedTo(*next, (SUMOVehicleClass)(SVC_RAIL_CLASSES & prev->getPermissions()))) {
    1689              : #ifdef DEBUG_MATCH
    1690              :                 std::cout << "  check dw=" << getID() << " prev=" << prev->getID() << " next=" << next->getID() << "\n";
    1691              : #endif
    1692              :                 return false;
    1693              :             }
    1694       244340 :             if (!myFoundJump && prev->getBidiEdge() == next && prev == &myForward.back()->getEdge()) {
    1695              :                 assert(isSubDriveWay() || myBidiEnded);
    1696              :                 // must not leave driveway via reversal
    1697              : #ifdef DEBUG_MATCH
    1698              :                 std::cout << getID() << " back=" << myForward.back()->getID() << " noMatch route " << toString(ConstMSEdgeVector(firstIt, endIt)) << "\n";
    1699              : #endif
    1700              :                 return false;
    1701              :             }
    1702       244334 :             if (myForward.back()->isInternal() && myForward.back()->getNextNormal() != (*itRoute)) {
    1703              :                 // driveway is part of a direct-control conflict and continues elsewhere
    1704              : #ifdef DEBUG_MATCH
    1705              :                 std::cout << getID() << " back=" << myForward.back()->getID() << " noMatch route " << toString(ConstMSEdgeVector(firstIt, itRoute)) << " (direct control)\n";
    1706              : #endif
    1707              :                 return false;
    1708              :             }
    1709              :         }
    1710     16856441 :         return !isSubDriveWay() || myParent->match(firstIt, endIt);
    1711              :     }
    1712              :     return false;
    1713              : }
    1714              : 
    1715              : void
    1716        12573 : MSDriveWay::addFoes(const MSLink* link) {
    1717              : #ifdef DEBUG_ADD_FOES
    1718              :     if (gDebugFlag4) {
    1719              :         std::cout << "driveway " << myID << " addFoes for link " << link->getDescription() << "\n";
    1720              :     }
    1721              : #endif
    1722        12573 :     const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(link->getTLLogic());
    1723        12573 :     if (rs != nullptr) {
    1724        17426 :         for (MSDriveWay* foe : rs->retrieveDriveWays(link->getTLIndex())) {
    1725              : #ifdef DEBUG_ADD_FOES
    1726              :             if (gDebugFlag4) {
    1727              :                 std::cout << "  cand foe=" << foe->myID << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this) << " cc1=" << crossingConflict(*foe) << " cc2=" <<  foe->crossingConflict(*this) << "\n";
    1728              :             }
    1729              : #endif
    1730         4853 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1731              : #ifdef DEBUG_ADD_FOES
    1732              :                 if (gDebugFlag4) {
    1733              :                     std::cout << "   foe=" << foe->myID << "\n";
    1734              :                 }
    1735              : #endif
    1736         3778 :                 myFoes.push_back(foe);
    1737              :             }
    1738        12573 :         }
    1739              :     }
    1740        12573 : }
    1741              : 
    1742              : 
    1743              : void
    1744        18566 : MSDriveWay::addBidiFoes(const MSRailSignal* ownSignal, bool extended) {
    1745              : #ifdef DEBUG_ADD_FOES
    1746              :     if (gDebugFlag4) {
    1747              :         std::cout << "driveway " << myID << " addBidiFoes extended=" << extended << "\n";
    1748              :     }
    1749              : #endif
    1750        18566 :     const std::vector<const MSLane*>& bidiLanes = extended ? myBidiExtended : myBidi;
    1751       102519 :     for (const MSLane* bidi : bidiLanes) {
    1752       171348 :         for (auto ili : bidi->getIncomingLanes()) {
    1753        87395 :             const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(ili.viaLink->getTLLogic());
    1754        87395 :             if (rs != nullptr && rs != ownSignal &&
    1755        87395 :                     std::find(bidiLanes.begin(), bidiLanes.end(), ili.lane) != bidiLanes.end()) {
    1756         4912 :                 addFoes(ili.viaLink);
    1757              :             }
    1758              :         }
    1759        83953 :         const MSEdge* bidiEdge = &bidi->getEdge();
    1760              :         if (myDepartureDriveways.count(bidiEdge) != 0) {
    1761         3734 :             for (MSDriveWay* foe : myDepartureDriveways[bidiEdge]) {
    1762         1678 :                 if (flankConflict(*foe)) {
    1763              : #ifdef DEBUG_ADD_FOES
    1764              :                     if (gDebugFlag4) {
    1765              :                         std::cout << "  foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << "\n";
    1766              :                     }
    1767              : #endif
    1768         1069 :                     myFoes.push_back(foe);
    1769              :                 } else {
    1770              : #ifdef DEBUG_ADD_FOES
    1771              :                     if (gDebugFlag4) {
    1772              :                         std::cout << "  cand foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << " rejected\n";
    1773              :                     }
    1774              : #endif
    1775              :                 }
    1776              :             }
    1777              :         }
    1778              :         if (myDepartureDrivewaysEnds.count(bidiEdge) != 0) {
    1779         3086 :             for (MSDriveWay* foe : myDepartureDrivewaysEnds[bidiEdge]) {
    1780         1589 :                 if (flankConflict(*foe)) {
    1781              : #ifdef DEBUG_ADD_FOES
    1782              :                     if (gDebugFlag4) {
    1783              :                         std::cout << "  foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << "\n";
    1784              :                     }
    1785              : #endif
    1786         1044 :                     myFoes.push_back(foe);
    1787              :                 } else {
    1788              : #ifdef DEBUG_ADD_FOES
    1789              :                     if (gDebugFlag4) {
    1790              :                         std::cout << "  cand foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << " rejected\n";
    1791              :                     }
    1792              : #endif
    1793              :                 }
    1794              :             }
    1795              :         }
    1796              :     }
    1797        18566 : }
    1798              : 
    1799              : 
    1800              : void
    1801         8959 : MSDriveWay::addParallelFoes(const MSLink* link, const MSEdge* first) {
    1802              : #ifdef DEBUG_ADD_FOES
    1803              :     if (gDebugFlag4) {
    1804              :         std::cout << "driveway " << myID << " addParallelFoes\n";
    1805              :     }
    1806              : #endif
    1807         8959 :     if (link) {
    1808         5375 :         addFoes(link);
    1809              :     } else {
    1810              :         auto it = myDepartureDriveways.find(first);
    1811         3584 :         if (it != myDepartureDriveways.end()) {
    1812         3808 :             for (MSDriveWay* foe : it->second) {
    1813              : #ifdef DEBUG_ADD_FOES
    1814              :                 if (gDebugFlag4) {
    1815              :                     std::cout << "  foe " << foe->getID() << " departs on first=" << first->getID() << "\n";
    1816              :                 }
    1817              : #endif
    1818          263 :                 myFoes.push_back(foe);
    1819              :             }
    1820              :         }
    1821              :     }
    1822         8959 : }
    1823              : 
    1824              : 
    1825              : void
    1826         9283 : MSDriveWay::addReversalFoes(bool movingBlock) {
    1827              : #ifdef DEBUG_ADD_FOES
    1828              :     if (gDebugFlag4) {
    1829              :         std::cout << "driveway " << myID << " addReversalFoes\n";
    1830              :     }
    1831              : #endif
    1832              :     std::set<const MSEdge*> forward;
    1833        46700 :     for (const MSLane* lane : myForward) {
    1834        37417 :         if (lane->isNormal()) {
    1835        21916 :             forward.insert(&lane->getEdge());
    1836              :         }
    1837              :     }
    1838              :     int i = 0;
    1839        79177 :     for (const MSEdge* e : myRoute) {
    1840        21932 :         if (forward.count(e) != 0 && !movingBlock) {
    1841              :             // reversals in our own forward can be ignored because each driveway
    1842              :             // is automatically a foe of itself by default
    1843              :             continue;
    1844              :         }
    1845        49666 :         if (i == myCoreSize) {
    1846              :             break;
    1847              :         }
    1848        49666 :         i++;
    1849              :         auto it = myReversalDriveWays.find(e);
    1850        49666 :         if (it != myReversalDriveWays.end()) {
    1851         6717 :             for (MSDriveWay* foe : it->second) {
    1852              :                 // check whether the foe reverses into our own forward section
    1853              :                 // (it might reverse again or disappear via arrival)
    1854              : #ifdef DEBUG_ADD_FOES
    1855              :                 //std::cout << "  candidate foe " << foe->getID() << " reverses on edge=" << e->getID() << " forward=" << joinNamedToString(forward, " ") << " foeRoute=" << toString(foe->myRoute) << "\n";
    1856              : #endif
    1857        11752 :                 if (forwardRouteConflict(forward, *foe)) {
    1858              :                     std::set<const MSEdge*> foeForward;
    1859         1795 :                     for (const MSLane* lane : foe->myForward) {
    1860         1557 :                         if (lane->isNormal()) {
    1861          801 :                             foeForward.insert(&lane->getEdge());
    1862          801 :                             if (lane->getBidiLane() != nullptr) {
    1863          790 :                                 foeForward.insert(lane->getEdge().getBidiEdge());
    1864              :                             }
    1865              :                         }
    1866              :                     }
    1867              : #ifdef DEBUG_ADD_FOES
    1868              :                     if (gDebugFlag4) {
    1869              :                         std::cout << "  reversal cand=" << foe->getID() << " foeForward " << toString(foeForward) << "\n";
    1870              :                     }
    1871              : #endif
    1872          476 :                     if (foe->forwardRouteConflict(foeForward, *this, true)) {
    1873              : #ifdef DEBUG_ADD_FOES
    1874              :                         if (gDebugFlag4) {
    1875              :                             std::cout << "  foe " << foe->getID() << " reverses on edge=" << e->getID() << "\n";
    1876              :                         }
    1877              : #endif
    1878          192 :                         myFoes.push_back(foe);
    1879              :                     }
    1880         5638 :                 } else if (movingBlock && foe == this) {
    1881              : #ifdef DEBUG_ADD_FOES
    1882              :                     if (gDebugFlag4) {
    1883              :                         std::cout << "  dw " << getID() << " reverses on forward edge=" << e->getID() << " (movingBlock)\n";
    1884              :                     }
    1885              : #endif
    1886           25 :                     myFoes.push_back(foe);
    1887              :                 }
    1888              :             }
    1889              :         }
    1890              :     }
    1891         9283 : }
    1892              : 
    1893              : 
    1894              : bool
    1895         4427 : MSDriveWay::buildSubFoe(MSDriveWay* foe, bool movingBlock) {
    1896              :     // Subdriveways (Teilfahrstraße) model the resolution of a driving conflict
    1897              :     // before a vehicle has left the driveway. This is possible when the driveway diverges from the foe
    1898              :     // driveway at an earlier point (switch or crossing).
    1899              :     //
    1900              :     // We already know that the last edge of this driveway doesn't impact the foe (unless the driveway ends within the block).
    1901              :     // Remove further edges from the end of the driveway (myForward) until the point of conflict is found.
    1902              :     //
    1903              :     // For movingBlock the logic is changed:
    1904              :     // We remove the conflict-free part as before but then keep removing the conflict part until another non-conconflit part is found
    1905         4427 :     if (myForward.size() < foe->myForward.size() &&
    1906         4427 :             myForward == std::vector<const MSLane*>(foe->myForward.begin(), foe->myForward.begin() + myForward.size())) {
    1907              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1908              :         if (gDebugFlag4) {
    1909              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " is subpart of foe=" << foe->getID() << "\n";
    1910              :         }
    1911              : #endif
    1912           83 :         foe->myFoes.push_back(this);
    1913           83 :         return true;
    1914              :     }
    1915         4344 :     int subLast = (int)myForward.size() - 2;
    1916         4344 :     if (movingBlock && myForward.back() == foe->myForward.back()) {
    1917           71 :         subLast++;
    1918              :     }
    1919              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1920              :     if (subLast < 0) {
    1921              :         if (gDebugFlag4) {
    1922              :             std::cout << "  " << getID() << " cannot build subDriveWay for foe " << foe->getID() << " because myForward has only a single lane\n";
    1923              :         }
    1924              :     }
    1925              : #endif
    1926              :     bool foundConflict = false;
    1927              :     bool flankC = false;
    1928              :     bool zipperC = false;
    1929              :     bool crossC = false;
    1930        15126 :     while (subLast >= 0) {
    1931        14960 :         const MSLane* lane = myForward[subLast];
    1932        14960 :         const MSLink* tmpOrigin = subLast > 0 ? myForward[subLast - 1]->getLinkTo(lane) : myOrigin;
    1933        14960 :         MSDriveWay tmp(tmpOrigin, "tmp", true);
    1934        14960 :         tmp.myForward.push_back(lane);
    1935        14960 :         tmp.myBidi = myBidi;
    1936        14960 :         tmp.myBidiExtended = myBidiExtended;
    1937        14960 :         tmp.myRoute.push_back(lane->getNextNormal());
    1938        14960 :         tmp.myCoreSize = 1;
    1939        14960 :         flankC = tmp.flankConflict(*foe);
    1940        14960 :         const bool bidiConflict = std::find(foe->myBidi.begin(), foe->myBidi.end(), lane) != foe->myBidi.end();
    1941        14960 :         crossC = tmp.crossingConflict(*foe);
    1942              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1943              :         if (gDebugFlag4) {
    1944              :             std::cout << "  subLast=" << subLast << " lane=" << lane->getID() << " fc=" << flankC << " cc=" << crossC << " bc=" << bidiConflict << "\n";
    1945              :         }
    1946              : #endif
    1947        14960 :         if (flankC || crossC || bidiConflict) {
    1948              :             foundConflict = true;
    1949         4486 :             if (!movingBlock || bidiConflict) {
    1950              :                 break;
    1951              :             }
    1952          358 :             if (((flankC && lane->getFromJunction()->getType() == SumoXMLNodeType::ZIPPER)
    1953           46 :                     || (!flankC && lane->getToJunction()->getType() == SumoXMLNodeType::ZIPPER))
    1954          431 :                     && (isDepartDriveway()
    1955           15 :                         || getForwardDistance(flankC ? subLast - 1 : subLast) > myMovingBlockMaxDist)) {
    1956              :                 zipperC = true;
    1957              :                 foundConflict = false;
    1958              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1959              :                 if (gDebugFlag4) {
    1960              :                     std::cout << "     ignored movingBlock zipperConflict\n";
    1961              :                 }
    1962              : #endif
    1963           18 :                 if (!flankC && crossC) {
    1964              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1965              :                     if (gDebugFlag4) {
    1966              :                         std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " movingBlock-save\n";
    1967              :                     }
    1968              : #endif
    1969              :                     return false;
    1970              :                 }
    1971              :             }
    1972          395 :             if (!flankC && crossC) {
    1973              :                 break;
    1974              :             }
    1975        10474 :         } else if (foundConflict) {
    1976              :             break;
    1977              :         }
    1978        10782 :         subLast--;
    1979        14960 :     }
    1980              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1981              :     if (gDebugFlag4) {
    1982              :         std::cout << "  subLastFinal=" << subLast << " movingBlock=" << movingBlock << " zipperC=" << zipperC << "\n";
    1983              :     }
    1984              : #endif
    1985         4335 :     if (bidiBlockedByEnd(*foe) && bidiBlockedBy(*this) && foe->forwardEndOnRoute(this)) {
    1986              :         //std::set<const MSEdge*> firstEdge;
    1987              :         //firstEdge.insert(foe->myRoute.front());
    1988              : 
    1989          248 :         ConstMSEdgeVector forward(myRoute.begin(), myRoute.begin() + myForwardEdgeCount);
    1990              :         ConstMSEdgeVector foeAfterForward(foe->myRoute.begin() + foe->myForwardEdgeCount,
    1991          248 :                                           foe->myRoute.begin() + MIN2(foe->myRoute.size(), foe->myForwardEdgeCount + forward.size()));
    1992              :         // @todo the check for forwardRouteConflict correctly reduces waiting in
    1993              :         // test rail/reversal/consecutive_before_reversal but creates deadlock in
    1994              :         // test rail/reversal/reversal_onRoute_beyond_core3b
    1995          248 :         if (forward == foeAfterForward /*&& forwardRouteConflict(firstEdge, *this, true)*/) {
    1996          126 :             foe->myFoes.push_back(this);
    1997              :             // foe will get the sidings
    1998          126 :             addSidings(foe, true);
    1999              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2000              :             if (gDebugFlag4) {
    2001              :                 std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " special case 1\n";
    2002              :             }
    2003              : #endif
    2004              :             return true;
    2005              :         }
    2006          248 :     }
    2007         4209 :     if (subLast < 0) {
    2008          136 :         if (movingBlock && zipperC) {
    2009              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2010              :             if (gDebugFlag4) {
    2011              :                 std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " movingBlock-save\n";
    2012              :             }
    2013              : #endif
    2014              :             return false;
    2015          136 :         } else if (&myForward.back()->getEdge() == myRoute.back() && foe->forwardEndOnRoute(this)) {
    2016              :             // driveway ends in the middle of the block and only the final edge overlaps with the foe driveWay
    2017           15 :             foe->myFoes.push_back(this);
    2018              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2019              :             if (gDebugFlag4) {
    2020              :                 std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " foe endsOnForward\n";
    2021              :             }
    2022              : #endif
    2023          121 :         } else if (foe->myTerminateRoute) {
    2024          121 :             if (bidiBlockedByEnd(*foe) && bidiBlockedBy(*this) && foe->forwardEndOnRoute(this)) {
    2025            5 :                 foe->myFoes.push_back(this);
    2026              :                 // foe will get the sidings
    2027            5 :                 addSidings(foe, true);
    2028              :             }
    2029              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2030              :             if (gDebugFlag4) {
    2031              :                 std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " terminates\n";
    2032              :             }
    2033              : #endif
    2034            0 :         } else if (myTerminateRoute && myBidi.size() <= myForward.size()) {
    2035            0 :             foe->myFoes.push_back(this);
    2036              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2037              :             if (gDebugFlag4) {
    2038              :                 std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " terminates, foe=" << foe->getID() << "\n";
    2039              :             }
    2040              : #endif
    2041            0 :             return true;
    2042              :         } else if (foe->myReversals.size() % 2 == 1) {
    2043              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2044              :             if (gDebugFlag4) {
    2045              :                 std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " has " << foe->myReversals.size() << " reversals\n";
    2046              :             }
    2047              : #endif
    2048              :         } else {
    2049              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2050              :             if (gDebugFlag4) {
    2051              :                 std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " failed\n";
    2052              :             }
    2053              : #endif
    2054              : #ifdef SUBDRIVEWAY_WARN_NOCONFLICT
    2055              :             WRITE_WARNINGF("No point of conflict found between driveway '%' and driveway '%' when creating sub-driveway", getID(), foe->getID());
    2056              : #endif
    2057              :         }
    2058          136 :         return false;
    2059              :     }
    2060         4073 :     int subSize = subLast + 1;
    2061         5003 :     for (MSDriveWay* cand : mySubDriveWays) {
    2062         2407 :         if ((int)cand->myForward.size() == subSize) {
    2063              :             // can re-use existing sub-driveway
    2064         1477 :             foe->myFoes.push_back(cand);
    2065         1477 :             if (foe->bidiBlockedByEnd(*cand)) {
    2066         1343 :                 foe->addSidings(cand);
    2067              :             }
    2068         1477 :             cand->myFoes.push_back(foe);
    2069              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2070              :             if (gDebugFlag4) {
    2071              :                 std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " useExisting=" << cand->getID() << "\n";
    2072              :             }
    2073              : #endif
    2074         1477 :             return true;
    2075              :         }
    2076              :     }
    2077         2596 :     std::vector<const MSLane*> forward(myForward.begin(), myForward.begin() + subSize);
    2078              :     std::vector<const MSEdge*> route;
    2079        14857 :     for (const MSLane* lane : forward) {
    2080        12261 :         if (lane->isNormal()) {
    2081         6095 :             route.push_back(&lane->getEdge());
    2082              :         }
    2083              :     }
    2084         2596 :     if (route.empty()) {
    2085           59 :         if (subSize == 1 && crossC
    2086           56 :                 && forward.front()->getFromJunction() == foe->myForward.front()->getFromJunction()
    2087          115 :                 && forward.front()->getFromJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    2088              :             assert(myForward.front()->isInternal());
    2089              :             // sub-driveway ends after a single internal lane but since the route cannot be empty we add the next edge
    2090           56 :             route.push_back(foe->myForward.front()->getEdge().getNormalSuccessor());
    2091              :         }  else {
    2092              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2093              :             if (gDebugFlag4) {
    2094              :                 std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID() << " empty subRoute\n";
    2095              :             }
    2096              : #endif
    2097            3 :             return false;
    2098              :         }
    2099              :     }
    2100         2593 :     if (myRoute.size() > route.size()) {
    2101              :         // route continues. make sure the subDriveway does not end with a reversal
    2102         2581 :         const MSEdge* lastNormal = route.back();
    2103         2581 :         const MSEdge* nextNormal = myRoute[route.size()];
    2104         2581 :         if (lastNormal->getBidiEdge() == nextNormal) {
    2105              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2106              :             if (gDebugFlag4) std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID()
    2107              :                                            << " lastNormal=" << lastNormal->getID() << " nextNormal=" << nextNormal->getID() << " endWithReversal\n";
    2108              : #endif
    2109              :             return false;
    2110              :         }
    2111              :     }
    2112         5146 :     MSDriveWay* sub = new MSDriveWay(myOrigin, getID() + "." + toString(mySubDriveWays.size()));
    2113         2573 :     sub->myLane = myLane;
    2114         2573 :     sub->myParent = this;
    2115         2573 :     sub->myForward = forward;
    2116         2573 :     sub->myRoute = route;
    2117         2573 :     sub->myCoreSize = (int)sub->myRoute.size();
    2118         2573 :     myLane->addMoveReminder(sub, false);
    2119              : 
    2120              :     // copy trains that are currently on this driveway (and associated entry events)
    2121         2745 :     for (SUMOVehicle* veh : myTrains) {
    2122          172 :         auto itOnSub = std::find(sub->myRoute.begin(), sub->myRoute.end(), veh->getEdge());
    2123          172 :         if (itOnSub != sub->myRoute.end()) {
    2124              :             sub->myTrains.insert(veh);
    2125              :             // non-zero is enough to avoid superfluous activation via activateReminders (and removal)
    2126          148 :             const double pos = sub->myRoute.front()->getLength();
    2127          148 :             dynamic_cast<MSBaseVehicle*>(veh)->addReminder(sub, pos);
    2128          169 :             for (const VehicleEvent& ve : myVehicleEvents) {
    2129           21 :                 if (ve.id == veh->getID()) {
    2130           21 :                     sub->myVehicleEvents.push_back(ve);
    2131              :                 }
    2132              :             }
    2133              :         }
    2134              :     }
    2135              : 
    2136         2573 :     foe->myFoes.push_back(sub);
    2137         2573 :     if (foe->bidiBlockedByEnd(*sub)) {
    2138         1765 :         foe->addSidings(sub);
    2139              :     }
    2140         2573 :     sub->myFoes.push_back(foe);
    2141         2573 :     mySubDriveWays.push_back(sub);
    2142              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2143              :     if (gDebugFlag4) {
    2144              :         std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " sub=" << sub->getID() << " route=" << toString(sub->myRoute) << "\n";
    2145              :     }
    2146              : #endif
    2147              :     return true;
    2148         2596 : }
    2149              : 
    2150              : 
    2151              : double
    2152           15 : MSDriveWay::getForwardDistance(int lastIndex) const {
    2153              :     assert(lastIndex < (int)myForward.size());
    2154              :     double result = 0;
    2155           57 :     for (int i = 0; i <= lastIndex; i++) {
    2156           42 :         result += myForward[i]->getLength();
    2157              :     }
    2158           15 :     return result;
    2159              : }
    2160              : 
    2161              : 
    2162              : void
    2163         7857 : MSDriveWay::addSidings(MSDriveWay* foe, bool addToFoe) {
    2164         7857 :     const ConstMSEdgeVector& foeRoute = foe->isSubDriveWay() ? foe->myParent->myRoute : foe->myRoute;
    2165         7857 :     const MSEdge* foeEndBidi = foe->myForward.back()->getEdge().getBidiEdge();
    2166              :     int foeForwardNormals = 0;
    2167        41469 :     for (auto lane : foe->myForward) {
    2168        33612 :         if (lane->isNormal()) {
    2169        17978 :             foeForwardNormals++;
    2170              :         }
    2171              :     }
    2172         7857 :     if (foeForwardNormals == (int)foeRoute.size()) {
    2173              : #ifdef DEBUG_BUILD_SIDINGS
    2174              :         if (gDebugFlag4) {
    2175              :             std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " foeForwardNormals=" << foeForwardNormals << " frSize=" << foeRoute.size() <<  " aborted\n";
    2176              :         }
    2177              : #endif
    2178         1027 :         return;
    2179              :     }
    2180              :     auto foeSearchBeg = foeRoute.begin() + foeForwardNormals;
    2181              :     auto foeSearchEnd = foeRoute.end();
    2182         6980 :     if (foeEndBidi == nullptr) {
    2183            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " noBidi\n");
    2184              :     }
    2185              :     // if foe is a subDriveway, the forward section may end on an internal edge which would not be found on myRoute
    2186         6980 :     foeEndBidi = foeEndBidi->getNormalSuccessor();
    2187         6980 :     std::set<const MSEdge*> foeForwardEdges(foeRoute.begin(), foeRoute.begin() + foeForwardNormals);
    2188              :     int forwardNormals = 0;
    2189        48024 :     for (auto lane : myForward) {
    2190        41194 :         if (lane->isNormal()) {
    2191        22087 :             forwardNormals++;
    2192              :             if (foeForwardEdges.count(&lane->getEdge()) != 0) {
    2193              : #ifdef DEBUG_BUILD_SIDINGS
    2194              :                 if (gDebugFlag4) {
    2195              :                     std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " forwardEdge=" << lane->getEdge().getID() << " on foeForward (sidings unsafe)\n";
    2196              :                 }
    2197              : #endif
    2198              :                 return;
    2199              :             }
    2200              :         }
    2201              :     }
    2202              :     int i;
    2203              :     std::vector<int> start;
    2204              :     std::vector<double> length;
    2205              :     std::vector<std::vector<int> > intermediateRS;
    2206        40488 :     for (i = 0; i < (int)myRoute.size(); i++) {
    2207        40488 :         if (myRoute[i] == foeEndBidi) {
    2208              :             break;
    2209              :         }
    2210              :     }
    2211         6830 :     if (i == (int)myRoute.size()) {
    2212            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " foeEndBidi=" + foeEndBidi->getID() + " not on route\n");
    2213              :     }
    2214         6830 :     const MSEdge* next = myRoute[i];
    2215              : #ifdef DEBUG_BUILD_SIDINGS
    2216              :     if (gDebugFlag4) {
    2217              :         std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " next=" << next->getID() << " foeForwardNormals=" << foeForwardNormals << " frSize=" << foeRoute.size() << " foeSearchBeg=" << (*foeSearchBeg)->getID() << "\n";
    2218              :     }
    2219              : #endif
    2220         6830 :     i--;
    2221              :     // look backward along our route starting at the final edge of the foe dw
    2222        40488 :     for (; i >= 0; i--) {
    2223        33658 :         const MSEdge* cur = myRoute[i];
    2224        33658 :         const bool curHasRS = hasRS(cur, next);
    2225        33658 :         if (curHasRS) {
    2226        11495 :             if (std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge()) == foeSearchEnd) {
    2227              :                 // we found a rail signal in a safe spot (not on the foe route)
    2228         7923 :                 start.push_back(i);
    2229         7923 :                 length.push_back(0);
    2230         7923 :                 intermediateRS.push_back({});
    2231              :             }
    2232              :         }
    2233        33658 :         if (!start.empty()) {
    2234              :             // move further backwards along the route to find the spot where it merges again with the foe route
    2235        12718 :             auto itFind = std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge());
    2236        12718 :             if (itFind != foeSearchEnd) {
    2237              : #ifdef DEBUG_BUILD_SIDINGS
    2238              :                 if (gDebugFlag4) {
    2239              :                     std::cout << "endSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " curBidi=" << Named::getIDSecure(cur->getBidiEdge()) << " length=" << toString(length) << "\n";
    2240              :                 }
    2241              : #endif
    2242         2144 :                 const int firstIndex = i + 1;
    2243         2144 :                 if (addToFoe) {
    2244          131 :                     auto& foeSidings = foe->mySidings[this];
    2245              :                     // indices must be mapped onto foe route;
    2246          131 :                     const MSEdge* first = myRoute[firstIndex];
    2247          131 :                     auto itFirst = std::find(foeRoute.begin(), foeRoute.end(), first);
    2248          131 :                     if (itFirst != foeRoute.end()) {
    2249          473 :                         for (int j = 0; j < (int)length.size(); j++) {
    2250          342 :                             const MSEdge* last = myRoute[start[j]];
    2251          342 :                             auto itLast = std::find(itFirst, foeRoute.end(), last);
    2252          342 :                             if (itLast != foeRoute.end()) {
    2253              :                                 // @todo are intermediateRS relevant here?
    2254          684 :                                 foeSidings.insert(foeSidings.begin(), Siding((int)(itFirst - foeRoute.begin()), (int)(itLast - foeRoute.begin()), length[j], {}));
    2255          342 :                                 if (foeSidings.size() > 2) {
    2256              :                                     foeSidings.pop_back();  // only need the first 2 sidings
    2257              :                                 }
    2258              :                             }
    2259              :                         }
    2260              :                     }
    2261              :                 } else {
    2262              :                     // pick up further rail signals between the start of the siding and the start of the current driveway
    2263              :                     std::vector<int> furtherRS;
    2264         2013 :                     if (curHasRS) {
    2265          128 :                         furtherRS.push_back(i);
    2266              :                     }
    2267              :                     const MSEdge* next2 = cur;
    2268         5670 :                     for (int i2 = i - 1; i2 >= forwardNormals; i2--) {
    2269         3657 :                         const MSEdge* cur2 = myRoute[i2];
    2270         3657 :                         if (hasRS(cur2, next2)) {
    2271         1711 :                             furtherRS.push_back(i2);
    2272              :                         }
    2273              :                         next2 = cur2;
    2274              :                     }
    2275         2013 :                     auto& foeSidings = mySidings[foe];
    2276         8896 :                     for (int j = 0; j < (int)length.size(); j++) {
    2277         6883 :                         intermediateRS[j].insert(intermediateRS[j].end(), furtherRS.begin(), furtherRS.end());
    2278        13766 :                         foeSidings.insert(foeSidings.begin(), Siding(firstIndex, start[j], length[j], intermediateRS[j]));
    2279         6883 :                         if (foeSidings.size() > 2) {
    2280              :                             foeSidings.pop_back();  // only need the first 2 sidings
    2281              :                         }
    2282              :                     }
    2283         2013 :                 }
    2284              :                 start.clear();
    2285              :                 length.clear();
    2286              :                 intermediateRS.clear();
    2287         2144 :                 foeSearchBeg = itFind;
    2288              :             } else {
    2289        75447 :                 for (int j = 0; j < (int)length.size(); j++) {
    2290        64873 :                     length[j] += cur->getLength();
    2291        64873 :                     if (curHasRS && i != start[j]) {
    2292        45431 :                         intermediateRS[j].push_back(i);
    2293              :                     }
    2294              :                 }
    2295              :             }
    2296              :         }
    2297              :         next = cur;
    2298              :     }
    2299         6830 : }
    2300              : 
    2301              : 
    2302              : bool
    2303        37315 : MSDriveWay::hasRS(const MSEdge* cur, const MSEdge* next) {
    2304        37315 :     if (cur->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    2305              :         // check if there is a controlled link between cur and next
    2306        25288 :         for (auto lane : cur->getLanes()) {
    2307        25699 :             for (const MSLink* link : lane->getLinkCont()) {
    2308        19658 :                 if (&link->getLane()->getEdge() == next && link->getTLLogic() != nullptr) {
    2309              :                     return true;
    2310              :                 }
    2311              :             }
    2312              :         }
    2313              :     }
    2314              :     return false;
    2315              : }
    2316              : 
    2317              : 
    2318              : bool
    2319          372 : MSDriveWay::forwardEndOnRoute(const MSDriveWay* foe) const {
    2320          372 :     const MSEdge* foeForwardEnd = &foe->myForward.back()->getNormalPredecessorLane()->getEdge();
    2321          372 :     return std::find(myRoute.begin(), myRoute.end(), foeForwardEnd) != myRoute.end();
    2322              : }
    2323              : 
    2324              : void
    2325         5186 : MSDriveWay::addConflictLink(const MSLink* link) {
    2326         5186 :     if (link->getTLLogic() != nullptr) {
    2327              :         // ignore links that originate on myBidi
    2328              :         // and also links from the same junction as my own link
    2329         5186 :         const MSLane* origin = link->getLaneBefore();
    2330         5186 :         if (std::find(myBidi.begin(), myBidi.end(), origin) == myBidi.end()) {
    2331         4158 :             if (link->getJunction() != myRoute.front()->getFromJunction()) {
    2332         2628 :                 if (std::find(myConflictLinks.begin(), myConflictLinks.end(), link) == myConflictLinks.end()) {
    2333         1869 :                     myConflictLinks.push_back(const_cast<MSLink*>(link));
    2334              :                 }
    2335              :             }
    2336              :         }
    2337              :     }
    2338         5186 : }
    2339              : 
    2340              : void
    2341          348 : MSDriveWay::addDWDeadlock(const std::vector<const MSDriveWay*>& deadlockFoes) {
    2342              :     std::set<const MSDriveWay*> filtered;
    2343         1737 :     for (const MSDriveWay* foe : deadlockFoes) {
    2344         1389 :         if (std::find(myFoes.begin(), myFoes.end(), foe) == myFoes.end()) {
    2345              :             filtered.insert(foe);
    2346              :         }
    2347              :     }
    2348          348 :     if (std::find(myDeadlocks.begin(), myDeadlocks.end(), filtered) == myDeadlocks.end()) {
    2349           93 :         myDeadlocks.push_back(filtered);
    2350              :         //std::cout << getID() << " deadlockFoes=" << toString(deadlockFoes) << "\n";
    2351              :     }
    2352          348 : }
    2353              : 
    2354              : const MSDriveWay*
    2355      8041317 : MSDriveWay::getDepartureDriveway(const SUMOVehicle* veh, bool init) {
    2356      8041317 :     const MSEdge* edge = init ? veh->getRoute().getEdges()[veh->getDepartEdge()] : veh->getEdge();
    2357              :     assert(isRailwayOrShared(edge->getPermissions()));
    2358      8041317 :     if (edge->getFromJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    2359         4467 :         for (const MSLane* lane : edge->getLanes()) {
    2360         4816 :             for (auto ili : lane->getIncomingLanes()) {
    2361         2818 :                 const MSLink* entry = ili.viaLink->getCorrespondingEntryLink();
    2362         2818 :                 const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(entry->getTLLogic());
    2363         1887 :                 if (rs != nullptr) {
    2364         1887 :                     const MSDriveWay* dw = &const_cast<MSRailSignal*>(rs)->retrieveDriveWayForVeh(entry->getTLIndex(), veh);
    2365         1887 :                     if (&dw->myForward.front()->getEdge() == edge) {
    2366              :                         return dw;
    2367              :                     }
    2368              :                 }
    2369              :             }
    2370              :         }
    2371              :     }
    2372      8045198 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    2373      8041401 :         auto matchStart = init ? veh->getRoute().begin() + veh->getDepartEdge() : veh->getCurrentRouteEdge();
    2374      8041401 :         if (dw->match(matchStart, veh->getRoute().end())) {
    2375      8037049 :             return dw;
    2376              :         }
    2377              :     }
    2378         7594 :     const std::string id = edge->getFromJunction()->getID() + ".d" + toString(myDepartDrivewayIndex[edge->getFromJunction()]++);
    2379         3797 :     MSDriveWay* dw = buildDriveWay(id, nullptr, veh->getCurrentRouteEdge(), veh->getRoute().end());
    2380         3797 :     myDepartureDriveways[edge].push_back(dw);
    2381         3797 :     myDepartureDrivewaysEnds[&dw->myForward.back()->getEdge()].push_back(dw);
    2382              :     dw->setVehicle(veh->getID());
    2383              :     return dw;
    2384              : }
    2385              : 
    2386              : 
    2387              : void
    2388         1547 : MSDriveWay::writeDepatureBlocks(OutputDevice& od, bool writeVehicles) {
    2389         4169 :     for (auto item  : myDepartureDriveways) {
    2390         2622 :         const MSEdge* edge = item.first;
    2391         2622 :         if (item.second.size() > 0) {
    2392         5244 :             od.openTag("departJunction");
    2393         2622 :             od.writeAttr(SUMO_ATTR_ID, edge->getFromJunction()->getID());
    2394         5549 :             for (const MSDriveWay* dw : item.second) {
    2395         2927 :                 if (writeVehicles) {
    2396          421 :                     dw->writeBlockVehicles(od);
    2397              :                 } else {
    2398         2506 :                     dw->writeBlocks(od);
    2399              :                 }
    2400              :             }
    2401         5244 :             od.closeTag(); // departJunction
    2402              :         }
    2403              :     }
    2404         1547 : }
    2405              : 
    2406              : void
    2407          538 : MSDriveWay::saveState(OutputDevice& out) {
    2408              :     // all driveways are in myEndingDriveways which makes it convenient
    2409          616 :     for (auto item : myEndingDriveways) {
    2410          172 :         for (MSDriveWay* dw : item.second) {
    2411           94 :             dw->_saveState(out);
    2412          112 :             for (MSDriveWay* sub : dw->mySubDriveWays) {
    2413           18 :                 sub->_saveState(out);
    2414              :             }
    2415              :         }
    2416              :     }
    2417          538 : }
    2418              : 
    2419              : void
    2420          112 : MSDriveWay::_saveState(OutputDevice& out) const {
    2421          112 :     if (!myTrains.empty() || haveSubTrains()) {
    2422           83 :         out.openTag(isSubDriveWay() ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
    2423           43 :         out.writeAttr(SUMO_ATTR_ID, getID());
    2424           43 :         out.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
    2425           43 :         if (!myTrains.empty()) {
    2426              :             std::vector<std::string> trainIDs;
    2427           86 :             for (SUMOVehicle* veh : myTrains) {
    2428           43 :                 trainIDs.push_back(veh->getID());
    2429              :             }
    2430           43 :             out.writeAttr(SUMO_ATTR_VEHICLES, toString(trainIDs));
    2431           43 :         }
    2432           86 :         out.closeTag();
    2433              :     }
    2434          112 : }
    2435              : 
    2436              : 
    2437              : bool
    2438           69 : MSDriveWay::haveSubTrains() const {
    2439           76 :     for (MSDriveWay* sub : mySubDriveWays) {
    2440            7 :         if (!sub->myTrains.empty()) {
    2441              :             return true;
    2442              :         }
    2443              :     }
    2444              :     return false;
    2445              : }
    2446              : 
    2447              : void
    2448           43 : MSDriveWay::loadState(const SUMOSAXAttributes& attrs, int tag) {
    2449           43 :     if ((int)myDriveWayRouteLookup.size() < myGlobalDriveWayIndex) {
    2450          106 :         for (auto item : myEndingDriveways) {
    2451          164 :             for (MSDriveWay* dw : item.second) {
    2452           84 :                 myDriveWayRouteLookup[dw->myRoute] = dw;
    2453              :             }
    2454              :         }
    2455              :     }
    2456           43 :     MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
    2457              :     bool ok;
    2458           43 :     const std::string id  = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    2459           43 :     const std::string edges = attrs.get<std::string>(SUMO_ATTR_EDGES, id.c_str(), ok);
    2460              :     ConstMSEdgeVector route;
    2461           43 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
    2462           43 :         MSEdge::parseEdgesList(edges, route, id);
    2463              :     }
    2464              :     // missing driveways and subdriveways can be ignored. They may have been created
    2465              :     // for vehicles that are not relevant at state loading time
    2466              :     MSDriveWay* dw = nullptr;
    2467           43 :     if (tag == SUMO_TAG_DRIVEWAY) {
    2468              :         auto it = myDriveWayRouteLookup.find(route);
    2469           40 :         if (it == myDriveWayRouteLookup.end()) {
    2470              :             //WRITE_WARNING(TLF("Unknown driveWay '%' with route '%'", id, edges));
    2471              :             return;
    2472              :         }
    2473           38 :         dw = it->second;
    2474           38 :         myDriveWayLookup[id] = dw;
    2475              :     } else {
    2476            3 :         std::string parentID = id.substr(0, id.rfind('.'));
    2477              :         auto it = myDriveWayLookup.find(parentID);
    2478            3 :         if (it == myDriveWayLookup.end()) {
    2479              :             //WRITE_WARNING(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
    2480              :             return;
    2481              :         }
    2482            3 :         MSDriveWay* parent = it->second;
    2483            3 :         for (MSDriveWay* sub : parent->mySubDriveWays) {
    2484            1 :             if (sub->myRoute == route) {
    2485              :                 dw = sub;
    2486              :                 break;
    2487              :             }
    2488              :         }
    2489            3 :         if (dw == nullptr) {
    2490              :             // missing subdriveways can be ignored. They may have been created
    2491              :             // as foes for driveways that are not relevant at state loading time
    2492              :             return;
    2493              :         }
    2494              :     }
    2495           78 :     const std::string vehicles = attrs.getOpt<std::string>(SUMO_ATTR_VEHICLES, id.c_str(), ok, "");
    2496          117 :     for (const std::string& vehID : StringTokenizer(vehicles).getVector()) {
    2497           39 :         MSBaseVehicle* veh = dynamic_cast<MSBaseVehicle*>(c.getVehicle(vehID));
    2498           39 :         if (veh == nullptr) {
    2499            0 :             throw ProcessError(TLF("Unknown vehicle '%' in driveway '%'", vehID, id));
    2500              :         }
    2501           39 :         if (!dw->hasTrain(veh)) {
    2502            4 :             dw->myTrains.insert(veh);
    2503            4 :             veh->addReminder(dw);
    2504              :         }
    2505           39 :     }
    2506           43 : }
    2507              : 
    2508              : const MSDriveWay*
    2509            0 : MSDriveWay::retrieveDepartDriveWay(const MSEdge* edge, const std::string& id) {
    2510            0 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    2511            0 :         if (dw->getID() == id) {
    2512              :             return dw;
    2513              :         }
    2514              :     }
    2515              :     return nullptr;
    2516              : }
    2517              : 
    2518              : 
    2519              : bool
    2520         1563 : MSDriveWay::hasTrain(SUMOVehicle* veh) const {
    2521         1563 :     return myTrains.count(veh) != 0;
    2522              : }
    2523              : 
    2524              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1