LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSDriveWay.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 96.7 % 946 915
Test Date: 2026-05-24 16:29:35 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        42366 : MSDriveWay::init() {
      81        42366 :     myWriteVehicles = OptionsCont::getOptions().isSet("railsignal-vehicle-output");
      82        42366 :     myMovingBlockMaxDist = OptionsCont::getOptions().getFloat("railsignal.moving-block.max-dist");
      83        42366 : }
      84              : 
      85              : // ===========================================================================
      86              : // MSDriveWay method definitions
      87              : // ===========================================================================
      88              : 
      89              : 
      90        26788 : MSDriveWay::MSDriveWay(const MSLink* origin, const std::string& id, bool temporary) :
      91        53576 :     MSMoveReminder("DriveWay_" + (temporary ? "tmp" : id)),
      92              :     Named(id),
      93        11776 :     myNumericalID(temporary ? -1 : myGlobalDriveWayIndex++),
      94        26788 :     myOrigin(origin),
      95        26788 :     myActive(nullptr),
      96        26788 :     myCoreSize(0),
      97        26788 :     myForwardEdgeCount(0),
      98        26788 :     myFoundSignal(false),
      99        26788 :     myFoundJump(false),
     100        26788 :     myTerminateRoute(false),
     101        26788 :     myAbortedBuild(false),
     102        26788 :     myBidiEnded(false),
     103        92140 :     myParent(nullptr)
     104        26788 : {}
     105              : 
     106              : 
     107        38564 : MSDriveWay::~MSDriveWay() {
     108        29358 :     for (const MSDriveWay* sub : mySubDriveWays) {
     109         2570 :         delete sub;
     110              :     }
     111              :     mySubDriveWays.clear();
     112       118928 : }
     113              : 
     114              : void
     115        42031 : MSDriveWay::cleanup() {
     116        42031 :     myGlobalDriveWayIndex = 0;
     117              :     myBlockLengthWarnings.clear();
     118        42031 :     myWriteVehicles = false;
     119              : 
     120        45496 :     for (auto item : myDepartureDriveways) {
     121         7241 :         for (MSDriveWay* dw : item.second) {
     122         3776 :             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        42031 : }
     132              : 
     133              : void
     134          168 : MSDriveWay::clearState() {
     135          193 :     for (auto item : myEndingDriveways) {
     136           50 :         for (MSDriveWay* dw : item.second) {
     137              :             dw->myTrains.clear();
     138              :         }
     139              :     }
     140          168 : }
     141              : 
     142              : 
     143              : bool
     144        35831 : 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        35831 :     if (veh.isVehicle() && MSRailSignalControl::isUsingDriveWays(veh.getVClass())) {
     150        11468 :         if ((enteredLane == myLane || (MSGlobals::gUseMesoSim && veh.getCurrentEdge() == &myLane->getEdge()))
     151        45810 :                 && (reason == NOTIFICATION_DEPARTED || reason == NOTIFICATION_JUNCTION || reason == NOTIFICATION_PARKING)) {
     152        34242 :             SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     153        34242 :             MSRouteIterator firstIt = std::find(sveh.getCurrentRouteEdge(), sveh.getRoute().end(), myLane->getNextNormal());
     154        34242 :             if (match(firstIt, sveh.getRoute().end())) {
     155        20608 :                 if (myTrains.count(&sveh) == 0) {
     156        20568 :                     enterDriveWay(sveh, reason);
     157              :                 }
     158        20588 :                 return true;
     159              :             }
     160         1531 :         } else if (reason == NOTIFICATION_REROUTE) {
     161         1471 :             SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     162              :             assert(myTrains.count(&sveh) == 0);
     163         1471 :             int movedPast = matchesPastRoute(sveh);
     164              :             // vehicle must still be one the drivway
     165         1471 :             if (movedPast >= 0 && movedPast < myForwardEdgeCount) {
     166          768 :                 enterDriveWay(sveh, reason);
     167          768 :                 return true;
     168              :             }
     169              :         }
     170              :     }
     171              :     return false;
     172              : }
     173              : 
     174              : 
     175              : bool
     176       141694 : 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       141694 :     if (veh.isVehicle() && MSRailSignalControl::isUsingDriveWays(veh.getVClass())) {
     182              :         // leaving network with departure, teleport etc
     183       141694 :         if (reason != MSMoveReminder::NOTIFICATION_JUNCTION && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     184         7391 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     185         7391 :             if (myWriteVehicles) {
     186          784 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     187              :             }
     188         7391 :             return false;
     189       134303 :         } else if (MSGlobals::gUseMesoSim && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     190              :             // notifyLeave is called before moving the route iterator
     191        19162 :             const MSLane* leftLane = (*(dynamic_cast<SUMOVehicle&>(veh).getCurrentRouteEdge()))->getLanes().front();
     192        19162 :             return notifyLeaveBack(veh, reason, leftLane);
     193              :         } else {
     194              :             return true;
     195              :         }
     196              :     } else {
     197            0 :         return false;
     198              :     }
     199              : }
     200              : 
     201              : 
     202              : bool
     203        86846 : 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        86846 :     if (veh.isVehicle() && MSRailSignalControl::isUsingDriveWays(veh.getVClass())) {
     208        86846 :         if (leftLane == myForward.back() && (veh.getBackLane() != leftLane->getBidiLane() || MSGlobals::gUseMesoSim)) {
     209        13232 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     210        13232 :             if (myWriteVehicles) {
     211         1284 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     212              :             }
     213        13232 :             return false;
     214              :         } else {
     215        73614 :             return true;
     216              :         }
     217              :     } else {
     218            0 :         return false;
     219              :     }
     220              : }
     221              : 
     222              : 
     223              : bool
     224         1552 : 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         1552 :     if (MSRailSignalControl::isUsingDriveWays(veh.getVClass())) {
     230         1550 :         SUMOVehicle* sveh = dynamic_cast<SUMOVehicle*>(&veh);
     231              :         assert(myTrains.count(sveh) != 0);
     232         1550 :         if (matchesPastRoute(*sveh) >= 0) {
     233              :             //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " valid\n";
     234          772 :             return true;
     235              :         }
     236              :         // no match found, remove
     237              :         myTrains.erase(sveh);
     238          778 :         if (myWriteVehicles) {
     239          136 :             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         3021 : MSDriveWay::matchesPastRoute(SUMOVehicle& sveh) const {
     249              :     // look backwards along the route to find the driveway lane
     250         3021 :     const ConstMSEdgeVector& routeEdges = sveh.getRoute().getEdges();
     251        18901 :     for (int i = sveh.getRoutePosition(); i >= 0; i--) {
     252        18483 :         if (routeEdges[i] == myLane->getNextNormal()) {
     253              :             MSRouteIterator firstIt = routeEdges.begin() + i;
     254         2603 :             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         1630 :                 return sveh.getRoutePosition() - i;
     258              :             }
     259          973 :             break;
     260              :         }
     261              :     }
     262              :     return -1;
     263              : }
     264              : 
     265              : 
     266              : void
     267        21336 : MSDriveWay::enterDriveWay(SUMOVehicle& sveh, Notification reason) {
     268        21336 :     myTrains.insert(&sveh);
     269        21336 :     if (myOrigin != nullptr) {
     270        13623 :         MSRailSignalControl::getInstance().notifyApproach(myOrigin);
     271              :     }
     272        71392 :     for (const MSDriveWay* foe : myFoes) {
     273        50056 :         if (foe->myOrigin != nullptr) {
     274        38177 :             MSRailSignalControl::getInstance().notifyApproach(foe->myOrigin);
     275              :         }
     276              :     }
     277        21336 :     if (myWriteVehicles) {
     278         2184 :         myVehicleEvents.push_back(VehicleEvent(SIMSTEP, true, sveh.getID(), reason));
     279              :     }
     280        21336 : }
     281              : 
     282              : bool
     283       622914 : MSDriveWay::reserve(const Approaching& closest, MSEdgeVector& occupied) {
     284       622914 :     if (foeDriveWayOccupied(true, closest.first, occupied)) {
     285              :         return false;
     286              :     }
     287       535942 :     for (MSLink* foeLink : myConflictLinks) {
     288       150851 :         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       385091 :     myActive = closest.first;
     298       385091 :     return true;
     299              : }
     300              : 
     301              : 
     302              : bool
     303       150851 : 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       150851 :     if (foeLink->getApproaching().size() > 0) {
     310        51927 :         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        51927 :         if (foe.first == veh.first) {
     317        51927 :             return false;
     318              :         }
     319              :         const MSTrafficLightLogic* foeTLL = foeLink->getTLLogic();
     320              :         assert(foeTLL != nullptr);
     321        46848 :         const MSRailSignal* constFoeRS = dynamic_cast<const MSRailSignal*>(foeTLL);
     322              :         MSRailSignal* foeRS = const_cast<MSRailSignal*>(constFoeRS);
     323        46848 :         if (foeRS != nullptr) {
     324        46848 :             const MSDriveWay& foeDriveWay = foeRS->retrieveDriveWayForVeh(foeLink->getTLIndex(), foe.first);
     325              :             MSEdgeVector occupied;
     326        72047 :             if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied) ||
     327        41806 :                     !foeRS->constraintsAllow(foe.first) ||
     328        32933 :                     !overlap(foeDriveWay) ||
     329        63174 :                     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        34855 :                 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        46848 :         }
     369              :     }
     370              :     return false;
     371              : }
     372              : 
     373              : 
     374              : const MSDriveWay*
     375        75683 : MSDriveWay::getFoeOrSubFoe(const MSDriveWay* dw) const {
     376        75683 :     if (std::find(myFoes.begin(), myFoes.end(), dw) != myFoes.end()) {
     377        44962 :         return dw;
     378              :     }
     379        35502 :     for (const MSDriveWay* sub : dw->mySubDriveWays) {
     380        27838 :         const MSDriveWay* foe = getFoeOrSubFoe(sub);
     381        27838 :         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        16059 : MSDriveWay::conflictLaneOccupied(bool store, const SUMOVehicle* ego) const {
     417       241593 :     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        11649 :     return false;
     473              : }
     474              : 
     475              : 
     476              : bool
     477      7601126 : MSDriveWay::foeDriveWayApproached() const {
     478     15345723 :     for (const MSDriveWay* foeDW : myFoes) {
     479     15269832 :         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     16453860 : MSDriveWay::foeDriveWayOccupied(bool store, const SUMOVehicle* ego, MSEdgeVector& occupied) const {
     494     33921617 :     for (const MSDriveWay* foeDW : myFoes) {
     495     25906731 :         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      8487795 :             if (foeDW->myTrains.size() == 1) {
     502      8486748 :                 SUMOVehicle* foe = *foeDW->myTrains.begin();
     503      8486748 :                 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        48909 :                     continue;
     510              :                 }
     511      8463744 :                 if (hasJoin(ego, foe)) {
     512           36 :                     continue;
     513              :                 }
     514              :             }
     515      8464755 :             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      8464755 :             if (useSiding.first) {
     527        25869 :                 continue;
     528              :             } else {
     529      8438886 :                 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     16879287 :                 for (const SUMOVehicle* foe : foeDW->myTrains) {
     536      8440401 :                     occupied.push_back(const_cast<MSEdge*>(foe->getEdge()));
     537      8440401 :                     MSEdge* bidi = const_cast<MSEdge*>(foe->getEdge()->getBidiEdge());
     538      8440401 :                     if (bidi != nullptr) {
     539      8005215 :                         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      7791875 :                 if (ego != nullptr && MSGlobals::gTimeToTeleportRSDeadlock > 0
     544      8701664 :                         && (ego->getWaitingTime() > ego->getVehicleType().getCarFollowModel().getStartupDelay() || !ego->isOnRoad())) {
     545              :                     // if there is an occupied siding, it becomes part of the waitRelation
     546       147812 :                     SUMOVehicle* foe = *(useSiding.second == nullptr ? foeDW : useSiding.second)->myTrains.begin();
     547       147812 :                     const MSRailSignal* rs = myOrigin != nullptr ? dynamic_cast<const MSRailSignal*>(myOrigin->getTLLogic()) : nullptr;
     548       147812 :                     MSRailSignalControl::getInstance().addWaitRelation(ego, rs, foe);
     549              :                 }
     550      8438886 :                 return true;
     551              :             }
     552     17418936 :         } else if (foeDW != this && isDepartDriveway() && !foeDW->isDepartDriveway()) {
     553        16284 :             if (foeDW->myOrigin->getApproaching().size() > 0) {
     554         3332 :                 Approaching foeA = foeDW->myOrigin->getClosest();
     555         3332 :                 const SUMOVehicle* foe = foeA.first;
     556         3332 :                 if (foeA.second.dist < foe->getBrakeGap(true)) {
     557          261 :                     MSRouteIterator firstIt = std::find(foe->getCurrentRouteEdge(), foe->getRoute().end(), foeDW->myRoute.front());
     558          261 :                     if (firstIt != foe->getRoute().end()) {
     559          261 :                         if (foeDW->match(firstIt, foe->getRoute().end())) {
     560          204 :                             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          204 :                             if (useSiding) {
     567              :                                 //std::cout << SIMTIME << " " << getID() << " ego=" << ego->getID() << " foeDW=" << foeDW->getID() << " myFoes=" << toString(myFoes) << "\n";
     568          116 :                                 continue;
     569              :                             } else {
     570           88 :                                 return true;
     571              :                             }
     572              :                         }
     573              :                     }
     574              :                 }
     575              :             }
     576              :         }
     577              :     }
     578      8016027 :     for (const std::set<const MSDriveWay*>& dlFoes : myDeadlocks) {
     579              :         bool allOccupied = true;
     580        11091 :         for (const MSDriveWay* dlFoe : dlFoes) {
     581         8376 :             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         3856 :         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         9369 :             for (const MSDriveWay* dlFoe : dlFoes) {
     594         6654 :                 MSRailSignal::blockingDriveWays().push_back(dlFoe);
     595              :             }
     596              :             return true;
     597              :         }
     598              :     }
     599              :     return false;
     600              : }
     601              : 
     602              : 
     603              : bool
     604      8463744 : MSDriveWay::hasJoin(const SUMOVehicle* ego, const SUMOVehicle* foe) {
     605      8463744 :     if (ego != nullptr && !MSGlobals::gUseMesoSim) {
     606       264570 :         std::string joinVehicle = "";
     607       264570 :         const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
     608       264570 :         if (stop != nullptr) {
     609       106034 :             joinVehicle = stop->join;
     610              :         }
     611       264570 :         if (joinVehicle == "" && !ego->hasDeparted() && ego->getStops().size() > 1) {
     612              :             // check one more stop
     613         7344 :             auto it = ego->getStops().begin();
     614              :             std::advance(it, 1);
     615         7344 :             joinVehicle = it->pars.join;
     616              :         }
     617       264570 :         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       264546 :         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      8496478 : MSDriveWay::canUseSiding(const SUMOVehicle* ego, const MSDriveWay* foe, const MSEdge* recurseSidingEnd) const {
     648              :     auto it = mySidings.find(foe);
     649      8496478 :     if (it != mySidings.end()) {
     650        69178 :         for (auto siding : it->second) {
     651              :             // assume siding is usuable when computing state for unapproached signal (ego == nullptr)
     652        68978 :             if (ego == nullptr || siding.length >= ego->getLength()) {
     653              :                 // if the siding is already "reserved" by another vehicle we cannot use it here
     654        59085 :                 const MSEdge* sidingEnd = myRoute[siding.end];
     655              :                 bool checkNext = false;
     656       137233 :                 for (MSDriveWay* sidingApproach : myEndingDriveways[sidingEnd]) {
     657        96939 :                     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         3622 :                         continue;
     666              :                     }
     667        93317 :                     if (!sidingApproach->myTrains.empty() && *sidingApproach->myTrains.begin() != ego) {
     668              :                         // possibly the foe vehicle can use the other part of the siding
     669        24794 :                         if (recurseSidingEnd == nullptr) {
     670              :                             const SUMOVehicle* foeVeh = nullptr;
     671        17225 :                             if (!foe->myTrains.empty()) {
     672        17096 :                                 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        17225 :                             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        17225 :                             const MSDriveWay* foe2 = foe->isSubDriveWay() ? foe->myParent : foe;
     681        17225 :                             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        17225 :                             if (this2 != nullptr && foe2->canUseSiding(foeVeh, this2, sidingEnd).first) {
     688         6003 :                                 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        18791 :                         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        45448 :                 for (int i : siding.intermediateEnds) {
     705         8188 :                     const MSEdge* intermediateEnd = myRoute[i];
     706        15338 :                     for (MSDriveWay* intermediateApproach : myEndingDriveways[intermediateEnd]) {
     707              :                         if (!intermediateApproach->myTrains.empty()
     708        10184 :                                 && (*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        37260 :                 if (checkNext) {
     745         3622 :                     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        33638 :                 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      8441015 :     return std::make_pair(false, nullptr);
     764              : }
     765              : 
     766              : bool
     767        23867 : MSDriveWay::overlap(const MSDriveWay& other) const {
     768        43502 :     for (int i = 0; i < myCoreSize; i++) {
     769       415418 :         for (int j = 0; j < other.myCoreSize; j++) {
     770       395783 :             const MSEdge* edge = myRoute[i];
     771       395783 :             const MSEdge* edge2 = other.myRoute[j];
     772              :             if (edge->getToJunction() == edge2->getToJunction()
     773       374496 :                     || edge->getToJunction() == edge2->getFromJunction()
     774       768263 :                     || 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        80238 : MSDriveWay::flankConflict(const MSDriveWay& other) const {
     786       277306 :     for (const MSLane* lane : myForward) {
     787      1143056 :         for (const MSLane* lane2 : other.myForward) {
     788       935950 :             if (lane == lane2) {
     789              :                 return true;
     790              :             }
     791              :         }
     792      3019597 :         for (const MSLane* lane2 : other.myBidi) {
     793      2817852 :             if (lane == lane2) {
     794         6077 :                 if (bidiBlockedBy(other)) {
     795              :                     // it's only a deadlock if both trains block symmetrically
     796              :                     return true;
     797              :                 }
     798              :             }
     799              :         }
     800      7221790 :         for (const MSLane* lane2 : other.myBidiExtended) {
     801      7024722 :             if (lane == lane2) {
     802        23307 :                 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        68343 : MSDriveWay::crossingConflict(const MSDriveWay& other) const {
     815       251904 :     for (const MSLane* lane : myForward) {
     816       861942 :         for (const MSLane* lane2 : other.myForward) {
     817       678381 :             if (lane->isNormal() && lane2->isNormal() && lane->getEdge().getToJunction() == lane2->getEdge().getToJunction()) {
     818              :                 return true;
     819              :             }
     820              :         }
     821              :     }
     822        60150 :     if (myOrigin != nullptr && other.myOrigin != nullptr
     823        52299 :             && myOrigin->getJunction() == other.myOrigin->getJunction()
     824              :             //&& myForward.front()->isInternal() && other.myForward.front()->isInternal()
     825         1877 :             && myOrigin->getJunction()->getLogic() != nullptr
     826        68018 :             && 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        66008 :     if (other.myOrigin != nullptr && other.myForward.front()->isInternal()) {
     834       155088 :         for (int i = 0; i < (int)myForward.size() - 1; i++) {
     835        99222 :             const MSLane* lane = myForward[i];
     836        99222 :             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        30547 : MSDriveWay::bidiBlockedBy(const MSDriveWay& other) const {
     856       538931 :     for (const MSLane* lane : myBidi) {
     857      1967699 :         for (const MSLane* lane2 : other.myForward) {
     858      1459315 :             if (lane == lane2) {
     859              :                 return true;
     860              :             }
     861              :         }
     862              :     }
     863       543359 :     for (const MSLane* lane : myBidiExtended) {
     864      1853621 :         for (const MSLane* lane2 : other.myForward) {
     865      1334750 :             if (lane == lane2) {
     866         4330 :                 if (overlap(other)) {
     867              :                     return true;
     868              :                 }
     869              :             }
     870              :         }
     871              :     }
     872              :     return false;
     873              : }
     874              : 
     875              : 
     876              : bool
     877        17543 : MSDriveWay::bidiBlockedByEnd(const MSDriveWay& other) const {
     878        17543 :     const MSLane* end = other.myForward.back();
     879       152526 :     for (const MSLane* lane : myBidi) {
     880       140946 :         if (lane == end) {
     881              :             return true;
     882              :         }
     883              :     }
     884        99078 :     for (const MSLane* lane : myBidiExtended) {
     885        90428 :         if (lane == end) {
     886         2930 :             if (overlap(other)) {
     887              :                 return true;
     888              :             }
     889              :         }
     890              :     }
     891              :     return false;
     892              : }
     893              : 
     894              : bool
     895         6106 : MSDriveWay::forwardRouteConflict(std::set<const MSEdge*> forward, const MSDriveWay& other, bool secondCheck) {
     896              :     int i = 0;
     897        64755 :     for (const MSEdge* edge2 : other.myRoute) {
     898        63935 :         if (i == other.myCoreSize) {
     899              :             return false;
     900              :         }
     901        63935 :         i++;
     902        63935 :         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         9301 : MSDriveWay::writeBlocks(OutputDevice& od) const {
     915        16305 :     od.openTag(isSubDriveWay() ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
     916         9301 :     od.writeAttr(SUMO_ATTR_ID, myID);
     917         9301 :     od.writeAttr(SUMO_ATTR_VEHICLE, myFirstVehicle);
     918         9301 :     od.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
     919         9301 :     if (myCoreSize != (int)myRoute.size()) {
     920            0 :         od.writeAttr("core", myCoreSize);
     921              :     }
     922         9301 :     od.openTag("forward");
     923         9301 :     od.writeAttr(SUMO_ATTR_LANES, toString(myForward));
     924        18602 :     od.closeTag();
     925         9301 :     if (!isSubDriveWay()) {
     926         7004 :         od.openTag("bidi");
     927         7004 :         od.writeAttr(SUMO_ATTR_LANES, toString(myBidi));
     928         7004 :         if (myBidiExtended.size() > 0) {
     929         2182 :             od.lf();
     930         2182 :             od << "                   ";
     931         4364 :             od.writeAttr("deadlockCheck", toString(myBidiExtended));
     932              :         }
     933         7004 :         od.closeTag();
     934         7004 :         od.openTag("flank");
     935         7004 :         od.writeAttr(SUMO_ATTR_LANES, toString(myFlank));
     936         7004 :         od.closeTag();
     937              : 
     938        14008 :         od.openTag("conflictLinks");
     939              : 
     940              :         std::vector<std::string> signals;
     941        11499 :         for (MSLink* link : myConflictLinks) {
     942         8990 :             signals.push_back(getTLLinkID(link));
     943              :         }
     944         7004 :         od.writeAttr("signals", joinToStringSorting(signals, " "));
     945        14008 :         od.closeTag();
     946              : 
     947              :         std::vector<std::string> foes;
     948        26931 :         for (MSDriveWay* dw : myFoes) {
     949        19927 :             foes.push_back(dw->myID);
     950              :         }
     951         7004 :         if (foes.size() > 0) {
     952         6847 :             od.openTag("foes");
     953         6847 :             od.writeAttr("driveWays", joinToStringSorting(foes, " "));
     954        13694 :             od.closeTag();
     955              :         }
     956         8672 :         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         7097 :         for (auto item : myDeadlocks) {
     976           93 :             od.openTag("deadlock");
     977           93 :             od.writeAttr("foes", joinNamedToStringSorting(item, " "));
     978          186 :             od.closeTag();
     979              :         }
     980         7004 :     }
     981        18602 :     od.closeTag(); // driveWay
     982              : 
     983        11598 :     for (const MSDriveWay* sub : mySubDriveWays) {
     984         2297 :         sub->writeBlocks(od);
     985              :     }
     986              : #ifdef DRIVEWAY_SANITY_CHECK
     987         9301 :     std::set<MSDriveWay*> uFoes(myFoes.begin(), myFoes.end());
     988         9301 :     if (uFoes.size() != myFoes.size()) {
     989            0 :         WRITE_WARNINGF("Duplicate foes in driveway '%'", getID());
     990              : 
     991              :     }
     992              : #endif
     993         9301 : }
     994              : 
     995              : 
     996              : void
     997         1103 : MSDriveWay::writeBlockVehicles(OutputDevice& od) const {
     998         1971 :     od.openTag(isSubDriveWay() ? "subDriveWay" : "driveWay");
     999         1103 :     od.writeAttr(SUMO_ATTR_ID, myID);
    1000         3317 :     for (const VehicleEvent& ve : myVehicleEvents) {
    1001         3316 :         od.openTag(ve.isEntry ? "entry" : "exit");
    1002         2214 :         od.writeAttr(SUMO_ATTR_ID, ve.id);
    1003         2214 :         od.writeAttr(SUMO_ATTR_TIME, time2string(ve.time));
    1004         2214 :         od.writeAttr("reason", Notifications.getString(ve.reason));
    1005         4428 :         od.closeTag(); // event
    1006              :     }
    1007         2206 :     od.closeTag(); // driveWay
    1008              : 
    1009         1338 :     for (const MSDriveWay* sub : mySubDriveWays) {
    1010          235 :         sub->writeBlockVehicles(od);
    1011              :     }
    1012         1103 : }
    1013              : 
    1014              : 
    1015              : void
    1016         9206 : MSDriveWay::buildRoute(const MSLink* origin,
    1017              :                        MSRouteIterator next, MSRouteIterator end,
    1018              :                        LaneVisitedMap& visited,
    1019              :                        std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess>& flankSwitches) {
    1020         9206 :     double length = 0;
    1021              :     bool seekForwardSignal = true;
    1022              :     bool seekBidiSwitch = true;
    1023              :     bool foundUnsafeSwitch = false;
    1024         9206 :     MSLane* toLane = origin ? origin->getViaLaneOrLane() : (*next)->getLanes()[0];
    1025        23842 :     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       127950 :         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          224 :             if (seekForwardSignal && myBlockLengthWarnings.count(myRoute.front()) == 0) {
    1037          234 :                 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          224 :             myAbortedBuild = true;
    1042              :             // length exceeded
    1043              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1044              :             if (gDebugFlag4) {
    1045              :                 std::cout << " abort: length=" << length << "\n";
    1046              :             }
    1047              : #endif
    1048         9206 :             return;
    1049              :         }
    1050              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1051              :         if (gDebugFlag4) {
    1052              :             std::cout << "   toLane=" << toLane->getID() << " visited=" << formatVisitedMap(visited) << "\n";
    1053              :         }
    1054              : #endif
    1055       127726 :         const MSEdge* current = &toLane->getEdge();
    1056       127726 :         if (current->isNormal()) {
    1057        68697 :             myRoute.push_back(current);
    1058        68697 :             if (next != end) {
    1059              :                 next++;
    1060              :             }
    1061              :         }
    1062       127726 :         appendMapIndex(visited, toLane);
    1063       127726 :         length += toLane->getLength();
    1064       127726 :         MSLane* bidi = toLane->getBidiLane();
    1065       127726 :         if (seekForwardSignal) {
    1066        36576 :             if (!foundUnsafeSwitch) {
    1067        36576 :                 myForward.push_back(toLane);
    1068        36576 :                 if (toLane->isNormal()) {
    1069        21503 :                     myForwardEdgeCount++;
    1070              :                 }
    1071        36576 :                 if (myForward.size() == 1) {
    1072         9206 :                     myLane = toLane;
    1073         9206 :                     if (MSGlobals::gUseMesoSim) {
    1074         2997 :                         MESegment* s = MSGlobals::gMesoNet->getSegmentForEdge(myLane->getEdge());
    1075         2997 :                         s->addDetector(this, myLane->getIndex());
    1076              :                     } else {
    1077         6209 :                         toLane->addMoveReminder(this, false);
    1078              :                     }
    1079              :                 }
    1080              :             }
    1081        91150 :         } else if (bidi == nullptr) {
    1082        28194 :             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       127726 :         if (bidi != nullptr) {
    1098        84068 :             if (!seekForwardSignal && !foundUnsafeSwitch && bidi->isNormal()) {
    1099              :                 // look for switch that could protect from oncoming vehicles
    1100        24542 :                 for (const MSLink* const link : bidi->getLinkCont()) {
    1101        13805 :                     if (link->getDirection() == LinkDirection::TURN) {
    1102          531 :                         continue;
    1103              :                     }
    1104        26390 :                     if (!myBidi.empty() && link->getViaLaneOrLane() != myBidi.back()) {
    1105         2434 :                         myCoreSize = (int)myRoute.size() - 1;
    1106         2434 :                         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         2434 :                         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        81634 :             if (foundUnsafeSwitch) {
    1124        40689 :                 myBidiExtended.push_back(bidi);
    1125              :             } else {
    1126        43379 :                 myBidi.push_back(bidi);
    1127              :             }
    1128              :         }
    1129       127726 :         const std::vector<MSLink*>& links = toLane->getLinkCont();
    1130       127726 :         toLane = nullptr;
    1131       134776 :         for (const MSLink* const link : links) {
    1132       126150 :             if ((next != end && &link->getLane()->getEdge() == *next)) {
    1133       119100 :                 toLane = link->getViaLaneOrLane();
    1134       119100 :                 if (link->getTLLogic() != nullptr && link->getTLIndex() >= 0 && link->getTLLogic()->getLogicType() == TrafficLightType::RAIL_SIGNAL) {
    1135        27678 :                     if (link == origin) {
    1136          356 :                         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          356 :                         myAbortedBuild = true;
    1141              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1142              :                         if (gDebugFlag4) {
    1143              :                             std::cout << " abort: found circle\n";
    1144              :                         }
    1145              : #endif
    1146          356 :                         return;
    1147              :                     }
    1148              :                     seekForwardSignal = false;
    1149        27322 :                     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       118744 :                 if (isSwitch(link)) {
    1159              :                     // switch on driveway
    1160              :                     //std::cout << "mySwitchDriveWays " << getID() << " link=" << link->getDescription() << "\n";
    1161        73861 :                     mySwitchDriveWays[link].push_back(this);
    1162              :                 }
    1163       118744 :                 if (link->getLane()->getBidiLane() != nullptr && &link->getLane()->getEdge() == current->getBidiEdge()) {
    1164              :                     // reversal on driveway
    1165         1775 :                     myReversalDriveWays[current].push_back(this);
    1166         1775 :                     myReversals.push_back(current);
    1167              :                 }
    1168              :                 break;
    1169              :             }
    1170              :         }
    1171       127370 :         if (toLane == nullptr) {
    1172         8626 :             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           91 :                 myFoundJump = true;
    1181           91 :                 return;
    1182              :             } else {
    1183              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1184              :                 if (gDebugFlag4) {
    1185              :                     std::cout << "      abort: no next lane available\n";
    1186              :                 }
    1187              : #endif
    1188         8535 :                 myTerminateRoute = true;
    1189         8535 :                 return;
    1190              :             }
    1191              :         }
    1192       118744 :     }
    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       131589 : MSDriveWay::isSwitch(const MSLink* link) {
    1204       255440 :     for (const MSLink* other : link->getLaneBefore()->getNormalPredecessorLane()->getLinkCont()) {
    1205       144523 :         if (other->getLane() != link->getLane() && !other->isTurnaround()) {
    1206              :             return true;
    1207              :         }
    1208              :     }
    1209       172661 :     for (auto ili : link->getLane()->getIncomingLanes()) {
    1210       125161 :         if (ili.viaLink != link && !ili.viaLink->isTurnaround()) {
    1211              :             return true;
    1212              :         }
    1213              :     }
    1214        47500 :     const MSLane* bidi = link->getLane()->getBidiLane();
    1215        47500 :     if (bidi != nullptr) {
    1216        71692 :         for (const MSLink* other : bidi->getLinkCont()) {
    1217        37049 :             if (other->getLane() != link->getLaneBefore()->getNormalPredecessorLane()->getBidiLane() && !other->isTurnaround()) {
    1218              :                 return true;
    1219              :             }
    1220              :         }
    1221              :     }
    1222              :     return false;
    1223              : }
    1224              : 
    1225              : 
    1226              : void
    1227        36824 : 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) std::cout << " checkFlanks lanes=" << toString(lanes) << " allFoes=" << allFoes << "\n";
    1231              : #endif
    1232        21720 :     const MSLink* reverseOriginLink = originLink != nullptr && originLink->getLane()->getBidiLane() != nullptr && originLink->getLaneBefore()->getBidiLane() != nullptr
    1233        48692 :                                       ? originLink->getLane()->getBidiLane()->getLinkTo(originLink->getLaneBefore()->getBidiLane())
    1234              :                                       : nullptr;
    1235              :     //std::cout << "   originLink=" << originLink->getDescription() << "\n";
    1236        11868 :     if (reverseOriginLink != nullptr) {
    1237        11868 :         reverseOriginLink = reverseOriginLink->getCorrespondingExitLink();
    1238              :         //std::cout << "   reverseOriginLink=" << reverseOriginLink->getDescription() << "\n";
    1239              :     }
    1240       160517 :     for (int i = 0; i < (int)lanes.size(); i++) {
    1241       123693 :         const MSLane* lane = lanes[i];
    1242       123693 :         const MSLane* prev = i > 0 ? lanes[i - 1] : nullptr;
    1243       123693 :         const MSLane* next = i + 1 < (int)lanes.size() ? lanes[i + 1] : nullptr;
    1244       123693 :         if (lane->isInternal()) {
    1245        53064 :             continue;
    1246              :         }
    1247       146669 :         for (auto ili : lane->getIncomingLanes()) {
    1248        86567 :             if (ili.viaLink == originLink
    1249        75125 :                     || ili.viaLink == reverseOriginLink
    1250        71821 :                     || ili.viaLink->getDirection() == LinkDirection::TURN
    1251        65632 :                     || ili.viaLink->getDirection() == LinkDirection::TURN_LEFTHAND
    1252       141672 :                     || (originLink == nullptr && i == 0 && movingBlock)) {
    1253        10527 :                 continue;
    1254              :             }
    1255        65513 :             if (ili.lane != prev && ili.lane != next) {
    1256              : #ifdef DEBUG_CHECK_FLANKS
    1257              :                 if (gDebugFlag4) 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";
    1258              : #endif
    1259              :                 flankSwitches.insert(ili.viaLink);
    1260        52659 :             } else if (allFoes) {
    1261              :                 // link is part of the driveway, find foes that cross the driveway without entering
    1262        15719 :                 checkCrossingFlanks(ili.viaLink, visited, flankSwitches);
    1263              :             }
    1264              :         }
    1265              :     }
    1266        36824 : }
    1267              : 
    1268              : 
    1269              : void
    1270        15719 : MSDriveWay::checkCrossingFlanks(MSLink* dwLink, const LaneVisitedMap& visited, std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess>& flankSwitches) const {
    1271              : #ifdef DEBUG_CHECK_FLANKS
    1272              :     if (gDebugFlag4) std::cout << "  checkCrossingFlanks  dwLink=" << dwLink->getDescription() << " visited=" << formatVisitedMap(visited) << "\n";
    1273              : #endif
    1274              :     const MSJunction* junction = dwLink->getJunction();
    1275        15719 :     if (junction == nullptr) {
    1276              :         return; // unregulated junction;
    1277              :     }
    1278        15639 :     const MSJunctionLogic* logic = junction->getLogic();
    1279        15639 :     if (logic == nullptr) {
    1280              :         return; // unregulated junction;
    1281              :     }
    1282        84112 :     for (const MSEdge* in : junction->getIncoming()) {
    1283        68541 :         if (in->isInternal()) {
    1284        34790 :             continue;
    1285              :         }
    1286        67920 :         for (MSLane* inLane : in->getLanes()) {
    1287        34169 :             const MSLane* inBidi = inLane->getBidiLane();
    1288        46783 :             if (isRailwayOrShared(inLane->getPermissions()) && visited.count(inLane) == 0 && (inBidi == nullptr || visited.count(inBidi) == 0)) {
    1289         9853 :                 for (MSLink* link : inLane->getLinkCont()) {
    1290         5145 :                     if (link->getIndex() >= 0 && logic->getFoesFor(dwLink->getIndex()).test(link->getIndex())
    1291        13198 :                             && visited.count(link->getLane()) == 0) {
    1292              : #ifdef DEBUG_CHECK_FLANKS
    1293              :                         if (gDebugFlag4) std::cout << " add crossing flankSwitch junction=" << junction->getID() << " index=" << link->getIndex() << "\n";
    1294              : #endif
    1295          687 :                         if (link->getViaLane() == nullptr) {
    1296              :                             flankSwitches.insert(link);
    1297              :                         } else {
    1298              :                             flankSwitches.insert(link->getViaLane()->getLinkCont().front());
    1299              :                         }
    1300              :                     }
    1301              :                 }
    1302              :             }
    1303              :         }
    1304              :     }
    1305              : }
    1306              : 
    1307              : void
    1308        16012 : MSDriveWay::findFlankProtection(MSLink* link, MSLink* origLink, std::vector<const MSLane*>& flank) {
    1309              : #ifdef DEBUG_CHECK_FLANKS
    1310              :     if (gDebugFlag4) std::cout << "  findFlankProtection link=" << link->getDescription() << " origLink=" << origLink->getDescription() << "\n";
    1311              : #endif
    1312        16012 :     if (link->getCorrespondingEntryLink()->getTLLogic() != nullptr && link->getJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1313         2334 :         MSLink* entry = const_cast<MSLink*>(link->getCorrespondingEntryLink());
    1314              :         // guarded by signal
    1315              : #ifdef DEBUG_CHECK_FLANKS
    1316              :         if (gDebugFlag4) std::cout << "   flank guarded by " << entry->getTLLogic()->getID() << "\n";
    1317              : #endif
    1318              :         // @note, technically it's enough to collect links from foe driveways
    1319              :         // but this also adds "unused" conflict links which may aid comprehension
    1320         2334 :         myConflictLinks.push_back(entry);
    1321         2334 :         addFoes(entry);
    1322              :     } else {
    1323              :         const MSLane* lane = link->getLaneBefore();
    1324              :         std::vector<MSLink*> predLinks;
    1325        27368 :         for (auto ili : lane->getIncomingLanes()) {
    1326        13690 :             if (!ili.viaLink->isTurnaround()) {
    1327        13291 :                 predLinks.push_back(ili.viaLink);
    1328              :             }
    1329              :         }
    1330        13678 :         if (predLinks.size() > 1) {
    1331              :             // this is a switch
    1332              : #ifdef DEBUG_ADD_FOES
    1333              :             if (gDebugFlag4) std::cout << "    predecessors of " << link->getDescription() << " isSwitch\n";
    1334              : #endif
    1335          669 :             for (MSLink* pred : predLinks) {
    1336          446 :                 addSwitchFoes(pred);
    1337              :             }
    1338        13455 :         } else if (predLinks.size() == 1) {
    1339        12845 :             if (isSwitch(link)) {
    1340        11386 :                 addSwitchFoes(link);
    1341              :             } else {
    1342              :                 // continue upstream via single predecessor
    1343         1459 :                 findFlankProtection(predLinks.front(), origLink, flank);
    1344              :             }
    1345              :         }
    1346              :         // check for insertions
    1347        13678 :         if (myDepartureDriveways.count(&lane->getEdge()) != 0) {
    1348          362 :             for (MSDriveWay* foe : myDepartureDriveways[&lane->getEdge()]) {
    1349          187 :                 if (flankConflict(*foe) || crossingConflict(*foe)) {
    1350              : #ifdef DEBUG_ADD_FOES
    1351              :                     if (gDebugFlag4) std::cout << "  foe " << foe->getID() << " departs on flank=" << lane->getID() << "\n";
    1352              : #endif
    1353          131 :                     myFoes.push_back(foe);
    1354              :                 } else {
    1355              : #ifdef DEBUG_ADD_FOES
    1356              :                     if (gDebugFlag4) std::cout << "  cand foe " << foe->getID() << " departs on flank=" << lane->getID() << " rejected\n";
    1357              : #endif
    1358              :                 }
    1359              :             }
    1360              :         }
    1361        13678 :     }
    1362        16012 : }
    1363              : 
    1364              : 
    1365              : void
    1366        11832 : MSDriveWay::addSwitchFoes(MSLink* link) {
    1367              :     auto it = mySwitchDriveWays.find(link);
    1368        11832 :     if (it != mySwitchDriveWays.end()) {
    1369              : #ifdef DEBUG_ADD_FOES
    1370              :         if (gDebugFlag4) std::cout << "   driveway " << myID << " addSwitchFoes for link " << link->getDescription() << "\n";
    1371              : #endif
    1372        36295 :         for (MSDriveWay* foe : it->second) {
    1373        31924 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1374              : #ifdef DEBUG_ADD_FOES
    1375              :                 if (gDebugFlag4) std::cout << "   foe=" << foe->myID
    1376              :                           << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this)
    1377              :                           << " cc1=" << crossingConflict(*foe) << " cc2=" << foe->crossingConflict(*this) << "\n";
    1378              : #endif
    1379         4750 :                 myFoes.push_back(foe);
    1380              :             } else {
    1381              : #ifdef DEBUG_ADD_FOES
    1382              :                 if (gDebugFlag4) std::cout << "   cand=" << foe->myID << "\n";
    1383              : #endif
    1384              :             }
    1385              :         }
    1386              :     }
    1387        11832 : }
    1388              : 
    1389              : 
    1390              : MSDriveWay*
    1391         9206 : MSDriveWay::buildDriveWay(const std::string& id, const MSLink* link, MSRouteIterator first, MSRouteIterator end) {
    1392              :     // collect lanes and links that are relevant for setting this signal for the current driveWay
    1393              :     // For each driveway we collect
    1394              :     //   - conflictLanes (signal must be red if any conflict lane is occupied)
    1395              :     //   - conflictLinks (signal must be red if any conflict link is approached by a vehicle
    1396              :     //      - that cannot break in time (arrivalSpeedBraking > 0)
    1397              :     //      - approached by a vehicle with higher switching priority (see #3941)
    1398              :     // These objects are construct in steps:
    1399              :     //
    1400              :     // forwardBlock
    1401              :     // - search forward recursive from outgoing lane until controlled railSignal link found
    1402              :     //   -> add all found lanes to conflictLanes
    1403              :     //
    1404              :     // bidiBlock (if any forwardBlock edge has bidi edge)
    1405              :     // - search bidi backward recursive until first switch
    1406              :     //   - from switch search backward recursive all other incoming until controlled rail signal link
    1407              :     //     -> add final links to conflictLinks
    1408              :     //
    1409              :     // flanks
    1410              :     // - search backward recursive from flanking switches
    1411              :     //   until controlled railSignal link or protecting switch is found
    1412              :     //   -> add all found lanes to conflictLanes
    1413              :     //   -> add final links to conflictLinks
    1414         9206 :     MSDriveWay* dw = new MSDriveWay(link, id);
    1415              :     LaneVisitedMap visited;
    1416              :     std::vector<const MSLane*> before;
    1417         9206 :     MSLane* fromBidi = nullptr;
    1418         9206 :     if (link != nullptr) {
    1419         5430 :         appendMapIndex(visited, link->getLaneBefore());
    1420         5430 :         fromBidi = link->getLaneBefore()->getBidiLane();
    1421              :     }
    1422              :     std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess> flankSwitches; // list of switches that threaten the driveway and for which protection must be found
    1423              : 
    1424         9206 :     if (fromBidi != nullptr) {
    1425         3049 :         before.push_back(fromBidi);
    1426              :     }
    1427              : #ifdef DEBUG_BUILD_DRIVEWAY
    1428              :     gDebugFlag4 = DEBUG_COND_DW(dw);
    1429              : #endif
    1430         9206 :     dw->buildRoute(link, first, end, visited, flankSwitches);
    1431         9206 :     dw->myCoreSize = (int)dw->myRoute.size();
    1432              : 
    1433         9206 :     MSRailSignal* rs = link ? const_cast<MSRailSignal*>(static_cast<const MSRailSignal*>(link->getTLLogic())) : nullptr;
    1434         9206 :     const bool movingBlock = (rs && rs->isMovingBlock()) || (!rs &&
    1435        12982 :             (OptionsCont::getOptions().getBool("railsignal-moving-block")
    1436         3652 :              || MSRailSignalControl::isMovingBlock((*first)->getPermissions())));
    1437              : 
    1438         9206 :     dw->checkFlanks(link, dw->myForward, visited, true, movingBlock, flankSwitches);
    1439         9206 :     dw->checkFlanks(link, dw->myBidi, visited, false, movingBlock, flankSwitches);
    1440         9206 :     dw->checkFlanks(link, before, visited, true, movingBlock, flankSwitches);
    1441        19407 :     for (MSLink* fsLink : flankSwitches) {
    1442              : #ifdef DEBUG_ADD_FOES
    1443              :         if (DEBUG_COND_DW(dw)) {
    1444              :             std::cout << " fsLink=" << fsLink->getDescription() << "\n";
    1445              :         }
    1446              : #endif
    1447        10201 :         dw->findFlankProtection(fsLink, fsLink, dw->myFlank);
    1448              :     }
    1449              :     std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess> flankSwitchesBidiExtended;
    1450         9206 :     dw->checkFlanks(link, dw->myBidiExtended, visited, false, movingBlock, flankSwitchesBidiExtended);
    1451        13558 :     for (MSLink* const flink : flankSwitchesBidiExtended) {
    1452              : #ifdef DEBUG_ADD_FOES
    1453              :         if (DEBUG_COND_DW(dw)) {
    1454              :             std::cout << " fsLinkExtended=" << flink->getDescription() << "\n";
    1455              :         }
    1456              : #endif
    1457         4352 :         dw->findFlankProtection(flink, flink, dw->myBidiExtended);
    1458              :     }
    1459              : #ifdef DEBUG_BUILD_DRIVEWAY
    1460              :     if (DEBUG_COND_DW(dw)) {
    1461              :         std::cout << SIMTIME << " buildDriveWay " << dw->myID << " link=" << (link == nullptr ? "NULL" : link->getDescription())
    1462              :                   << "\n    route=" << toString(dw->myRoute)
    1463              :                   << "\n    forward=" << toString(dw->myForward)
    1464              :                   << "\n    bidi=" << toString(dw->myBidi)
    1465              :                   << "\n    bidiEx=" << toString(dw->myBidiExtended)
    1466              :                   << "\n    flank=" << toString(dw->myFlank)
    1467              :                   << "\n    flankSwitch=" << MSRailSignal::describeLinks(std::vector<MSLink*>(flankSwitches.begin(), flankSwitches.end()))
    1468              :                   << "\n    coreSize=" << dw->myCoreSize
    1469              :                   << "\n";
    1470              :     }
    1471              : #endif
    1472         9206 :     if (!rs || !rs->isMovingBlock()) {
    1473         9100 :         dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myForward.begin(), dw->myForward.end());
    1474              :     }
    1475         9206 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myBidi.begin(), dw->myBidi.end());
    1476         9206 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myFlank.begin(), dw->myFlank.end());
    1477         9206 :     dw->addBidiFoes(rs, false);
    1478         9206 :     dw->addBidiFoes(rs, true);
    1479         9206 :     if (!movingBlock) {
    1480              :         // add driveways that start on the same signal / lane
    1481         8892 :         dw->addParallelFoes(link, *first);
    1482              :     }
    1483              :     // add driveways that reverse along this driveways route
    1484         9206 :     dw->addReversalFoes(movingBlock);
    1485              :     // make foes unique and symmetrical
    1486         9206 :     std::set<MSDriveWay*, ComparatorNumericalIdLess> uniqueFoes(dw->myFoes.begin(), dw->myFoes.end());
    1487              :     dw->myFoes.clear();
    1488              :     // check for self-intersecting forward-section in movingBlock mode
    1489         9206 :     if (movingBlock && uniqueFoes.count(dw) == 0) {
    1490              :         std::set<const MSJunction*> forwardJunctions;
    1491         3079 :         for (const MSLane* fw : dw->myForward) {
    1492         2811 :             if (fw->isNormal()) {
    1493         1499 :                 const MSJunction* fwTo = fw->getEdge().getToJunction();
    1494              :                 if (forwardJunctions.count(fwTo) == 1) {
    1495           21 :                     dw->myFoes.push_back(dw);
    1496              : #ifdef DEBUG_ADD_FOES
    1497              :                     if (DEBUG_COND_DW(dw)) {
    1498              :                         std::cout << " self-intersecting movingBlock for dw=" << dw->getID() << "\n";
    1499              :                     }
    1500              : #endif
    1501           21 :                     break;
    1502              :                 }
    1503              :                 forwardJunctions.insert(fwTo);
    1504              :             }
    1505              :         }
    1506              :     }
    1507         9206 :     std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess> uniqueCLink(dw->myConflictLinks.begin(), dw->myConflictLinks.end());
    1508         9206 :     const MSEdge* lastEdge = &dw->myForward.back()->getEdge();
    1509        16440 :     for (MSDriveWay* foe : uniqueFoes) {
    1510         7234 :         const MSEdge* foeLastEdge = &foe->myForward.back()->getEdge();
    1511         7234 :         const bool sameLast = foeLastEdge == lastEdge;
    1512         7234 :         if (sameLast && !movingBlock) {
    1513         2700 :             dw->myFoes.push_back(foe);
    1514         2700 :             if (foe != dw) {
    1515         2700 :                 foe->myFoes.push_back(dw);
    1516              :             }
    1517              :         } else {
    1518         4534 :             if (foe->bidiBlockedByEnd(*dw)) {
    1519              : #ifdef DEBUG_ADD_FOES
    1520              :                 if (DEBUG_COND_DW(dw)) {
    1521              :                     std::cout << " setting " << dw->getID() << " as foe of " << foe->getID() << "\n";
    1522              :                 }
    1523              : #endif
    1524         2193 :                 foe->myFoes.push_back(dw);
    1525         2193 :                 foe->addSidings(dw);
    1526              :             } else {
    1527         2341 :                 dw->buildSubFoe(foe, movingBlock);
    1528              :             }
    1529         4534 :             if (foe != dw) { // check for movingBlock
    1530         4509 :                 if (dw->bidiBlockedByEnd(*foe)) {
    1531              : #ifdef DEBUG_ADD_FOES
    1532              :                     if (DEBUG_COND_DW(dw)) {
    1533              :                         std::cout << " addFoeCheckSiding " << foe->getID() << "\n";
    1534              :                     }
    1535              : #endif
    1536         2429 :                     dw->myFoes.push_back(foe);
    1537         2429 :                     dw->addSidings(foe);
    1538              :                 } else  {
    1539         2080 :                     foe->buildSubFoe(dw, movingBlock);
    1540              :                 }
    1541              :             }
    1542              :         }
    1543         7234 :         if (link) {
    1544         5172 :             foe->addConflictLink(link);
    1545              :         }
    1546              :         // ignore links that have the same start junction
    1547         7234 :         if (foe->myRoute.front()->getFromJunction() != dw->myRoute.front()->getFromJunction()) {
    1548         8947 :             for (auto ili : foe->myForward.front()->getIncomingLanes()) {
    1549         4015 :                 if (ili.viaLink->getTLLogic() != nullptr) {
    1550              :                     // ignore links that originate on myBidi
    1551         3554 :                     const MSLane* origin = ili.viaLink->getLaneBefore();
    1552         3554 :                     if (std::find(dw->myBidi.begin(), dw->myBidi.end(), origin) == dw->myBidi.end()) {
    1553              :                         uniqueCLink.insert(ili.viaLink);
    1554              :                     }
    1555              :                 }
    1556              :             }
    1557              :         }
    1558              :     }
    1559              :     dw->myConflictLinks.clear();
    1560         9206 :     dw->myConflictLinks.insert(dw->myConflictLinks.begin(), uniqueCLink.begin(), uniqueCLink.end());
    1561         9206 :     myEndingDriveways[lastEdge].push_back(dw);
    1562         9206 :     if (!movingBlock) {
    1563              :         // every driveway is it's own foe (also all driveways that depart in the same block)
    1564        20782 :         for (MSDriveWay* sameEnd : myEndingDriveways[lastEdge]) {
    1565              :             if (uniqueFoes.count(sameEnd) == 0) {
    1566         9190 :                 dw->myFoes.push_back(sameEnd);
    1567         9190 :                 if (sameEnd != dw) {
    1568          298 :                     sameEnd->myFoes.push_back(dw);
    1569              :                 }
    1570              :             }
    1571              :         }
    1572              :     }
    1573              : #ifdef DEBUG_BUILD_DRIVEWAY
    1574              :     if (DEBUG_COND_DW(dw)) {
    1575              :         std::cout << dw->myID << " mb=" << movingBlock << " finalFoes " << toString(dw->myFoes) << "\n";
    1576              :     }
    1577              :     gDebugFlag4 = false;
    1578              : #endif
    1579         9206 :     return dw;
    1580         9206 : }
    1581              : 
    1582              : std::string
    1583         4495 : MSDriveWay::getTLLinkID(const MSLink* link) {
    1584         8990 :     return link->getTLLogic()->getID() + "_" + toString(link->getTLIndex());
    1585              : }
    1586              : 
    1587              : std::string
    1588            0 : MSDriveWay::getJunctionLinkID(const MSLink* link) {
    1589            0 :     return link->getJunction()->getID() + "_" + toString(link->getIndex());
    1590              : }
    1591              : 
    1592              : std::string
    1593         5430 : MSDriveWay::getClickableTLLinkID(const MSLink* link) {
    1594        16290 :     return "junction '" +  link->getTLLogic()->getID() + "', link " + toString(link->getTLIndex());
    1595              : }
    1596              : 
    1597              : std::string
    1598            0 : MSDriveWay::formatVisitedMap(const LaneVisitedMap& visited) {
    1599              :     UNUSED_PARAMETER(visited);
    1600              :     /*
    1601              :     std::vector<const MSLane*> lanes(visited.size(), nullptr);
    1602              :     for (auto item : visited) {
    1603              :         lanes[item.second] = item.first;
    1604              :     }
    1605              :     for (auto it = lanes.begin(); it != lanes.end();) {
    1606              :         if (*it == nullptr) {
    1607              :             it = lanes.erase(it);
    1608              :         } else {
    1609              :             it++;
    1610              :         }
    1611              :     }
    1612              :     return toString(lanes);
    1613              :     */
    1614            0 :     return "dummy";
    1615              : }
    1616              : 
    1617              : 
    1618              : void
    1619       133156 : MSDriveWay::appendMapIndex(LaneVisitedMap& map, const MSLane* lane) {
    1620              :     // avoid undefined behavior from evaluation order
    1621       133156 :     const int tmp = (int)map.size();
    1622       133156 :     map[lane] = tmp;
    1623       133156 : }
    1624              : 
    1625              : bool
    1626     16014070 : MSDriveWay::match(MSRouteIterator firstIt, MSRouteIterator endIt) const {
    1627              :     // @todo optimize: it is sufficient to check for specific edges (after each switch)
    1628     16014070 :     auto itRoute = firstIt;
    1629              :     auto itDwRoute = myRoute.begin();
    1630              :     bool match = true;
    1631    112431384 :     while (itRoute != endIt && itDwRoute != myRoute.end()) {
    1632     96615195 :         if (*itRoute != *itDwRoute) {
    1633              :             match = false;
    1634              : #ifdef DEBUG_MATCH
    1635              :             std::cout << "  check dw=" << getID() << " match failed at vehEdge=" << (*itRoute)->getID() << " dwEdge=" << (*itDwRoute)->getID() << "\n";
    1636              : #endif
    1637              :             break;
    1638              :         }
    1639              :         itRoute++;
    1640              :         itDwRoute++;
    1641              :     }
    1642              :     // if the vehicle arrives before the end of this driveway,
    1643              :     // we'd rather build a new driveway to avoid superfluous restrictions
    1644     15816189 :     if (match && itDwRoute == myRoute.end()
    1645     31806811 :             && (itRoute == endIt || myAbortedBuild || myBidiEnded || myFoundJump || isSubDriveWay())) {
    1646              :         //std::cout << "  using dw=" << "\n";
    1647     15780135 :         if (itRoute != endIt) {
    1648              :             // check whether the current route requires an extended driveway
    1649       145505 :             const MSEdge* next = *itRoute;
    1650       145505 :             const MSEdge* prev = myRoute.back();
    1651          421 :             if (myFoundJump && prev->getBidiEdge() != next && prev->getBidiEdge() != nullptr
    1652       145766 :                     && prev->isConnectedTo(*next, (SUMOVehicleClass)(SVC_RAIL_CLASSES & prev->getPermissions()))) {
    1653              : #ifdef DEBUG_MATCH
    1654              :                 std::cout << "  check dw=" << getID() << " prev=" << prev->getID() << " next=" << next->getID() << "\n";
    1655              : #endif
    1656              :                 return false;
    1657              :             }
    1658       145483 :             if (!myFoundJump && prev->getBidiEdge() == next && prev == &myForward.back()->getEdge()) {
    1659              :                 assert(isSubDriveWay() || myBidiEnded);
    1660              :                 // must not leave driveway via reversal
    1661              : #ifdef DEBUG_MATCH
    1662              :                 std::cout << getID() << " back=" << myForward.back()->getID() << " noMatch route " << toString(ConstMSEdgeVector(firstIt, endIt)) << "\n";
    1663              : #endif
    1664              :                 return false;
    1665              :             }
    1666       145477 :             if (myForward.back()->isInternal() && myForward.back()->getNextNormal() != (*itRoute)) {
    1667              :                 // driveway is part of a direct-control conflict and continues elsewhere
    1668              : #ifdef DEBUG_MATCH
    1669              :                 std::cout << getID() << " back=" << myForward.back()->getID() << " noMatch route " << toString(ConstMSEdgeVector(firstIt, itRoute)) << " (direct control)\n";
    1670              : #endif
    1671              :                 return false;
    1672              :             }
    1673              :         }
    1674     15780105 :         return !isSubDriveWay() || myParent->match(firstIt, endIt);
    1675              :     }
    1676              :     return false;
    1677              : }
    1678              : 
    1679              : void
    1680        12575 : MSDriveWay::addFoes(const MSLink* link) {
    1681              : #ifdef DEBUG_ADD_FOES
    1682              :     if (gDebugFlag4) std::cout << "driveway " << myID << " addFoes for link " << link->getDescription() << "\n";
    1683              : #endif
    1684        12575 :     const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(link->getTLLogic());
    1685        12575 :     if (rs != nullptr) {
    1686        17426 :         for (MSDriveWay* foe : rs->retrieveDriveWays(link->getTLIndex())) {
    1687              : #ifdef DEBUG_ADD_FOES
    1688              :             if (gDebugFlag4) std::cout << "  cand foe=" << foe->myID << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this) << " cc1=" << crossingConflict(*foe) << " cc2=" <<  foe->crossingConflict(*this) << "\n";
    1689              : #endif
    1690         4851 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1691              : #ifdef DEBUG_ADD_FOES
    1692              :                 if (gDebugFlag4) std::cout << "   foe=" << foe->myID << "\n";
    1693              : #endif
    1694         3764 :                 myFoes.push_back(foe);
    1695              :             }
    1696        12575 :         }
    1697              :     }
    1698        12575 : }
    1699              : 
    1700              : 
    1701              : void
    1702        18412 : MSDriveWay::addBidiFoes(const MSRailSignal* ownSignal, bool extended) {
    1703              : #ifdef DEBUG_ADD_FOES
    1704              :     if (gDebugFlag4) std::cout << "driveway " << myID << " addBidiFoes extended=" << extended << "\n";
    1705              : #endif
    1706        18412 :     const std::vector<const MSLane*>& bidiLanes = extended ? myBidiExtended : myBidi;
    1707       102480 :     for (const MSLane* bidi : bidiLanes) {
    1708       171620 :         for (auto ili : bidi->getIncomingLanes()) {
    1709        87552 :             const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(ili.viaLink->getTLLogic());
    1710        87552 :             if (rs != nullptr && rs != ownSignal &&
    1711        87552 :                     std::find(bidiLanes.begin(), bidiLanes.end(), ili.lane) != bidiLanes.end()) {
    1712         4917 :                 addFoes(ili.viaLink);
    1713              :             }
    1714              :         }
    1715        84068 :         const MSEdge* bidiEdge = &bidi->getEdge();
    1716              :         if (myDepartureDriveways.count(bidiEdge) != 0) {
    1717         3751 :             for (MSDriveWay* foe : myDepartureDriveways[bidiEdge]) {
    1718         1685 :                 if (flankConflict(*foe)) {
    1719              : #ifdef DEBUG_ADD_FOES
    1720              :                     if (gDebugFlag4) std::cout << "  foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << "\n";
    1721              : #endif
    1722         1073 :                     myFoes.push_back(foe);
    1723              :                 } else {
    1724              : #ifdef DEBUG_ADD_FOES
    1725              :                     if (gDebugFlag4) std::cout << "  cand foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << " rejected\n";
    1726              : #endif
    1727              :                 }
    1728              :             }
    1729              :         }
    1730              :         if (myDepartureDrivewaysEnds.count(bidiEdge) != 0) {
    1731         3112 :             for (MSDriveWay* foe : myDepartureDrivewaysEnds[bidiEdge]) {
    1732         1601 :                 if (flankConflict(*foe)) {
    1733              : #ifdef DEBUG_ADD_FOES
    1734              :                     if (gDebugFlag4) std::cout << "  foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << "\n";
    1735              : #endif
    1736         1051 :                     myFoes.push_back(foe);
    1737              :                 } else {
    1738              : #ifdef DEBUG_ADD_FOES
    1739              :                     if (gDebugFlag4) std::cout << "  cand foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << " rejected\n";
    1740              : #endif
    1741              :                 }
    1742              :             }
    1743              :         }
    1744              :     }
    1745        18412 : }
    1746              : 
    1747              : 
    1748              : void
    1749         8892 : MSDriveWay::addParallelFoes(const MSLink* link, const MSEdge* first) {
    1750              : #ifdef DEBUG_ADD_FOES
    1751              :     if (gDebugFlag4) std::cout << "driveway " << myID << " addParallelFoes\n";
    1752              : #endif
    1753         8892 :     if (link) {
    1754         5324 :         addFoes(link);
    1755              :     } else {
    1756              :         auto it = myDepartureDriveways.find(first);
    1757         3568 :         if (it != myDepartureDriveways.end()) {
    1758         3791 :             for (MSDriveWay* foe : it->second) {
    1759              : #ifdef DEBUG_ADD_FOES
    1760              :                 if (gDebugFlag4) std::cout << "  foe " << foe->getID() << " departs on first=" << first->getID() << "\n";
    1761              : #endif
    1762          262 :                 myFoes.push_back(foe);
    1763              :             }
    1764              :         }
    1765              :     }
    1766         8892 : }
    1767              : 
    1768              : 
    1769              : void
    1770         9206 : MSDriveWay::addReversalFoes(bool movingBlock) {
    1771              : #ifdef DEBUG_ADD_FOES
    1772              :     if (gDebugFlag4) std::cout << "driveway " << myID << " addReversalFoes\n";
    1773              : #endif
    1774              :     std::set<const MSEdge*> forward;
    1775        45782 :     for (const MSLane* lane : myForward) {
    1776        36576 :         if (lane->isNormal()) {
    1777        21503 :             forward.insert(&lane->getEdge());
    1778              :         }
    1779              :     }
    1780              :     int i = 0;
    1781        77903 :     for (const MSEdge* e : myRoute) {
    1782        21513 :         if (forward.count(e) != 0 && !movingBlock) {
    1783              :             // reversals in our own forward can be ignored because each driveway
    1784              :             // is automatically a foe of itself by default
    1785              :             continue;
    1786              :         }
    1787        48843 :         if (i == myCoreSize) {
    1788              :             break;
    1789              :         }
    1790        48843 :         i++;
    1791              :         auto it = myReversalDriveWays.find(e);
    1792        48843 :         if (it != myReversalDriveWays.end()) {
    1793         6716 :             for (MSDriveWay* foe : it->second) {
    1794              :                 // check whether the foe reverses into our own forward section
    1795              :                 // (it might reverse again or disappear via arrival)
    1796              : #ifdef DEBUG_ADD_FOES
    1797              :                 //std::cout << "  candidate foe " << foe->getID() << " reverses on edge=" << e->getID() << " forward=" << joinNamedToString(forward, " ") << " foeRoute=" << toString(foe->myRoute) << "\n";
    1798              : #endif
    1799        11752 :                 if (forwardRouteConflict(forward, *foe)) {
    1800              :                     std::set<const MSEdge*> foeForward;
    1801         1751 :                     for (const MSLane* lane : foe->myForward) {
    1802         1521 :                         if (lane->isNormal()) {
    1803          782 :                             foeForward.insert(&lane->getEdge());
    1804          782 :                             if (lane->getBidiLane() != nullptr) {
    1805          771 :                                 foeForward.insert(lane->getEdge().getBidiEdge());
    1806              :                             }
    1807              :                         }
    1808              :                     }
    1809              : #ifdef DEBUG_ADD_FOES
    1810              :                     if (gDebugFlag4) std::cout << "  reversal cand=" << foe->getID() << " foeForward " << toString(foeForward) << "\n";
    1811              : #endif
    1812          460 :                     if (foe->forwardRouteConflict(foeForward, *this, true)) {
    1813              : #ifdef DEBUG_ADD_FOES
    1814              :                         if (gDebugFlag4) std::cout << "  foe " << foe->getID() << " reverses on edge=" << e->getID() << "\n";
    1815              : #endif
    1816          190 :                         myFoes.push_back(foe);
    1817              :                     }
    1818         5646 :                 } else if (movingBlock && foe == this) {
    1819              : #ifdef DEBUG_ADD_FOES
    1820              :                     if (gDebugFlag4) std::cout << "  dw " << getID() << " reverses on forward edge=" << e->getID() << " (movingBlock)\n";
    1821              : #endif
    1822           25 :                     myFoes.push_back(foe);
    1823              :                 }
    1824              :             }
    1825              :         }
    1826              :     }
    1827         9206 : }
    1828              : 
    1829              : 
    1830              : bool
    1831         4421 : MSDriveWay::buildSubFoe(MSDriveWay* foe, bool movingBlock) {
    1832              :     // Subdriveways (Teilfahrstraße) model the resolution of a driving conflict
    1833              :     // before a vehicle has left the driveway. This is possible when the driveway diverges from the foe
    1834              :     // driveway at an earlier point (switch or crossing).
    1835              :     //
    1836              :     // We already know that the last edge of this driveway doesn't impact the foe (unless the driveway ends within the block).
    1837              :     // Remove further edges from the end of the driveway (myForward) until the point of conflict is found.
    1838              :     //
    1839              :     // For movingBlock the logic is changed:
    1840              :     // We remove the conflict-free part as before but then keep removing the conflict part until another non-conconflit part is found
    1841         4421 :     if (myForward.size() < foe->myForward.size() &&
    1842         4421 :             myForward == std::vector<const MSLane*>(foe->myForward.begin(), foe->myForward.begin() + myForward.size())) {
    1843              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1844              :         if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " is subpart of foe=" << foe->getID() << "\n";
    1845              : #endif
    1846           80 :         foe->myFoes.push_back(this);
    1847           80 :         return true;
    1848              :     }
    1849         4341 :     int subLast = (int)myForward.size() - 2;
    1850         4341 :     if (movingBlock && myForward.back() == foe->myForward.back()) {
    1851           71 :         subLast++;
    1852              :     }
    1853              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1854              :     if (subLast < 0) {
    1855              :         if (gDebugFlag4) std::cout << "  " << getID() << " cannot build subDriveWay for foe " << foe->getID() << " because myForward has only a single lane\n";
    1856              :     }
    1857              : #endif
    1858              :     bool foundConflict = false;
    1859              :     bool flankC = false;
    1860              :     bool zipperC = false;
    1861              :     bool crossC = false;
    1862        15178 :     while (subLast >= 0) {
    1863        15012 :         const MSLane* lane = myForward[subLast];
    1864        15012 :         const MSLink* tmpOrigin = subLast > 0 ? myForward[subLast - 1]->getLinkTo(lane) : myOrigin;
    1865        15012 :         MSDriveWay tmp(tmpOrigin, "tmp", true);
    1866        15012 :         tmp.myForward.push_back(lane);
    1867        15012 :         tmp.myBidi = myBidi;
    1868        15012 :         tmp.myBidiExtended = myBidiExtended;
    1869        15012 :         tmp.myRoute.push_back(lane->getNextNormal());
    1870        15012 :         tmp.myCoreSize = 1;
    1871        15012 :         flankC = tmp.flankConflict(*foe);
    1872        15012 :         const bool bidiConflict = std::find(foe->myBidi.begin(), foe->myBidi.end(), lane) != foe->myBidi.end();
    1873        15012 :         crossC = tmp.crossingConflict(*foe);
    1874              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1875              :         if (gDebugFlag4) std::cout << "  subLast=" << subLast << " lane=" << lane->getID() << " fc=" << flankC << " cc=" << crossC << " bc=" << bidiConflict << "\n";
    1876              : #endif
    1877        15012 :         if (flankC || crossC || bidiConflict) {
    1878              :             foundConflict = true;
    1879         4483 :             if (!movingBlock || bidiConflict) {
    1880              :                 break;
    1881              :             }
    1882          358 :             if (((flankC && lane->getFromJunction()->getType() == SumoXMLNodeType::ZIPPER)
    1883           46 :                     || (!flankC && lane->getToJunction()->getType() == SumoXMLNodeType::ZIPPER))
    1884          431 :                     && (isDepartDriveway()
    1885           15 :                         || getForwardDistance(flankC ? subLast - 1 : subLast) > myMovingBlockMaxDist)) {
    1886              :                 zipperC = true;
    1887              :                 foundConflict = false;
    1888              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1889              :                 if (gDebugFlag4) std::cout << "     ignored movingBlock zipperConflict\n";
    1890              : #endif
    1891           18 :                 if (!flankC && crossC) {
    1892              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1893              :                     if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " movingBlock-save\n";
    1894              : #endif
    1895              :                     return false;
    1896              :                 }
    1897              :             }
    1898          395 :             if (!flankC && crossC) {
    1899              :                 break;
    1900              :             }
    1901        10529 :         } else if (foundConflict) {
    1902              :             break;
    1903              :         }
    1904        10837 :         subLast--;
    1905        15012 :     }
    1906              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1907              :     if (gDebugFlag4) std::cout << "  subLastFinal=" << subLast << " movingBlock=" << movingBlock << " zipperC=" << zipperC << "\n";
    1908              : #endif
    1909         4332 :     if (bidiBlockedByEnd(*foe) && bidiBlockedBy(*this) && foe->forwardEndOnRoute(this)) {
    1910              :         //std::set<const MSEdge*> firstEdge;
    1911              :         //firstEdge.insert(foe->myRoute.front());
    1912              : 
    1913          248 :         ConstMSEdgeVector forward(myRoute.begin(), myRoute.begin() + myForwardEdgeCount);
    1914              :         ConstMSEdgeVector foeAfterForward(foe->myRoute.begin() + foe->myForwardEdgeCount,
    1915          248 :                 foe->myRoute.begin() + MIN2(foe->myRoute.size(), foe->myForwardEdgeCount + forward.size()));
    1916              :         // @todo the check for forwardRouteConflict correctly reduces waiting in
    1917              :         // test rail/reversal/consecutive_before_reversal but creates deadlock in
    1918              :         // test rail/reversal/reversal_onRoute_beyond_core3b
    1919          248 :         if (forward == foeAfterForward /*&& forwardRouteConflict(firstEdge, *this, true)*/) {
    1920          126 :             foe->myFoes.push_back(this);
    1921              :             // foe will get the sidings
    1922          126 :             addSidings(foe, true);
    1923              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1924              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " special case 1\n";
    1925              : #endif
    1926              :             return true;
    1927              :         }
    1928          248 :     }
    1929         4206 :     if (subLast < 0) {
    1930          136 :         if (movingBlock && zipperC) {
    1931              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1932              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " movingBlock-save\n";
    1933              : #endif
    1934              :             return false;
    1935          136 :         } else if (&myForward.back()->getEdge() == myRoute.back() && foe->forwardEndOnRoute(this)) {
    1936              :             // driveway ends in the middle of the block and only the final edge overlaps with the foe driveWay
    1937           15 :             foe->myFoes.push_back(this);
    1938              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1939              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " foe endsOnForward\n";
    1940              : #endif
    1941          121 :         } else if (foe->myTerminateRoute) {
    1942          121 :             if (bidiBlockedByEnd(*foe) && bidiBlockedBy(*this) && foe->forwardEndOnRoute(this)) {
    1943            5 :                 foe->myFoes.push_back(this);
    1944              :                 // foe will get the sidings
    1945            5 :                 addSidings(foe, true);
    1946              :             }
    1947              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1948              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " terminates\n";
    1949              : #endif
    1950            0 :         } else if (myTerminateRoute && myBidi.size() <= myForward.size()) {
    1951            0 :             foe->myFoes.push_back(this);
    1952              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1953              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " terminates, foe=" << foe->getID() << "\n";
    1954              : #endif
    1955            0 :             return true;
    1956              :         } else if (foe->myReversals.size() % 2 == 1) {
    1957              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1958              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " has " << foe->myReversals.size() << " reversals\n";
    1959              : #endif
    1960              :         } else {
    1961              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1962              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " failed\n";
    1963              : #endif
    1964              : #ifdef SUBDRIVEWAY_WARN_NOCONFLICT
    1965              :             WRITE_WARNINGF("No point of conflict found between driveway '%' and driveway '%' when creating sub-driveway", getID(), foe->getID());
    1966              : #endif
    1967              :         }
    1968          136 :         return false;
    1969              :     }
    1970         4070 :     int subSize = subLast + 1;
    1971         5001 :     for (MSDriveWay* cand : mySubDriveWays) {
    1972         2408 :         if ((int)cand->myForward.size() == subSize) {
    1973              :             // can re-use existing sub-driveway
    1974         1477 :             foe->myFoes.push_back(cand);
    1975         1477 :             if (foe->bidiBlockedByEnd(*cand)) {
    1976         1342 :                 foe->addSidings(cand);
    1977              :             }
    1978         1477 :             cand->myFoes.push_back(foe);
    1979              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1980              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " useExisting=" << cand->getID() << "\n";
    1981              : #endif
    1982         1477 :             return true;
    1983              :         }
    1984              :     }
    1985         2593 :     std::vector<const MSLane*> forward(myForward.begin(), myForward.begin() + subSize);
    1986              :     std::vector<const MSEdge*> route;
    1987        14876 :     for (const MSLane* lane : forward) {
    1988        12283 :         if (lane->isNormal()) {
    1989         6079 :             route.push_back(&lane->getEdge());
    1990              :         }
    1991              :     }
    1992         2593 :     if (route.empty()) {
    1993           59 :         if (subSize == 1 && crossC
    1994           56 :                 && forward.front()->getFromJunction() == foe->myForward.front()->getFromJunction()
    1995          115 :                 && forward.front()->getFromJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1996              :             assert(myForward.front()->isInternal());
    1997              :             // sub-driveway ends after a single internal lane but since the route cannot be empty we add the next edge
    1998           56 :             route.push_back(foe->myForward.front()->getEdge().getNormalSuccessor());
    1999              :         }  else {
    2000              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2001              :             if (gDebugFlag4) std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID() << " empty subRoute\n";
    2002              : #endif
    2003            3 :             return false;
    2004              :         }
    2005              :     }
    2006         2590 :     if (myRoute.size() > route.size()) {
    2007              :         // route continues. make sure the subDriveway does not end with a reversal
    2008         2578 :         const MSEdge* lastNormal = route.back();
    2009         2578 :         const MSEdge* nextNormal = myRoute[route.size()];
    2010         2578 :         if (lastNormal->getBidiEdge() == nextNormal) {
    2011              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2012              :             if (gDebugFlag4) std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID()
    2013              :                       << " lastNormal=" << lastNormal->getID() << " nextNormal=" << nextNormal->getID() << " endWithReversal\n";
    2014              : #endif
    2015              :             return false;
    2016              :         }
    2017              :     }
    2018         5140 :     MSDriveWay* sub = new MSDriveWay(myOrigin, getID() + "." + toString(mySubDriveWays.size()));
    2019         2570 :     sub->myLane = myLane;
    2020         2570 :     sub->myParent = this;
    2021         2570 :     sub->myForward = forward;
    2022         2570 :     sub->myRoute = route;
    2023         2570 :     sub->myCoreSize = (int)sub->myRoute.size();
    2024         2570 :     myLane->addMoveReminder(sub, false);
    2025              : 
    2026              :     // copy trains that are currently on this driveway (and associated entry events)
    2027         2741 :     for (SUMOVehicle* veh : myTrains) {
    2028          171 :         auto itOnSub = std::find(sub->myRoute.begin(), sub->myRoute.end(), veh->getEdge());
    2029          171 :         if (itOnSub != sub->myRoute.end()) {
    2030              :             sub->myTrains.insert(veh);
    2031              :             // non-zero is enough to avoid superfluous activation via activateReminders (and removal)
    2032          147 :             const double pos = sub->myRoute.front()->getLength();
    2033          147 :             dynamic_cast<MSBaseVehicle*>(veh)->addReminder(sub, pos);
    2034          167 :             for (const VehicleEvent& ve : myVehicleEvents) {
    2035           20 :                 if (ve.id == veh->getID()) {
    2036           20 :                     sub->myVehicleEvents.push_back(ve);
    2037              :                 }
    2038              :             }
    2039              :         }
    2040              :     }
    2041              : 
    2042         2570 :     foe->myFoes.push_back(sub);
    2043         2570 :     if (foe->bidiBlockedByEnd(*sub)) {
    2044         1766 :         foe->addSidings(sub);
    2045              :     }
    2046         2570 :     sub->myFoes.push_back(foe);
    2047         2570 :     mySubDriveWays.push_back(sub);
    2048              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2049              :     if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " sub=" << sub->getID() << " route=" << toString(sub->myRoute) << "\n";
    2050              : #endif
    2051              :     return true;
    2052         2593 : }
    2053              : 
    2054              : 
    2055              : double
    2056           15 : MSDriveWay::getForwardDistance(int lastIndex) const {
    2057              :     assert(lastIndex < (int)myForward.size());
    2058              :     double result = 0;
    2059           57 :     for (int i = 0; i <= lastIndex; i++) {
    2060           42 :         result += myForward[i]->getLength();
    2061              :     }
    2062           15 :     return result;
    2063              : }
    2064              : 
    2065              : 
    2066              : void
    2067         7861 : MSDriveWay::addSidings(MSDriveWay* foe, bool addToFoe) {
    2068         7861 :     const ConstMSEdgeVector& foeRoute = foe->isSubDriveWay() ? foe->myParent->myRoute : foe->myRoute;
    2069         7861 :     const MSEdge* foeEndBidi = foe->myForward.back()->getEdge().getBidiEdge();
    2070              :     int foeForwardNormals = 0;
    2071        41672 :     for (auto lane : foe->myForward) {
    2072        33811 :         if (lane->isNormal()) {
    2073        17982 :             foeForwardNormals++;
    2074              :         }
    2075              :     }
    2076         7861 :     if (foeForwardNormals == (int)foeRoute.size()) {
    2077              : #ifdef DEBUG_BUILD_SIDINGS
    2078              :         if (gDebugFlag4) std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " foeForwardNormals=" << foeForwardNormals << " frSize=" << foeRoute.size() <<  " aborted\n";
    2079              : #endif
    2080         1027 :         return;
    2081              :     }
    2082              :     auto foeSearchBeg = foeRoute.begin() + foeForwardNormals;
    2083              :     auto foeSearchEnd = foeRoute.end();
    2084         6984 :     if (foeEndBidi == nullptr) {
    2085            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " noBidi\n");
    2086              :     }
    2087              :     // if foe is a subDriveway, the forward section may end on an internal edge which would not be found on myRoute
    2088         6984 :     foeEndBidi = foeEndBidi->getNormalSuccessor();
    2089         6984 :     std::set<const MSEdge*> foeForwardEdges(foeRoute.begin(), foeRoute.begin() + foeForwardNormals);
    2090              :     int forwardNormals = 0;
    2091        48279 :     for (auto lane : myForward) {
    2092        41445 :         if (lane->isNormal()) {
    2093        22091 :             forwardNormals++;
    2094              :             if (foeForwardEdges.count(&lane->getEdge()) != 0) {
    2095              : #ifdef DEBUG_BUILD_SIDINGS
    2096              :                 if (gDebugFlag4) std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " forwardEdge=" << lane->getEdge().getID() << " on foeForward (sidings unsafe)\n";
    2097              : #endif
    2098              :                 return;
    2099              :             }
    2100              :         }
    2101              :     }
    2102              :     int i;
    2103              :     std::vector<int> start;
    2104              :     std::vector<double> length;
    2105              :     std::vector<std::vector<int> > intermediateRS;
    2106        40492 :     for (i = 0; i < (int)myRoute.size(); i++) {
    2107        40492 :         if (myRoute[i] == foeEndBidi) {
    2108              :             break;
    2109              :         }
    2110              :     }
    2111         6834 :     if (i == (int)myRoute.size()) {
    2112            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " foeEndBidi=" + foeEndBidi->getID() + " not on route\n");
    2113              :     }
    2114         6834 :     const MSEdge* next = myRoute[i];
    2115              : #ifdef DEBUG_BUILD_SIDINGS
    2116              :     if (gDebugFlag4) std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " next=" << next->getID() << " foeForwardNormals=" << foeForwardNormals << " frSize=" << foeRoute.size() << " foeSearchBeg=" << (*foeSearchBeg)->getID() << "\n";
    2117              : #endif
    2118         6834 :     i--;
    2119              :     // look backward along our route starting at the final edge of the foe dw
    2120        40492 :     for (; i >= 0; i--) {
    2121        33658 :         const MSEdge* cur = myRoute[i];
    2122        33658 :         const bool curHasRS = hasRS(cur, next);
    2123        33658 :         if (curHasRS) {
    2124        11495 :             if (std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge()) == foeSearchEnd) {
    2125              :                 // we found a rail signal in a safe spot (not on the foe route)
    2126         7923 :                 start.push_back(i);
    2127         7923 :                 length.push_back(0);
    2128         7923 :                 intermediateRS.push_back({});
    2129              :             }
    2130              :         }
    2131        33658 :         if (!start.empty()) {
    2132              :             // move further backwards along the route to find the spot where it merges again with the foe route
    2133        12718 :             auto itFind = std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge());
    2134        12718 :             if (itFind != foeSearchEnd) {
    2135              : #ifdef DEBUG_BUILD_SIDINGS
    2136              :                 if (gDebugFlag4) std::cout << "endSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " curBidi=" << Named::getIDSecure(cur->getBidiEdge()) << " length=" << toString(length) << "\n";
    2137              : #endif
    2138         2144 :                 const int firstIndex = i + 1;
    2139         2144 :                 if (addToFoe) {
    2140          131 :                     auto& foeSidings = foe->mySidings[this];
    2141              :                     // indices must be mapped onto foe route;
    2142          131 :                     const MSEdge* first = myRoute[firstIndex];
    2143          131 :                     auto itFirst = std::find(foeRoute.begin(), foeRoute.end(), first);
    2144          131 :                     if (itFirst != foeRoute.end()) {
    2145          473 :                         for (int j = 0; j < (int)length.size(); j++) {
    2146          342 :                             const MSEdge* last = myRoute[start[j]];
    2147          342 :                             auto itLast = std::find(itFirst, foeRoute.end(), last);
    2148          342 :                             if (itLast != foeRoute.end()) {
    2149              :                                 // @todo are intermediateRS relevant here?
    2150          684 :                                 foeSidings.insert(foeSidings.begin(), Siding((int)(itFirst - foeRoute.begin()), (int)(itLast - foeRoute.begin()), length[j], {}));
    2151          342 :                                 if (foeSidings.size() > 2) {
    2152              :                                     foeSidings.pop_back();  // only need the first 2 sidings
    2153              :                                 }
    2154              :                             }
    2155              :                         }
    2156              :                     }
    2157              :                 } else {
    2158              :                     // pick up further rail signals between the start of the siding and the start of the current driveway
    2159              :                     std::vector<int> furtherRS;
    2160         2013 :                     if (curHasRS) {
    2161          128 :                         furtherRS.push_back(i);
    2162              :                     }
    2163              :                     const MSEdge* next2 = cur;
    2164         5670 :                     for (int i2 = i - 1; i2 >= forwardNormals; i2--) {
    2165         3657 :                         const MSEdge* cur2 = myRoute[i2];
    2166         3657 :                         if (hasRS(cur2, next2)) {
    2167         1711 :                             furtherRS.push_back(i2);
    2168              :                         }
    2169              :                         next2 = cur2;
    2170              :                     }
    2171         2013 :                     auto& foeSidings = mySidings[foe];
    2172         8896 :                     for (int j = 0; j < (int)length.size(); j++) {
    2173         6883 :                         intermediateRS[j].insert(intermediateRS[j].end(), furtherRS.begin(), furtherRS.end());
    2174        13766 :                         foeSidings.insert(foeSidings.begin(), Siding(firstIndex, start[j], length[j], intermediateRS[j]));
    2175         6883 :                         if (foeSidings.size() > 2) {
    2176              :                             foeSidings.pop_back();  // only need the first 2 sidings
    2177              :                         }
    2178              :                     }
    2179         2013 :                 }
    2180              :                 start.clear();
    2181              :                 length.clear();
    2182              :                 intermediateRS.clear();
    2183         2144 :                 foeSearchBeg = itFind;
    2184              :             } else {
    2185        75447 :                 for (int j = 0; j < (int)length.size(); j++) {
    2186        64873 :                     length[j] += cur->getLength();
    2187        64873 :                     if (curHasRS && i != start[j]) {
    2188        45431 :                         intermediateRS[j].push_back(i);
    2189              :                     }
    2190              :                 }
    2191              :             }
    2192              :         }
    2193              :         next = cur;
    2194              :     }
    2195         6834 : }
    2196              : 
    2197              : 
    2198              : bool
    2199        37315 : MSDriveWay::hasRS(const MSEdge* cur, const MSEdge* next) {
    2200        37315 :     if (cur->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    2201              :         // check if there is a controlled link between cur and next
    2202        25288 :         for (auto lane : cur->getLanes()) {
    2203        25699 :             for (const MSLink* link : lane->getLinkCont()) {
    2204        19658 :                 if (&link->getLane()->getEdge() == next && link->getTLLogic() != nullptr) {
    2205              :                     return true;
    2206              :                 }
    2207              :             }
    2208              :         }
    2209              :     }
    2210              :     return false;
    2211              : }
    2212              : 
    2213              : 
    2214              : bool
    2215          372 : MSDriveWay::forwardEndOnRoute(const MSDriveWay* foe) const {
    2216          372 :     const MSEdge* foeForwardEnd = &foe->myForward.back()->getNormalPredecessorLane()->getEdge();
    2217          372 :     return std::find(myRoute.begin(), myRoute.end(), foeForwardEnd) != myRoute.end();
    2218              : }
    2219              : 
    2220              : void
    2221         5172 : MSDriveWay::addConflictLink(const MSLink* link) {
    2222         5172 :     if (link->getTLLogic() != nullptr) {
    2223              :         // ignore links that originate on myBidi
    2224              :         // and also links from the same junction as my own link
    2225         5172 :         const MSLane* origin = link->getLaneBefore();
    2226         5172 :         if (std::find(myBidi.begin(), myBidi.end(), origin) == myBidi.end()) {
    2227         4150 :             if (link->getJunction() != myRoute.front()->getFromJunction()) {
    2228         2648 :                 if (std::find(myConflictLinks.begin(), myConflictLinks.end(), link) == myConflictLinks.end()) {
    2229         1887 :                     myConflictLinks.push_back(const_cast<MSLink*>(link));
    2230              :                 }
    2231              :             }
    2232              :         }
    2233              :     }
    2234         5172 : }
    2235              : 
    2236              : void
    2237          335 : MSDriveWay::addDWDeadlock(const std::vector<const MSDriveWay*>& deadlockFoes) {
    2238              :     std::set<const MSDriveWay*> filtered;
    2239         1651 :     for (const MSDriveWay* foe : deadlockFoes) {
    2240         1316 :         if (std::find(myFoes.begin(), myFoes.end(), foe) == myFoes.end()) {
    2241              :             filtered.insert(foe);
    2242              :         }
    2243              :     }
    2244          335 :     if (std::find(myDeadlocks.begin(), myDeadlocks.end(), filtered) == myDeadlocks.end()) {
    2245           93 :         myDeadlocks.push_back(filtered);
    2246              :         //std::cout << getID() << " deadlockFoes=" << toString(deadlockFoes) << "\n";
    2247              :     }
    2248          335 : }
    2249              : 
    2250              : const MSDriveWay*
    2251      7555320 : MSDriveWay::getDepartureDriveway(const SUMOVehicle* veh, bool init) {
    2252      7555320 :     const MSEdge* edge = init ? veh->getRoute().getEdges()[veh->getDepartEdge()] : veh->getEdge();
    2253              :     assert(isRailwayOrShared(edge->getPermissions()));
    2254      7555320 :     if (edge->getFromJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    2255         4443 :         for (const MSLane* lane : edge->getLanes()) {
    2256         4796 :             for (auto ili : lane->getIncomingLanes()) {
    2257         2804 :                 const MSLink* entry = ili.viaLink->getCorrespondingEntryLink();
    2258         2804 :                 const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(entry->getTLLogic());
    2259         1873 :                 if (rs != nullptr) {
    2260         1873 :                     const MSDriveWay* dw = &const_cast<MSRailSignal*>(rs)->retrieveDriveWayForVeh(entry->getTLIndex(), veh);
    2261         1873 :                     if (&dw->myForward.front()->getEdge() == edge) {
    2262              :                         return dw;
    2263              :                     }
    2264              :                 }
    2265              :             }
    2266              :         }
    2267              :     }
    2268      7559265 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    2269      7555489 :         auto matchStart = init ? veh->getRoute().begin() + veh->getDepartEdge() : veh->getCurrentRouteEdge();
    2270      7555489 :         if (dw->match(matchStart, veh->getRoute().end())) {
    2271      7551085 :             return dw;
    2272              :         }
    2273              :     }
    2274         7552 :     const std::string id = edge->getFromJunction()->getID() + ".d" + toString(myDepartDrivewayIndex[edge->getFromJunction()]++);
    2275         3776 :     MSDriveWay* dw = buildDriveWay(id, nullptr, veh->getCurrentRouteEdge(), veh->getRoute().end());
    2276         3776 :     myDepartureDriveways[edge].push_back(dw);
    2277         3776 :     myDepartureDrivewaysEnds[&dw->myForward.back()->getEdge()].push_back(dw);
    2278              :     dw->setVehicle(veh->getID());
    2279              :     return dw;
    2280              : }
    2281              : 
    2282              : 
    2283              : void
    2284         1531 : MSDriveWay::writeDepatureBlocks(OutputDevice& od, bool writeVehicles) {
    2285         4134 :     for (auto item  : myDepartureDriveways) {
    2286         2603 :         const MSEdge* edge = item.first;
    2287         2603 :         if (item.second.size() > 0) {
    2288         5206 :             od.openTag("departJunction");
    2289         2603 :             od.writeAttr(SUMO_ATTR_ID, edge->getFromJunction()->getID());
    2290         5509 :             for (const MSDriveWay* dw : item.second) {
    2291         2906 :                 if (writeVehicles) {
    2292          412 :                     dw->writeBlockVehicles(od);
    2293              :                 } else {
    2294         2494 :                     dw->writeBlocks(od);
    2295              :                 }
    2296              :             }
    2297         5206 :             od.closeTag(); // departJunction
    2298              :         }
    2299              :     }
    2300         1531 : }
    2301              : 
    2302              : void
    2303          513 : MSDriveWay::saveState(OutputDevice& out) {
    2304              :     // all driveways are in myEndingDriveways which makes it convenient
    2305          591 :     for (auto item : myEndingDriveways) {
    2306          172 :         for (MSDriveWay* dw : item.second) {
    2307           94 :             dw->_saveState(out);
    2308          112 :             for (MSDriveWay* sub : dw->mySubDriveWays) {
    2309           18 :                 sub->_saveState(out);
    2310              :             }
    2311              :         }
    2312              :     }
    2313          513 : }
    2314              : 
    2315              : void
    2316          112 : MSDriveWay::_saveState(OutputDevice& out) const {
    2317          112 :     if (!myTrains.empty() || haveSubTrains()) {
    2318           83 :         out.openTag(isSubDriveWay() ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
    2319           43 :         out.writeAttr(SUMO_ATTR_ID, getID());
    2320           43 :         out.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
    2321           43 :         if (!myTrains.empty()) {
    2322              :             std::vector<std::string> trainIDs;
    2323           86 :             for (SUMOVehicle* veh : myTrains) {
    2324           43 :                 trainIDs.push_back(veh->getID());
    2325              :             }
    2326           43 :             out.writeAttr(SUMO_ATTR_VEHICLES, toString(trainIDs));
    2327           43 :         }
    2328           86 :         out.closeTag();
    2329              :     }
    2330          112 : }
    2331              : 
    2332              : 
    2333              : bool
    2334           69 : MSDriveWay::haveSubTrains() const {
    2335           76 :     for (MSDriveWay* sub : mySubDriveWays) {
    2336            7 :         if (!sub->myTrains.empty()) {
    2337              :             return true;
    2338              :         }
    2339              :     }
    2340              :     return false;
    2341              : }
    2342              : 
    2343              : void
    2344           43 : MSDriveWay::loadState(const SUMOSAXAttributes& attrs, int tag) {
    2345           43 :     if ((int)myDriveWayRouteLookup.size() < myGlobalDriveWayIndex) {
    2346          106 :         for (auto item : myEndingDriveways) {
    2347          164 :             for (MSDriveWay* dw : item.second) {
    2348           84 :                 myDriveWayRouteLookup[dw->myRoute] = dw;
    2349              :             }
    2350              :         }
    2351              :     }
    2352           43 :     MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
    2353              :     bool ok;
    2354           43 :     const std::string id  = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    2355           43 :     const std::string edges = attrs.get<std::string>(SUMO_ATTR_EDGES, id.c_str(), ok);
    2356              :     ConstMSEdgeVector route;
    2357           43 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
    2358           43 :         MSEdge::parseEdgesList(edges, route, id);
    2359              :     }
    2360              :     // missing driveways and subdriveways can be ignored. They may have been created
    2361              :     // for vehicles that are not relevant at state loading time
    2362              :     MSDriveWay* dw = nullptr;
    2363           43 :     if (tag == SUMO_TAG_DRIVEWAY) {
    2364              :         auto it = myDriveWayRouteLookup.find(route);
    2365           40 :         if (it == myDriveWayRouteLookup.end()) {
    2366              :             //WRITE_WARNING(TLF("Unknown driveWay '%' with route '%'", id, edges));
    2367              :             return;
    2368              :         }
    2369           38 :         dw = it->second;
    2370           38 :         myDriveWayLookup[id] = dw;
    2371              :     } else {
    2372            3 :         std::string parentID = id.substr(0, id.rfind('.'));
    2373              :         auto it = myDriveWayLookup.find(parentID);
    2374            3 :         if (it == myDriveWayLookup.end()) {
    2375              :             //WRITE_WARNING(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
    2376              :             return;
    2377              :         }
    2378            3 :         MSDriveWay* parent = it->second;
    2379            3 :         for (MSDriveWay* sub : parent->mySubDriveWays) {
    2380            1 :             if (sub->myRoute == route) {
    2381              :                 dw = sub;
    2382              :                 break;
    2383              :             }
    2384              :         }
    2385            3 :         if (dw == nullptr) {
    2386              :             // missing subdriveways can be ignored. They may have been created
    2387              :             // as foes for driveways that are not relevant at state loading time
    2388              :             return;
    2389              :         }
    2390              :     }
    2391           78 :     const std::string vehicles = attrs.getOpt<std::string>(SUMO_ATTR_VEHICLES, id.c_str(), ok, "");
    2392          117 :     for (const std::string& vehID : StringTokenizer(vehicles).getVector()) {
    2393           39 :         MSBaseVehicle* veh = dynamic_cast<MSBaseVehicle*>(c.getVehicle(vehID));
    2394           39 :         if (veh == nullptr) {
    2395            0 :             throw ProcessError(TLF("Unknown vehicle '%' in driveway '%'", vehID, id));
    2396              :         }
    2397           39 :         if (!dw->hasTrain(veh)) {
    2398            4 :             dw->myTrains.insert(veh);
    2399            4 :             veh->addReminder(dw);
    2400              :         }
    2401           39 :     }
    2402           43 : }
    2403              : 
    2404              : const MSDriveWay*
    2405            0 : MSDriveWay::retrieveDepartDriveWay(const MSEdge* edge, const std::string& id) {
    2406            0 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    2407            0 :         if (dw->getID() == id) {
    2408              :             return dw;
    2409              :         }
    2410              :     }
    2411              :     return nullptr;
    2412              : }
    2413              : 
    2414              : 
    2415              : bool
    2416         1561 : MSDriveWay::hasTrain(SUMOVehicle* veh) const {
    2417         1561 :     return myTrains.count(veh) != 0;
    2418              : }
    2419              : 
    2420              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1