LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSDriveWay.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 96.5 % 866 836
Test Date: 2026-03-02 16:00:03 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        41242 : MSDriveWay::init() {
      81        41242 :     myWriteVehicles = OptionsCont::getOptions().isSet("railsignal-vehicle-output");
      82        41242 :     myMovingBlockMaxDist = OptionsCont::getOptions().getFloat("railsignal.moving-block.max-dist");
      83        41242 : }
      84              : 
      85              : // ===========================================================================
      86              : // MSDriveWay method definitions
      87              : // ===========================================================================
      88              : 
      89              : 
      90        22565 : MSDriveWay::MSDriveWay(const MSLink* origin, const std::string& id, bool temporary) :
      91        45130 :     MSMoveReminder("DriveWay_" + (temporary ? "tmp" : id)),
      92              :     Named(id),
      93        10308 :     myNumericalID(temporary ? -1 : myGlobalDriveWayIndex++),
      94        22565 :     myOrigin(origin),
      95        22565 :     myActive(nullptr),
      96        22565 :     myCoreSize(0),
      97        22565 :     myForwardEdgeCount(0),
      98        22565 :     myFoundSignal(false),
      99        22565 :     myFoundJump(false),
     100        22565 :     myTerminateRoute(false),
     101        22565 :     myAbortedBuild(false),
     102        22565 :     myBidiEnded(false),
     103        78003 :     myIsSubDriveway(false)
     104        22565 : {}
     105              : 
     106              : 
     107        32873 : MSDriveWay::~MSDriveWay() {
     108        24737 :     for (const MSDriveWay* sub : mySubDriveWays) {
     109         2172 :         delete sub;
     110              :     }
     111              :     mySubDriveWays.clear();
     112       100568 : }
     113              : 
     114              : void
     115        40907 : MSDriveWay::cleanup() {
     116        40907 :     myGlobalDriveWayIndex = 0;
     117              :     myBlockLengthWarnings.clear();
     118        40907 :     myWriteVehicles = false;
     119              : 
     120        43777 :     for (auto item : myDepartureDriveways) {
     121         5962 :         for (MSDriveWay* dw : item.second) {
     122         3092 :             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        40907 : }
     132              : 
     133              : void
     134          186 : MSDriveWay::clearState() {
     135          211 :     for (auto item : myEndingDriveways) {
     136           50 :         for (MSDriveWay* dw : item.second) {
     137              :             dw->myTrains.clear();
     138              :         }
     139              :     }
     140          186 : }
     141              : 
     142              : 
     143              : bool
     144        33141 : 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.getEdge()->getID() : Named::getIDSecure(enteredLane)) << " reason=" << reason << "\n";
     147              : #endif
     148        66282 :     if (veh.isVehicle() && (enteredLane == myLane || (MSGlobals::gUseMesoSim && veh.getEdge() == &myLane->getEdge()))
     149        64881 :             && (reason == NOTIFICATION_DEPARTED || reason == NOTIFICATION_JUNCTION || reason == NOTIFICATION_PARKING)) {
     150        31654 :         SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     151        31654 :         MSRouteIterator firstIt = std::find(sveh.getCurrentRouteEdge(), sveh.getRoute().end(), myLane->getNextNormal());
     152        31654 :         if (match(firstIt, sveh.getRoute().end())) {
     153        19527 :             if (myTrains.count(&sveh) == 0) {
     154        19483 :                 enterDriveWay(sveh, reason);
     155              :             }
     156        19505 :             return true;
     157              :         }
     158         1487 :     } else if (reason == NOTIFICATION_REROUTE) {
     159              :         assert(veh.isVehicle());
     160         1431 :         SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     161              :         assert(myTrains.count(&sveh) == 0);
     162         1431 :         int movedPast = matchesPastRoute(sveh);
     163              :         // vehicle must still be one the drivway
     164         1431 :         if (movedPast >= 0 && movedPast < myForwardEdgeCount) {
     165          719 :             enterDriveWay(sveh, reason);
     166          719 :             return true;
     167              :         }
     168              :     }
     169              :     return false;
     170              : }
     171              : 
     172              : 
     173              : bool
     174       111456 : MSDriveWay::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/, Notification reason, const MSLane* enteredLane) {
     175              :     UNUSED_PARAMETER(enteredLane);
     176              : #ifdef DEBUG_MOVEREMINDER
     177              :     std::cout << SIMTIME << " notifyLeave " << getDescription() << " veh=" << veh.getID() << " lane=" << Named::getIDSecure(enteredLane) << " reason=" << toString(reason) << "\n";
     178              : #endif
     179       111456 :     if (veh.isVehicle()) {
     180              :         // leaving network with departure, teleport etc
     181       111456 :         if (reason != MSMoveReminder::NOTIFICATION_JUNCTION && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     182         6100 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     183         6100 :             if (myWriteVehicles) {
     184          572 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     185              :             }
     186         6100 :             return false;
     187       105356 :         } else if (MSGlobals::gUseMesoSim && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     188              :             // notifyLeave is called before moving the route iterator
     189         7219 :             const MSLane* leftLane = (*(dynamic_cast<SUMOVehicle&>(veh).getCurrentRouteEdge()))->getLanes().front();
     190         7219 :             return notifyLeaveBack(veh, reason, leftLane);
     191              :         } else {
     192              :             return true;
     193              :         }
     194              :     } else {
     195              :         return false;
     196              :     }
     197              : }
     198              : 
     199              : 
     200              : bool
     201        76976 : MSDriveWay::notifyLeaveBack(SUMOTrafficObject& veh, Notification reason, const MSLane* leftLane) {
     202              : #ifdef DEBUG_MOVEREMINDER
     203              :     std::cout << SIMTIME << " notifyLeaveBack " << getDescription() << " veh=" << veh.getID() << " lane=" << Named::getIDSecure(leftLane) << " reason=" << toString(reason) << "\n";
     204              : #endif
     205        76976 :     if (veh.isVehicle()) {
     206        76976 :         if (leftLane == myForward.back() && (veh.getBackLane() != leftLane->getBidiLane() || MSGlobals::gUseMesoSim)) {
     207        13447 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     208        13447 :             if (myWriteVehicles) {
     209         1234 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     210              :             }
     211        13447 :             return false;
     212              :         } else {
     213        63529 :             return true;
     214              :         }
     215              :     } else {
     216              :         return false;
     217              :     }
     218              : }
     219              : 
     220              : 
     221              : bool
     222         1437 : MSDriveWay::notifyReroute(SUMOTrafficObject& veh) {
     223              : #ifdef DEBUG_MOVEREMINDER
     224              :     std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << "\n";
     225              : #endif
     226              :     assert(veh.isVehicle());
     227         1437 :     SUMOVehicle* sveh = dynamic_cast<SUMOVehicle*>(&veh);
     228              :     assert(myTrains.count(sveh) != 0);
     229         1437 :     if (matchesPastRoute(*sveh) >= 0) {
     230              :         //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " valid\n";
     231              :         return true;
     232              :     }
     233              :     // no match found, remove
     234              :     myTrains.erase(sveh);
     235          701 :     if (myWriteVehicles) {
     236           30 :         myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), NOTIFICATION_REROUTE));
     237              :     }
     238              :     //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " invalid\n";
     239              :     return false;
     240              : }
     241              : 
     242              : 
     243              : int
     244         2868 : MSDriveWay::matchesPastRoute(SUMOVehicle& sveh) const {
     245              :     // look backwards along the route to find the driveway lane
     246         2868 :     const ConstMSEdgeVector& routeEdges = sveh.getRoute().getEdges();
     247        18759 :     for (int i = sveh.getRoutePosition(); i >= 0; i--) {
     248        18341 :         if (routeEdges[i] == myLane->getNextNormal()) {
     249              :             MSRouteIterator firstIt = routeEdges.begin() + i;
     250         2450 :             if (match(firstIt, sveh.getRoute().end())) {
     251              :                 // driveway is still valid after rerouting
     252              :                 //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " valid\n";
     253         1554 :                 return sveh.getRoutePosition() - i;
     254              :             }
     255          896 :             break;
     256              :         }
     257              :     }
     258              :     return -1;
     259              : }
     260              : 
     261              : 
     262              : void
     263        20202 : MSDriveWay::enterDriveWay(SUMOVehicle& sveh, Notification reason) {
     264        20202 :     myTrains.insert(&sveh);
     265        20202 :     if (myOrigin != nullptr) {
     266        13958 :         MSRailSignalControl::getInstance().notifyApproach(myOrigin);
     267              :     }
     268        65822 :     for (const MSDriveWay* foe : myFoes) {
     269        45620 :         if (foe->myOrigin != nullptr) {
     270        36404 :             MSRailSignalControl::getInstance().notifyApproach(foe->myOrigin);
     271              :         }
     272              :     }
     273        20202 :     if (myWriteVehicles) {
     274         1828 :         myVehicleEvents.push_back(VehicleEvent(SIMSTEP, true, sveh.getID(), reason));
     275              :     }
     276        20202 : }
     277              : 
     278              : bool
     279       599975 : MSDriveWay::reserve(const Approaching& closest, MSEdgeVector& occupied) {
     280       599975 :     if (foeDriveWayOccupied(true, closest.first, occupied)) {
     281              :         return false;
     282              :     }
     283       495606 :     for (MSLink* foeLink : myConflictLinks) {
     284       136319 :         if (hasLinkConflict(closest, foeLink)) {
     285              : #ifdef DEBUG_SIGNALSTATE
     286              :             if (gDebugFlag4 || DEBUG_HELPER(closest.first)) {
     287              :                 std::cout << getID() << " linkConflict with " << getTLLinkID(foeLink) << "\n";
     288              :             }
     289              : #endif
     290              :             return false;
     291              :         }
     292              :     }
     293       359287 :     myActive = closest.first;
     294       359287 :     return true;
     295              : }
     296              : 
     297              : 
     298              : bool
     299       136319 : MSDriveWay::hasLinkConflict(const Approaching& veh, const MSLink* foeLink) const {
     300              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     301              :     if (gDebugFlag4) {
     302              :         std::cout << "   checkLinkConflict foeLink=" << getTLLinkID(foeLink) << " ego=" << Named::getIDSecure(veh.first) << "\n";
     303              :     }
     304              : #endif
     305       136319 :     if (foeLink->getApproaching().size() > 0) {
     306        45272 :         Approaching foe = foeLink->getClosest();
     307              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     308              :         if (gDebugFlag4) {
     309              :             std::cout << "     approaching foe=" << foe.first->getID() << "\n";
     310              :         }
     311              : #endif
     312        45272 :         if (foe.first == veh.first) {
     313        45272 :             return false;
     314              :         }
     315              :         const MSTrafficLightLogic* foeTLL = foeLink->getTLLogic();
     316              :         assert(foeTLL != nullptr);
     317        40461 :         const MSRailSignal* constFoeRS = dynamic_cast<const MSRailSignal*>(foeTLL);
     318              :         MSRailSignal* foeRS = const_cast<MSRailSignal*>(constFoeRS);
     319        40461 :         if (foeRS != nullptr) {
     320        40461 :             const MSDriveWay& foeDriveWay = foeRS->retrieveDriveWayForVeh(foeLink->getTLIndex(), foe.first);
     321              :             MSEdgeVector occupied;
     322        65279 :             if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied) ||
     323        41219 :                     !foeRS->constraintsAllow(foe.first) ||
     324        32521 :                     !overlap(foeDriveWay) ||
     325        56581 :                     !isFoeOrSubFoe(&foeDriveWay) ||
     326        12640 :                     canUseSiding(veh.first, &foeDriveWay).first) {
     327              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     328              :                 if (gDebugFlag4) {
     329              :                     if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied)) {
     330              :                         std::cout << "     foe blocked\n";
     331              :                     } else if (!foeRS->constraintsAllow(foe.first)) {
     332              :                         std::cout << "     foe constrained\n";
     333              :                     } else if (!overlap(foeDriveWay)) {
     334              :                         std::cout << "     no overlap with foeDW=" << foeDriveWay.getID() << "\n";
     335              :                     } else if (!isFoeOrSubFoe(&foeDriveWay)) {
     336              :                         std::cout << "     foeDW=" << foeDriveWay.getID() << " is not a foe to " << getID() << "\n";
     337              :                     } else if (canUseSiding(veh.first, &foeDriveWay).first) {
     338              :                         std::cout << "     use siding\n";
     339              :                     }
     340              :                 }
     341              : #endif
     342        27959 :                 return false;
     343              :             }
     344              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     345              :             if (gDebugFlag4) {
     346              :                 std::cout
     347              :                         << "  aSB=" << veh.second.arrivalSpeedBraking << " foeASB=" << foe.second.arrivalSpeedBraking
     348              :                         << "  aT=" << veh.second.arrivalTime << " foeAT=" << foe.second.arrivalTime
     349              :                         << "  aS=" << veh.first->getSpeed() << " foeS=" << foe.first->getSpeed()
     350              :                         << "  aD=" << veh.second.dist << " foeD=" << foe.second.dist
     351              :                         << "  aW=" << veh.first->getWaitingTime() << " foeW=" << foe.first->getWaitingTime()
     352              :                         << "  aN=" << veh.first->getNumericalID() << " foeN=" << foe.first->getNumericalID()
     353              :                         << "\n";
     354              :             }
     355              : #endif
     356        12502 :             const bool yield = mustYield(veh, foe);
     357        12502 :             if (MSRailSignal::storeVehicles()) {
     358          540 :                 MSRailSignal::rivalVehicles().push_back(foe.first);
     359          540 :                 if (yield) {
     360          315 :                     MSRailSignal::priorityVehicles().push_back(foe.first);
     361              :                 }
     362              :             }
     363        12502 :             return yield;
     364        40461 :         }
     365              :     }
     366              :     return false;
     367              : }
     368              : 
     369              : 
     370              : bool
     371        26271 : MSDriveWay::isFoeOrSubFoe(const MSDriveWay* foe) const {
     372        26271 :     if (std::find(myFoes.begin(), myFoes.end(), foe) != myFoes.end()) {
     373              :         return true;
     374              :     }
     375        18191 :     for (const MSDriveWay* sub : foe->mySubDriveWays) {
     376        10151 :         if (isFoeOrSubFoe(sub)) {
     377              :             return true;
     378              :         }
     379              :     }
     380              :     return false;
     381              : }
     382              : 
     383              : 
     384              : bool
     385        12502 : MSDriveWay::mustYield(const Approaching& veh, const Approaching& foe) {
     386        12502 :     if (foe.second.arrivalSpeedBraking == veh.second.arrivalSpeedBraking) {
     387         8051 :         if (foe.second.arrivalTime == veh.second.arrivalTime) {
     388          618 :             if (foe.first->getSpeed() == veh.first->getSpeed()) {
     389          594 :                 if (foe.second.dist == veh.second.dist) {
     390          558 :                     if (foe.first->getWaitingTime() == veh.first->getWaitingTime()) {
     391          540 :                         return foe.first->getNumericalID() < veh.first->getNumericalID();
     392              :                     } else {
     393           18 :                         return foe.first->getWaitingTime() > veh.first->getWaitingTime();
     394              :                     }
     395              :                 } else {
     396           36 :                     return foe.second.dist < veh.second.dist;
     397              :                 }
     398              :             } else {
     399           24 :                 return foe.first->getSpeed() > veh.first->getSpeed();
     400              :             }
     401              :         } else {
     402         7433 :             return foe.second.arrivalTime < veh.second.arrivalTime;
     403              :         }
     404              :     } else {
     405         4451 :         return foe.second.arrivalSpeedBraking > veh.second.arrivalSpeedBraking;
     406              :     }
     407              : }
     408              : 
     409              : 
     410              : bool
     411        16035 : MSDriveWay::conflictLaneOccupied(bool store, const SUMOVehicle* ego) const {
     412       210513 :     for (const MSLane* lane : myConflictLanes) {
     413              :         if (!lane->isEmpty()) {
     414         4410 :             std::string joinVehicle = "";
     415         4410 :             if (ego != nullptr && !MSGlobals::gUseMesoSim) {
     416            0 :                 const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
     417            0 :                 if (stop != nullptr) {
     418            0 :                     joinVehicle = stop->join;
     419              :                 }
     420              :             }
     421              : #ifdef DEBUG_SIGNALSTATE
     422              :             if (gDebugFlag4) {
     423              :                 std::cout << SIMTIME << " conflictLane " << lane->getID() << " occupied ego=" << Named::getIDSecure(ego) << " vehNumber=" << lane->getVehicleNumber() << "\n";
     424              :                 if (joinVehicle != "") {
     425              :                     std::cout << "  joinVehicle=" << joinVehicle << " occupant=" << toString(lane->getVehiclesSecure()) << "\n";
     426              :                     lane->releaseVehicles();
     427              :                 }
     428              :             }
     429              : #endif
     430         4410 :             if (lane->getVehicleNumberWithPartials() == 1) {
     431         4410 :                 MSVehicle* foe = lane->getLastAnyVehicle();
     432         4410 :                 if (joinVehicle != "") {
     433            0 :                     if (foe->getID() == joinVehicle && foe->isStopped()) {
     434              : #ifdef DEBUG_SIGNALSTATE
     435              :                         if (gDebugFlag4) {
     436              :                             std::cout << "    ignore join-target '" << joinVehicle << "\n";
     437              :                         }
     438              : #endif
     439            0 :                         continue;
     440              :                     }
     441              :                 }
     442         4410 :                 if (ego != nullptr) {
     443            0 :                     if (foe == ego && std::find(myForward.begin(), myForward.end(), lane) == myForward.end()) {
     444              : #ifdef DEBUG_SIGNALSTATE
     445              :                         if (gDebugFlag4) {
     446              :                             std::cout << "    ignore ego as oncoming '" << ego->getID() << "\n";
     447              :                         }
     448              : #endif
     449            0 :                         continue;
     450              :                     }
     451            0 :                     if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
     452              : #ifdef DEBUG_SIGNALSTATE
     453              :                         if (gDebugFlag4) {
     454              :                             std::cout << "    ignore " << foe->getID() << " for which ego is join-target\n";
     455              :                         }
     456              : #endif
     457            0 :                         continue;
     458              :                     }
     459              :                 }
     460              :             }
     461         4410 :             if (MSRailSignal::storeVehicles() && store) {
     462         4410 :                 MSRailSignal::blockingVehicles().push_back(lane->getLastAnyVehicle());
     463              :             }
     464              :             return true;
     465              :         }
     466              :     }
     467        11625 :     return false;
     468              : }
     469              : 
     470              : 
     471              : bool
     472      8520797 : MSDriveWay::foeDriveWayApproached() const {
     473     17149987 :     for (const MSDriveWay* foeDW : myFoes) {
     474     17088463 :         if (foeDW->myOrigin != nullptr && foeDW->myOrigin->getApproaching().size() > 0) {
     475              : #ifdef DEBUG_SIGNALSTATE
     476              :             if (gDebugFlag4) {
     477              :                 std::cout << SIMTIME << " foeLink=" << foeDW->myOrigin->getDescription() << " approachedBy=" << foeDW->myOrigin->getApproaching().begin()->first->getID() << "\n";
     478              :             }
     479              : #endif
     480              :             return true;
     481              :         }
     482              :     }
     483              :     return false;
     484              : }
     485              : 
     486              : 
     487              : bool
     488      9796969 : MSDriveWay::foeDriveWayOccupied(bool store, const SUMOVehicle* ego, MSEdgeVector& occupied) const {
     489     28716893 :     for (const MSDriveWay* foeDW : myFoes) {
     490     19810562 :         if (!foeDW->myTrains.empty()) {
     491              : #ifdef DEBUG_SIGNALSTATE
     492              :             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     493              :                 std::cout << SIMTIME << " " << getID() << " foeDriveWay " << foeDW->getID() << " occupied ego=" << Named::getIDSecure(ego) << " foeVeh=" << toString(foeDW->myTrains) << "\n";
     494              :             }
     495              : #endif
     496       917254 :             if (foeDW->myTrains.size() == 1) {
     497       914849 :                 SUMOVehicle* foe = *foeDW->myTrains.begin();
     498       914849 :                 if (foe == ego) {
     499              : #ifdef DEBUG_SIGNALSTATE
     500              :                     if (gDebugFlag4 || DEBUG_HELPER(ego)) {
     501              :                         std::cout << "    ignore ego as foe '" << Named::getIDSecure(ego) << "\n";
     502              :                     }
     503              : #endif
     504        26704 :                     continue;
     505              :                 }
     506       893283 :                 if (hasJoin(ego, foe)) {
     507           36 :                     continue;
     508              :                 }
     509              :             }
     510       895652 :             std::pair<bool, const MSDriveWay*> useSiding = canUseSiding(ego, foeDW);
     511              : #ifdef DEBUG_SIGNALSTATE
     512              :             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     513              :                 auto it = mySidings.find(foeDW);
     514              :                 int numSidings = 0;
     515              :                 if (it != mySidings.end()) {
     516              :                     numSidings = it->second.size();
     517              :                 }
     518              :                 std::cout << "  useSiding=" << useSiding.first << " sidingFoe=" << Named::getIDSecure(useSiding.second) << " numSidings=" << numSidings << "\n";
     519              :             }
     520              : #endif
     521       895652 :             if (useSiding.first) {
     522         5102 :                 continue;
     523              :             } else {
     524       890550 :                 if (MSRailSignal::storeVehicles() && store) {
     525          740 :                     for (SUMOVehicle* foe : foeDW->myTrains) {
     526          370 :                         MSRailSignal::blockingVehicles().push_back(foe);
     527              :                     }
     528          370 :                     MSRailSignal::blockingDriveWays().push_back(foeDW);
     529              :                 }
     530      1784190 :                 for (const SUMOVehicle* foe : foeDW->myTrains) {
     531       893640 :                     occupied.push_back(const_cast<MSEdge*>(foe->getEdge()));
     532       893640 :                     MSEdge* bidi = const_cast<MSEdge*>(foe->getEdge()->getBidiEdge());
     533       893640 :                     if (bidi != nullptr) {
     534       480951 :                         occupied.push_back(bidi);
     535              :                     }
     536              :                     /// @todo: if foe occupies more than one edge we should add all of them to the occupied vector
     537              :                 }
     538       297434 :                 if (ego != nullptr && MSGlobals::gTimeToTeleportRSDeadlock > 0
     539      1134144 :                         && (ego->getWaitingTime() > ego->getVehicleType().getCarFollowModel().getStartupDelay() || !ego->isOnRoad())) {
     540              :                     // if there is an occupied siding, it becomes part of the waitRelation
     541       137140 :                     SUMOVehicle* foe = *(useSiding.second == nullptr ? foeDW : useSiding.second)->myTrains.begin();
     542       137140 :                     const MSRailSignal* rs = myOrigin != nullptr ? dynamic_cast<const MSRailSignal*>(myOrigin->getTLLogic()) : nullptr;
     543       137140 :                     MSRailSignalControl::getInstance().addWaitRelation(ego, rs, foe);
     544              :                 }
     545       890550 :                 return true;
     546              :             }
     547     18893308 :         } else if (foeDW != this && isDepartDriveway() && !foeDW->isDepartDriveway()) {
     548         9207 :             if (foeDW->myOrigin->getApproaching().size() > 0) {
     549         1612 :                 Approaching foeA = foeDW->myOrigin->getClosest();
     550         1612 :                 const SUMOVehicle* foe = foeA.first;
     551         1612 :                 if (foeA.second.dist < foe->getBrakeGap(true)) {
     552          128 :                     MSRouteIterator firstIt = std::find(foe->getCurrentRouteEdge(), foe->getRoute().end(), foeDW->myRoute.front());
     553          128 :                     if (firstIt != foe->getRoute().end()) {
     554          128 :                         if (foeDW->match(firstIt, foe->getRoute().end())) {
     555           88 :                             bool useSiding = canUseSiding(ego, foeDW).first;
     556              : #ifdef DEBUG_SIGNALSTATE
     557              :                             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     558              :                                 std::cout << SIMTIME << " " << getID() << " blocked by " << foeDW->getID() << " (approached by " << foe->getID() << ") useSiding=" << useSiding << "\n";
     559              :                             }
     560              : #endif
     561           88 :                             if (useSiding) {
     562              :                                 //std::cout << SIMTIME << " " << getID() << " ego=" << ego->getID() << " foeDW=" << foeDW->getID() << " myFoes=" << toString(myFoes) << "\n";
     563            0 :                                 continue;
     564              :                             } else {
     565           88 :                                 return true;
     566              :                             }
     567              :                         }
     568              :                     }
     569              :                 }
     570              :             }
     571              :         }
     572              :     }
     573      8907299 :     for (const std::set<const MSDriveWay*>& dlFoes : myDeadlocks) {
     574              :         bool allOccupied = true;
     575        10530 :         for (const MSDriveWay* dlFoe : dlFoes) {
     576         7874 :             if (dlFoe->myTrains.empty()) {
     577              :                 allOccupied = false;
     578              :                 //std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << "  deadlockCheck clear " << dlFoe->getID() << "\n";
     579              :                 break;
     580              :             }
     581              :         }
     582         3624 :         if (allOccupied) {
     583              : #ifdef DEBUG_SIGNALSTATE
     584              :             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     585              :                 std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " deadlockCheck " << joinNamedToString(dlFoes, " ") << "\n";
     586              :             }
     587              : #endif
     588         9040 :             for (const MSDriveWay* dlFoe : dlFoes) {
     589         6384 :                 MSRailSignal::blockingDriveWays().push_back(dlFoe);
     590              :             }
     591              :             return true;
     592              :         }
     593              :     }
     594              :     return false;
     595              : }
     596              : 
     597              : 
     598              : bool
     599       893283 : MSDriveWay::hasJoin(const SUMOVehicle* ego, const SUMOVehicle* foe) {
     600       893283 :     if (ego != nullptr && !MSGlobals::gUseMesoSim) {
     601       233387 :         std::string joinVehicle = "";
     602       233387 :         const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
     603       233387 :         if (stop != nullptr) {
     604        91596 :             joinVehicle = stop->join;
     605              :         }
     606       233387 :         if (joinVehicle == "" && !ego->hasDeparted() && ego->getStops().size() > 1) {
     607              :             // check one more stop
     608         6425 :             auto it = ego->getStops().begin();
     609              :             std::advance(it, 1);
     610         6425 :             joinVehicle = it->pars.join;
     611              :         }
     612       233387 :         if (joinVehicle != "") {
     613              : #ifdef DEBUG_SIGNALSTATE
     614              :             if (gDebugFlag4 || DEBUG_COND_DW(ego) || DEBUG_COND_DW(foe)) {
     615              :                 std::cout << "  joinVehicle=" << joinVehicle << "\n";
     616              :             }
     617              : #endif
     618          345 :             if (foe->getID() == joinVehicle && foe->isStopped()) {
     619              : #ifdef DEBUG_SIGNALSTATE
     620              :                 if (gDebugFlag4 || DEBUG_COND_DW(ego) || DEBUG_COND_DW(foe)) {
     621              :                     std::cout << "    ignore join-target '" << joinVehicle << "\n";
     622              :                 }
     623              : #endif
     624              :                 return true;
     625              :             }
     626              :         }
     627              : 
     628       233363 :         if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
     629              : #ifdef DEBUG_SIGNALSTATE
     630              :             if (gDebugFlag4 || DEBUG_COND_DW(ego) || DEBUG_COND_DW(foe)) {
     631              :                 std::cout << "    ignore " << foe->getID() << " for which ego is join-target\n";
     632              :             }
     633              : #endif
     634              :             return true;
     635              :         }
     636              :     }
     637              :     return false;
     638              : }
     639              : 
     640              : 
     641              : std::pair<bool, const MSDriveWay*>
     642       912180 : MSDriveWay::canUseSiding(const SUMOVehicle* ego, const MSDriveWay* foe, bool recurse) const {
     643              :     auto it = mySidings.find(foe);
     644       912180 :     if (it != mySidings.end()) {
     645        14523 :         for (auto siding : it->second) {
     646              :             // assume siding is usuable when computing state for unapproached signal (ego == nullptr)
     647        13731 :             if (ego == nullptr || siding.length >= ego->getLength()) {
     648              :                 // if the siding is already "reserved" by another vehicle we cannot use it here
     649        10380 :                 const MSEdge* sidingEnd = myRoute[siding.end];
     650        24082 :                 for (MSDriveWay* sidingApproach : myEndingDriveways[sidingEnd]) {
     651        18536 :                     if (!sidingApproach->myTrains.empty()) {
     652              :                         // possibly the foe vehicle can use the other part of the siding
     653         5140 :                         if (recurse) {
     654              :                             const SUMOVehicle* foeVeh = nullptr;
     655         3800 :                             if (!foe->myTrains.empty()) {
     656         3786 :                                 foeVeh = *foe->myTrains.begin();
     657           14 :                             } else if (foe->myOrigin != nullptr && foe->myOrigin->getApproaching().size() > 0) {
     658           14 :                                 foeVeh = foe->myOrigin->getClosest().first;
     659              :                             }
     660         3800 :                             if (foeVeh == nullptr) {
     661            0 :                                 WRITE_WARNINGF("Invalid call to canUseSiding dw=% foe=% ego=% time=%", getID(), foe->getID(), Named::getIDSecure(ego), time2string(SIMSTEP));
     662            0 :                                 continue;
     663              :                             }
     664         3800 :                             if (foe->canUseSiding(foeVeh, this, false).first) {
     665          306 :                                 continue;
     666              :                             }
     667              :                         }
     668              :                         // possibly the foe vehicle
     669              :                         // @todo: in principle it might still be possible to continue if vehicle that approaches the siding can safely leave the situation
     670              : #ifdef DEBUG_SIGNALSTATE
     671              :                         if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     672              :                             std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
     673              :                                       << " foeVeh=" << toString(foe->myTrains)
     674              :                                       << " sidingEnd=" << sidingEnd->getID() << " sidingApproach=" << sidingApproach->getID() << " approaching=" << toString(sidingApproach->myTrains) << "\n";
     675              :                         }
     676              : #endif
     677         4834 :                         return std::make_pair(false, sidingApproach);
     678              :                     }
     679              :                 }
     680              :                 //std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
     681              :                 //    << " foeVeh=" << toString(foe->myTrains)
     682              :                 //    << " sidingEnd=" << sidingEnd->getID() << "usable\n";
     683         5546 :                 return std::make_pair(true, nullptr);
     684              :             }
     685              :         }
     686              :     }
     687       901800 :     return std::make_pair(false, nullptr);
     688              : }
     689              : 
     690              : bool
     691        20131 : MSDriveWay::overlap(const MSDriveWay& other) const {
     692        32590 :     for (int i = 0; i < myCoreSize; i++) {
     693       372897 :         for (int j = 0; j < other.myCoreSize; j++) {
     694       360438 :             const MSEdge* edge = myRoute[i];
     695       360438 :             const MSEdge* edge2 = other.myRoute[j];
     696              :             if (edge->getToJunction() == edge2->getToJunction()
     697       342737 :                     || edge->getToJunction() == edge2->getFromJunction()
     698       701339 :                     || edge->getFromJunction() == edge2->getFromJunction()) {
     699              :                 // XXX might be rail_crossing with parallel tracks
     700              :                 return true;
     701              :             }
     702              :         }
     703              :     }
     704              :     return false;
     705              : }
     706              : 
     707              : 
     708              : bool
     709        74695 : MSDriveWay::flankConflict(const MSDriveWay& other) const {
     710       247601 :     for (const MSLane* lane : myForward) {
     711       973124 :         for (const MSLane* lane2 : other.myForward) {
     712       794829 :             if (lane == lane2) {
     713              :                 return true;
     714              :             }
     715              :         }
     716      2533032 :         for (const MSLane* lane2 : other.myBidi) {
     717      2357437 :             if (lane == lane2) {
     718         5286 :                 if (bidiBlockedBy(other)) {
     719              :                     // it's only a deadlock if both trains block symmetrically
     720              :                     return true;
     721              :                 }
     722              :             }
     723              :         }
     724      6065428 :         for (const MSLane* lane2 : other.myBidiExtended) {
     725      5892522 :             if (lane == lane2) {
     726        20245 :                 if (bidiBlockedBy(other)) {
     727              :                     // it's only a deadlock if both trains block symmetrically
     728              :                     return true;
     729              :                 }
     730              :             }
     731              :         }
     732              :     }
     733              :     return false;
     734              : }
     735              : 
     736              : 
     737              : bool
     738        64695 : MSDriveWay::crossingConflict(const MSDriveWay& other) const {
     739       224820 :     for (const MSLane* lane : myForward) {
     740       731742 :         for (const MSLane* lane2 : other.myForward) {
     741       571617 :             if (lane->isNormal() && lane2->isNormal() && lane->getEdge().getToJunction() == lane2->getEdge().getToJunction()) {
     742              :                 return true;
     743              :             }
     744              :         }
     745              :     }
     746        57127 :     if (myOrigin != nullptr && other.myOrigin != nullptr
     747        49832 :             && myOrigin->getJunction() == other.myOrigin->getJunction()
     748              :             //&& myForward.front()->isInternal() && other.myForward.front()->isInternal()
     749         1233 :             && myOrigin->getJunction()->getLogic() != nullptr
     750        63906 :             && myOrigin->getJunction()->getLogic()->getFoesFor(myOrigin->getIndex()).test(other.myOrigin->getIndex())) {
     751              :         // switch/crossing is also a rail_signal (direct control)
     752           80 :         if (!(myForward.front()->isInternal() && other.myForward.front()->isInternal())) {
     753           23 :             return false;
     754              :         }
     755              :         return true;
     756              :     }
     757              :     return false;
     758              : }
     759              : 
     760              : 
     761              : bool
     762        25871 : MSDriveWay::bidiBlockedBy(const MSDriveWay& other) const {
     763       417483 :     for (const MSLane* lane : myBidi) {
     764      1377123 :         for (const MSLane* lane2 : other.myForward) {
     765       985511 :             if (lane == lane2) {
     766              :                 return true;
     767              :             }
     768              :         }
     769              :     }
     770       442240 :     for (const MSLane* lane : myBidiExtended) {
     771      1434975 :         for (const MSLane* lane2 : other.myForward) {
     772      1015367 :             if (lane == lane2) {
     773         2249 :                 if (overlap(other)) {
     774              :                     return true;
     775              :                 }
     776              :             }
     777              :         }
     778              :     }
     779              :     return false;
     780              : }
     781              : 
     782              : 
     783              : bool
     784         8168 : MSDriveWay::bidiBlockedByEnd(const MSDriveWay& other) const {
     785         8168 :     const MSLane* end = other.myForward.back();
     786        65670 :     for (const MSLane* lane : myBidi) {
     787        60269 :         if (lane == end) {
     788              :             return true;
     789              :         }
     790              :     }
     791        41899 :     for (const MSLane* lane : myBidiExtended) {
     792        37979 :         if (lane == end) {
     793         1481 :             if (overlap(other)) {
     794              :                 return true;
     795              :             }
     796              :         }
     797              :     }
     798              :     return false;
     799              : }
     800              : 
     801              : bool
     802         5753 : MSDriveWay::forwardRouteConflict(std::set<const MSEdge*> forward, const MSDriveWay& other, bool secondCheck) {
     803              :     int i = 0;
     804        62289 :     for (const MSEdge* edge2 : other.myRoute) {
     805        61491 :         if (i == other.myCoreSize) {
     806              :             return false;
     807              :         }
     808        61491 :         i++;
     809        61491 :         if (edge2 == myForward.front()->getNextNormal() && !secondCheck) {
     810              :             // foe should not pass from behind through our own forward section
     811              :             return false;
     812              :         }
     813              :         if (forward.count(edge2->getBidiEdge()) != 0) {
     814              :             return true;
     815              :         }
     816              :     }
     817              :     return false;
     818              : }
     819              : 
     820              : void
     821         8235 : MSDriveWay::writeBlocks(OutputDevice& od) const {
     822        14520 :     od.openTag(myIsSubDriveway ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
     823         8235 :     od.writeAttr(SUMO_ATTR_ID, myID);
     824         8235 :     od.writeAttr(SUMO_ATTR_VEHICLE, myFirstVehicle);
     825         8235 :     od.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
     826         8235 :     if (myCoreSize != (int)myRoute.size()) {
     827            0 :         od.writeAttr("core", myCoreSize);
     828              :     }
     829         8235 :     od.openTag("forward");
     830         8235 :     od.writeAttr(SUMO_ATTR_LANES, toString(myForward));
     831         8235 :     od.closeTag();
     832         8235 :     if (!myIsSubDriveway) {
     833         6285 :         od.openTag("bidi");
     834        12570 :         od.writeAttr(SUMO_ATTR_LANES, toString(myBidi));
     835         6285 :         if (myBidiExtended.size() > 0) {
     836         1951 :             od.lf();
     837         1951 :             od << "                   ";
     838         3902 :             od.writeAttr("deadlockCheck", toString(myBidiExtended));
     839              :         }
     840         6285 :         od.closeTag();
     841         6285 :         od.openTag("flank");
     842         6285 :         od.writeAttr(SUMO_ATTR_LANES, toString(myFlank));
     843         6285 :         od.closeTag();
     844              : 
     845        12570 :         od.openTag("conflictLinks");
     846              : 
     847              :         std::vector<std::string> signals;
     848        10018 :         for (MSLink* link : myConflictLinks) {
     849         7466 :             signals.push_back(getTLLinkID(link));
     850              :         }
     851        12570 :         od.writeAttr("signals", joinToStringSorting(signals, " "));
     852        12570 :         od.closeTag();
     853              : 
     854              :         std::vector<std::string> foes;
     855        23197 :         for (MSDriveWay* dw : myFoes) {
     856        16912 :             foes.push_back(dw->myID);
     857              :         }
     858         6285 :         if (foes.size() > 0) {
     859         6199 :             od.openTag("foes");
     860        12398 :             od.writeAttr("driveWays", joinToStringSorting(foes, " "));
     861        12398 :             od.closeTag();
     862              :         }
     863         6901 :         for (auto item : mySidings) {
     864          616 :             od.openTag("sidings");
     865         1232 :             od.writeAttr("foe", item.first->getID());
     866         3163 :             for (auto siding : item.second) {
     867         2547 :                 od.openTag("siding");
     868         2547 :                 od.writeAttr("start", myRoute[siding.start]->getID());
     869         2547 :                 od.writeAttr("end", myRoute[siding.end]->getID());
     870         2547 :                 od.writeAttr("length", siding.length);
     871         5094 :                 od.closeTag();
     872              :             }
     873         1232 :             od.closeTag();
     874              :         }
     875         6366 :         for (auto item : myDeadlocks) {
     876           81 :             od.openTag("deadlock");
     877          162 :             od.writeAttr("foes", joinNamedToStringSorting(item, " "));
     878          162 :             od.closeTag();
     879              :         }
     880         6285 :     }
     881        16470 :     od.closeTag(); // driveWay
     882              : 
     883        10185 :     for (const MSDriveWay* sub : mySubDriveWays) {
     884         1950 :         sub->writeBlocks(od);
     885              :     }
     886              : #ifdef DRIVEWAY_SANITY_CHECK
     887         8235 :     std::set<MSDriveWay*> uFoes(myFoes.begin(), myFoes.end());
     888         8235 :     if (uFoes.size() != myFoes.size()) {
     889            0 :         WRITE_WARNINGF("Duplicate foes in driveway '%'", getID());
     890              : 
     891              :     }
     892              : #endif
     893         8235 : }
     894              : 
     895              : 
     896              : void
     897          787 : MSDriveWay::writeBlockVehicles(OutputDevice& od) const {
     898         1391 :     od.openTag(myIsSubDriveway ? "subDriveWay" : "driveWay");
     899          787 :     od.writeAttr(SUMO_ATTR_ID, myID);
     900         2626 :     for (const VehicleEvent& ve : myVehicleEvents) {
     901         2757 :         od.openTag(ve.isEntry ? "entry" : "exit");
     902         1839 :         od.writeAttr(SUMO_ATTR_ID, ve.id);
     903         1839 :         od.writeAttr(SUMO_ATTR_TIME, time2string(ve.time));
     904         1839 :         od.writeAttr("reason", Notifications.getString(ve.reason));
     905         3678 :         od.closeTag(); // event
     906              :     }
     907         1574 :     od.closeTag(); // driveWay
     908              : 
     909          970 :     for (const MSDriveWay* sub : mySubDriveWays) {
     910          183 :         sub->writeBlockVehicles(od);
     911              :     }
     912          787 : }
     913              : 
     914              : 
     915              : void
     916         8136 : MSDriveWay::buildRoute(const MSLink* origin,
     917              :                        MSRouteIterator next, MSRouteIterator end,
     918              :                        LaneVisitedMap& visited,
     919              :                        std::set<MSLink*>& flankSwitches) {
     920         8136 :     double length = 0;
     921              :     bool seekForwardSignal = true;
     922              :     bool seekBidiSwitch = true;
     923              :     bool foundUnsafeSwitch = false;
     924         8136 :     MSLane* toLane = origin ? origin->getViaLaneOrLane() : (*next)->getLanes()[0];
     925        21316 :     const std::string warnID = origin ? "rail signal " + getClickableTLLinkID(origin) : "insertion lane '" + toLane->getID() + "'";
     926              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     927              :     gDebugFlag4 = DEBUG_COND_DW2;
     928              :     if (gDebugFlag4) std::cout << "buildRoute origin=" << warnID << " vehRoute=" << toString(ConstMSEdgeVector(next, end))
     929              :                                    << " visited=" << formatVisitedMap(visited) << "\n";
     930              : #endif
     931              :     while (true) {
     932        99378 :         if (length > MSGlobals::gMaxRailSignalBlockLength) {
     933              :             // typical block length in germany on main lines is 3-5km on branch lines up to 7km
     934              :             // special branches that are used by one train exclusively could also be up to 20km in length
     935              :             // minimum block size in germany is 37.5m (LZB)
     936              :             // larger countries (USA, Russia) might see blocks beyond 20km)
     937          222 :             if (seekForwardSignal && myBlockLengthWarnings.count(myRoute.front()) == 0) {
     938          222 :                 WRITE_WARNINGF("Block after % exceeds maximum length (stopped searching after edge '%' (length=%m).",
     939              :                                warnID, toLane->getEdge().getID(), length);
     940              :                 myBlockLengthWarnings.insert(myRoute.front());
     941              :             }
     942          222 :             myAbortedBuild = true;
     943              :             // length exceeded
     944              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     945              :             if (gDebugFlag4) {
     946              :                 std::cout << " abort: length=" << length << "\n";
     947              :             }
     948              : #endif
     949         8136 :             return;
     950              :         }
     951              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     952              :         if (gDebugFlag4) {
     953              :             std::cout << "   toLane=" << toLane->getID() << " visited=" << formatVisitedMap(visited) << "\n";
     954              :         }
     955              : #endif
     956        99156 :         const MSEdge* current = &toLane->getEdge();
     957        99156 :         if (current->isNormal()) {
     958        62086 :             myRoute.push_back(current);
     959        62086 :             if (next != end) {
     960              :                 next++;
     961              :             }
     962              :         }
     963        99156 :         appendMapIndex(visited, toLane);
     964        99156 :         length += toLane->getLength();
     965        99156 :         MSLane* bidi = toLane->getBidiLane();
     966        99156 :         if (seekForwardSignal) {
     967        27545 :             if (!foundUnsafeSwitch) {
     968        27545 :                 myForward.push_back(toLane);
     969        27545 :                 if (toLane->isNormal()) {
     970        18227 :                     myForwardEdgeCount++;
     971              :                 }
     972        27545 :                 if (myForward.size() == 1) {
     973         8136 :                     myLane = toLane;
     974         8136 :                     if (MSGlobals::gUseMesoSim) {
     975         2380 :                         MESegment* s = MSGlobals::gMesoNet->getSegmentForEdge(myLane->getEdge());
     976         2380 :                         s->addDetector(this, myLane->getIndex());
     977              :                     } else {
     978         5756 :                         toLane->addMoveReminder(this, false);
     979              :                     }
     980              :                 }
     981              :             }
     982        71611 :         } else if (bidi == nullptr) {
     983        22224 :             if (toLane->isInternal() && toLane->getIncomingLanes().front().viaLink->isTurnaround()) {
     984              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     985              :                 if (gDebugFlag4) {
     986              :                     std::cout << "      continue bidiSearch beyond turnaround\n";
     987              :                 }
     988              : #endif
     989              :             } else {
     990              :                 seekBidiSwitch = false;
     991              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
     992              :                 if (gDebugFlag4) {
     993              :                     std::cout << "      noBidi, abort search for bidiSwitch\n";
     994              :                 }
     995              : #endif
     996              :             }
     997              :         }
     998        99156 :         if (bidi != nullptr) {
     999        64790 :             if (!seekForwardSignal && !foundUnsafeSwitch && bidi->isNormal()) {
    1000              :                 // look for switch that could protect from oncoming vehicles
    1001        22992 :                 for (const MSLink* const link : bidi->getLinkCont()) {
    1002        12903 :                     if (link->getDirection() == LinkDirection::TURN) {
    1003          531 :                         continue;
    1004              :                     }
    1005        24586 :                     if (!myBidi.empty() && link->getViaLaneOrLane() != myBidi.back()) {
    1006         2195 :                         myCoreSize = (int)myRoute.size() - 1;
    1007         2195 :                         MSLink* used = const_cast<MSLink*>(bidi->getLinkTo(myBidi.back()));
    1008              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1009              :                         if (gDebugFlag4) {
    1010              :                             std::cout << "      found unsafe switch " << link->getDescription() << " (used=" << (used == nullptr ? "NULL" : used->getDescription()) << ")\n";
    1011              :                         }
    1012              : #endif
    1013              :                         // trains along our route beyond this switch might create deadlock
    1014              :                         foundUnsafeSwitch = true;
    1015              :                         // the switch itself must still be guarded to ensure safety
    1016         2195 :                         if (used != nullptr) {
    1017              :                             // possibly nullptr if there was an intermediate section of unidirectional edges
    1018              :                             flankSwitches.insert(used);
    1019              :                         }
    1020              :                         break;
    1021              :                     }
    1022              :                 }
    1023              :             }
    1024        62595 :             if (foundUnsafeSwitch) {
    1025        32185 :                 myBidiExtended.push_back(bidi);
    1026              :             } else {
    1027        32605 :                 myBidi.push_back(bidi);
    1028              :             }
    1029              :         }
    1030        99156 :         const std::vector<MSLink*>& links = toLane->getLinkCont();
    1031        99156 :         toLane = nullptr;
    1032       105560 :         for (const MSLink* const link : links) {
    1033        95422 :             if ((next != end && &link->getLane()->getEdge() == *next)
    1034       189631 :                     && isRailwayOrShared(link->getViaLaneOrLane()->getPermissions())) {
    1035       148512 :                 toLane = link->getViaLaneOrLane();
    1036        91598 :                 if (link->getTLLogic() != nullptr && link->getTLIndex() >= 0 && link->getTLLogic()->getLogicType() == TrafficLightType::RAIL_SIGNAL) {
    1037        26352 :                     if (link == origin) {
    1038          356 :                         if (seekForwardSignal) {
    1039            0 :                             WRITE_WARNINGF(TL("Found circular block after % (% edges, length %)"), warnID, toString(myRoute.size()), toString(length));
    1040              :                         }
    1041              :                         //std::cout << getClickableTLLinkID(origin) << " circularBlock2=" << toString(myRoute) << "\n";
    1042          356 :                         myAbortedBuild = true;
    1043              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1044              :                         if (gDebugFlag4) {
    1045              :                             std::cout << " abort: found circle\n";
    1046              :                         }
    1047              : #endif
    1048          356 :                         return;
    1049              :                     }
    1050              :                     seekForwardSignal = false;
    1051        25996 :                     myFoundSignal = true;
    1052              :                     seekBidiSwitch = bidi != nullptr;
    1053              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1054              :                     if (gDebugFlag4) {
    1055              :                         std::cout << "      found forwardSignal " << link->getTLLogic()->getID() << " seekBidiSwitch=" << seekBidiSwitch << "\n";
    1056              :                     }
    1057              : #endif
    1058              :                 }
    1059              :                 //if (links.size() > 1 && !foundUnsafeSwitch) {
    1060        91242 :                 if (isSwitch(link)) {
    1061              :                     // switch on driveway
    1062              :                     //std::cout << "mySwitchDriveWays " << getID() << " link=" << link->getDescription() << "\n";
    1063        51958 :                     mySwitchDriveWays[link].push_back(this);
    1064              :                 }
    1065        91242 :                 if (link->getLane()->getBidiLane() != nullptr && &link->getLane()->getEdge() == current->getBidiEdge()) {
    1066              :                     // reversal on driveway
    1067         1380 :                     myReversalDriveWays[current].push_back(this);
    1068         1380 :                     myReversals.push_back(current);
    1069              :                 }
    1070              :                 break;
    1071              :             }
    1072              :         }
    1073        98800 :         if (toLane == nullptr) {
    1074         7558 :             if (next != end) {
    1075              :                 // no connection found, jump to next route edge
    1076              :                 toLane = (*next)->getLanes()[0];
    1077              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1078              :                 if (gDebugFlag4) {
    1079              :                     std::cout << "      abort: turn-around or jump\n";
    1080              :                 }
    1081              : #endif
    1082           87 :                 myFoundJump = true;
    1083           87 :                 return;
    1084              :             } else {
    1085              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1086              :                 if (gDebugFlag4) {
    1087              :                     std::cout << "      abort: no next lane available\n";
    1088              :                 }
    1089              : #endif
    1090         7471 :                 myTerminateRoute = true;
    1091         7471 :                 return;
    1092              :             }
    1093              :         }
    1094        91242 :     }
    1095              :     myBidiEnded = !seekBidiSwitch;
    1096              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1097              :     if (gDebugFlag4) {
    1098              :         std::cout << " normalEnd myBidiEnded=" << myBidiEnded << "\n";
    1099              :     }
    1100              : #endif
    1101              : }
    1102              : 
    1103              : 
    1104              : bool
    1105       102281 : MSDriveWay::isSwitch(const MSLink* link) {
    1106       198124 :     for (const MSLink* other : link->getLaneBefore()->getNormalPredecessorLane()->getLinkCont()) {
    1107       112043 :         if (other->getLane() != link->getLane() && !other->isTurnaround()) {
    1108              :             return true;
    1109              :         }
    1110              :     }
    1111       140007 :     for (auto ili : link->getLane()->getIncomingLanes()) {
    1112        98209 :         if (ili.viaLink != link && !ili.viaLink->isTurnaround()) {
    1113              :             return true;
    1114              :         }
    1115              :     }
    1116        41798 :     const MSLane* bidi = link->getLane()->getBidiLane();
    1117        41798 :     if (bidi != nullptr) {
    1118        63089 :         for (const MSLink* other : bidi->getLinkCont()) {
    1119        32646 :             if (other->getLane() != link->getLaneBefore()->getNormalPredecessorLane()->getBidiLane() && !other->isTurnaround()) {
    1120              :                 return true;
    1121              :             }
    1122              :         }
    1123              :     }
    1124              :     return false;
    1125              : }
    1126              : 
    1127              : 
    1128              : void
    1129        32544 : MSDriveWay::checkFlanks(const MSLink* originLink, const std::vector<const MSLane*>& lanes, const LaneVisitedMap& visited, bool allFoes, bool movingBlock, std::set<MSLink*>& flankSwitches) const {
    1130              : #ifdef DEBUG_CHECK_FLANKS
    1131              :     std::cout << " checkFlanks lanes=" << toString(lanes) << " allFoes=" << allFoes << "\n";
    1132              : #endif
    1133        20176 :     const MSLink* reverseOriginLink = originLink != nullptr && originLink->getLane()->getBidiLane() != nullptr && originLink->getLaneBefore()->getBidiLane() != nullptr
    1134        43288 :                                       ? originLink->getLane()->getBidiLane()->getLinkTo(originLink->getLaneBefore()->getBidiLane())
    1135              :                                       : nullptr;
    1136              :     //std::cout << "   originLink=" << originLink->getDescription() << "\n";
    1137        10744 :     if (reverseOriginLink != nullptr) {
    1138        10744 :         reverseOriginLink = reverseOriginLink->getCorrespondingExitLink();
    1139              :         //std::cout << "   reverseOriginLink=" << reverseOriginLink->getDescription() << "\n";
    1140              :     }
    1141       127647 :     for (int i = 0; i < (int)lanes.size(); i++) {
    1142        95103 :         const MSLane* lane = lanes[i];
    1143        95103 :         const MSLane* prev = i > 0 ? lanes[i - 1] : nullptr;
    1144        95103 :         const MSLane* next = i + 1 < (int)lanes.size() ? lanes[i + 1] : nullptr;
    1145        95103 :         if (lane->isInternal()) {
    1146        32773 :             continue;
    1147              :         }
    1148       129471 :         for (auto ili : lane->getIncomingLanes()) {
    1149        77853 :             if (ili.viaLink == originLink
    1150        64753 :                     || ili.viaLink == reverseOriginLink
    1151        61730 :                     || ili.viaLink->getDirection() == LinkDirection::TURN
    1152        56536 :                     || ili.viaLink->getDirection() == LinkDirection::TURN_LEFTHAND
    1153       123677 :                     || (originLink == nullptr && i == 0 && movingBlock)) {
    1154        10712 :                 continue;
    1155              :             }
    1156        56429 :             if (ili.lane != prev && ili.lane != next) {
    1157              : #ifdef DEBUG_CHECK_FLANKS
    1158              :                 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";
    1159              : #endif
    1160              :                 flankSwitches.insert(ili.viaLink);
    1161        44739 :             } else if (allFoes) {
    1162              :                 // link is part of the driveway, find foes that cross the driveway without entering
    1163        12036 :                 checkCrossingFlanks(ili.viaLink, visited, flankSwitches);
    1164              :             }
    1165              :         }
    1166              :     }
    1167        32544 : }
    1168              : 
    1169              : 
    1170              : void
    1171        12036 : MSDriveWay::checkCrossingFlanks(MSLink* dwLink, const LaneVisitedMap& visited, std::set<MSLink*>& flankSwitches) const {
    1172              : #ifdef DEBUG_CHECK_FLANKS
    1173              :     std::cout << "  checkCrossingFlanks  dwLink=" << dwLink->getDescription() << " visited=" << formatVisitedMap(visited) << "\n";
    1174              : #endif
    1175              :     const MSJunction* junction = dwLink->getJunction();
    1176        12036 :     if (junction == nullptr) {
    1177              :         return; // unregulated junction;
    1178              :     }
    1179        11962 :     const MSJunctionLogic* logic = junction->getLogic();
    1180        11962 :     if (logic == nullptr) {
    1181              :         return; // unregulated junction;
    1182              :     }
    1183        65562 :     for (const MSEdge* in : junction->getIncoming()) {
    1184        53672 :         if (in->isInternal()) {
    1185        27056 :             continue;
    1186              :         }
    1187        53646 :         for (MSLane* inLane : in->getLanes()) {
    1188        27030 :             const MSLane* inBidi = inLane->getBidiLane();
    1189        37032 :             if (isRailwayOrShared(inLane->getPermissions()) && visited.count(inLane) == 0 && (inBidi == nullptr || visited.count(inBidi) == 0)) {
    1190         8548 :                 for (MSLink* link : inLane->getLinkCont()) {
    1191         4462 :                     if (link->getIndex() >= 0 && logic->getFoesFor(dwLink->getIndex()).test(link->getIndex())
    1192        11438 :                             && visited.count(link->getLane()) == 0) {
    1193              : #ifdef DEBUG_CHECK_FLANKS
    1194              :                         std::cout << " add crossing flankSwitch junction=" << junction->getID() << " index=" << link->getIndex() << "\n";
    1195              : #endif
    1196          624 :                         if (link->getViaLane() == nullptr) {
    1197              :                             flankSwitches.insert(link);
    1198              :                         } else {
    1199              :                             flankSwitches.insert(link->getViaLane()->getLinkCont().front());
    1200              :                         }
    1201              :                     }
    1202              :                 }
    1203              :             }
    1204              :         }
    1205              :     }
    1206              : }
    1207              : 
    1208              : void
    1209        14160 : MSDriveWay::findFlankProtection(MSLink* link, MSLink* origLink, std::vector<const MSLane*>& flank) {
    1210              : #ifdef DEBUG_CHECK_FLANKS
    1211              :     std::cout << "  findFlankProtection link=" << link->getDescription() << " origLink=" << origLink->getDescription() << "\n";
    1212              : #endif
    1213        14160 :     if (link->getCorrespondingEntryLink()->getTLLogic() != nullptr && link->getJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1214         2224 :         MSLink* entry = const_cast<MSLink*>(link->getCorrespondingEntryLink());
    1215              :         // guarded by signal
    1216              : #ifdef DEBUG_CHECK_FLANKS
    1217              :         std::cout << "   flank guarded by " << entry->getTLLogic()->getID() << "\n";
    1218              : #endif
    1219              :         // @note, technically it's enough to collect links from foe driveways
    1220              :         // but this also adds "unused" conflict links which may aid comprehension
    1221         2224 :         myConflictLinks.push_back(entry);
    1222         2224 :         addFoes(entry);
    1223              :     } else {
    1224              :         const MSLane* lane = link->getLaneBefore();
    1225              :         std::vector<MSLink*> predLinks;
    1226        23832 :         for (auto ili : lane->getIncomingLanes()) {
    1227        11896 :             if (!ili.viaLink->isTurnaround()) {
    1228        11497 :                 predLinks.push_back(ili.viaLink);
    1229              :             }
    1230              :         }
    1231        11936 :         if (predLinks.size() > 1) {
    1232              :             // this is a switch
    1233              : #ifdef DEBUG_ADD_FOES
    1234              :             std::cout << "    predecessors of " << link->getDescription() << " isSwitch\n";
    1235              : #endif
    1236          687 :             for (MSLink* pred : predLinks) {
    1237          458 :                 addSwitchFoes(pred);
    1238              :             }
    1239        11707 :         } else if (predLinks.size() == 1) {
    1240        11039 :             if (isSwitch(link)) {
    1241         9622 :                 addSwitchFoes(link);
    1242              :             } else {
    1243              :                 // continue upstream via single predecessor
    1244         1417 :                 findFlankProtection(predLinks.front(), origLink, flank);
    1245              :             }
    1246              :         }
    1247              :         // check for insertions
    1248        11936 :         if (myDepartureDriveways.count(&lane->getEdge()) != 0) {
    1249          306 :             for (MSDriveWay* foe : myDepartureDriveways[&lane->getEdge()]) {
    1250          155 :                 if (flankConflict(*foe) || crossingConflict(*foe)) {
    1251              : #ifdef DEBUG_ADD_FOES
    1252              :                     std::cout << "  foe " << foe->getID() << " departs on flank=" << lane->getID() << "\n";
    1253              : #endif
    1254          112 :                     myFoes.push_back(foe);
    1255              :                 } else {
    1256              : #ifdef DEBUG_ADD_FOES
    1257              :                     std::cout << "  cand foe " << foe->getID() << " departs on flank=" << lane->getID() << " rejected\n";
    1258              : #endif
    1259              :                 }
    1260              :             }
    1261              :         }
    1262        11936 :     }
    1263        14160 : }
    1264              : 
    1265              : 
    1266              : void
    1267        10080 : MSDriveWay::addSwitchFoes(MSLink* link) {
    1268              :     auto it = mySwitchDriveWays.find(link);
    1269        10080 :     if (it != mySwitchDriveWays.end()) {
    1270              : #ifdef DEBUG_ADD_FOES
    1271              :         std::cout << "   driveway " << myID << " addSwitchFoes for link " << link->getDescription() << "\n";
    1272              : #endif
    1273        33920 :         for (MSDriveWay* foe : it->second) {
    1274        30194 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1275              : #ifdef DEBUG_ADD_FOES
    1276              :                 std::cout << "   foe=" << foe->myID
    1277              :                           << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this)
    1278              :                           << " cc1=" << crossingConflict(*foe) << " cc2=" << foe->crossingConflict(*this) << "\n";
    1279              : #endif
    1280         3780 :                 myFoes.push_back(foe);
    1281              :             } else {
    1282              : #ifdef DEBUG_ADD_FOES
    1283              :                 std::cout << "   cand=" << foe->myID << "\n";
    1284              : #endif
    1285              :             }
    1286              :         }
    1287              :     }
    1288        10080 : }
    1289              : 
    1290              : 
    1291              : MSDriveWay*
    1292         8136 : MSDriveWay::buildDriveWay(const std::string& id, const MSLink* link, MSRouteIterator first, MSRouteIterator end) {
    1293              :     // collect lanes and links that are relevant for setting this signal for the current driveWay
    1294              :     // For each driveway we collect
    1295              :     //   - conflictLanes (signal must be red if any conflict lane is occupied)
    1296              :     //   - conflictLinks (signal must be red if any conflict link is approached by a vehicle
    1297              :     //      - that cannot break in time (arrivalSpeedBraking > 0)
    1298              :     //      - approached by a vehicle with higher switching priority (see #3941)
    1299              :     // These objects are construct in steps:
    1300              :     //
    1301              :     // forwardBlock
    1302              :     // - search forward recursive from outgoing lane until controlled railSignal link found
    1303              :     //   -> add all found lanes to conflictLanes
    1304              :     //
    1305              :     // bidiBlock (if any forwardBlock edge has bidi edge)
    1306              :     // - search bidi backward recursive until first switch
    1307              :     //   - from switch search backward recursive all other incoming until controlled rail signal link
    1308              :     //     -> add final links to conflictLinks
    1309              :     //
    1310              :     // flanks
    1311              :     // - search backward recursive from flanking switches
    1312              :     //   until controlled railSignal link or protecting switch is found
    1313              :     //   -> add all found lanes to conflictLanes
    1314              :     //   -> add final links to conflictLinks
    1315         8136 :     MSDriveWay* dw = new MSDriveWay(link, id);
    1316              :     LaneVisitedMap visited;
    1317              :     std::vector<const MSLane*> before;
    1318         8136 :     MSLane* fromBidi = nullptr;
    1319         8136 :     if (link != nullptr) {
    1320         5044 :         appendMapIndex(visited, link->getLaneBefore());
    1321         5044 :         fromBidi = link->getLaneBefore()->getBidiLane();
    1322              :     }
    1323              :     std::set<MSLink*> flankSwitches; // list of switches that threaten the driveway and for which protection must be found
    1324              : 
    1325         8136 :     if (fromBidi != nullptr) {
    1326         2768 :         before.push_back(fromBidi);
    1327              :     }
    1328         8136 :     dw->buildRoute(link, first, end, visited, flankSwitches);
    1329         8136 :     dw->myCoreSize = (int)dw->myRoute.size();
    1330              : 
    1331         8136 :     MSRailSignal* rs = link ? const_cast<MSRailSignal*>(static_cast<const MSRailSignal*>(link->getTLLogic())) : nullptr;
    1332         8136 :     const bool movingBlock = (rs && rs->isMovingBlock()) || (!rs &&
    1333        11228 :             (OptionsCont::getOptions().getBool("railsignal-moving-block")
    1334         3004 :              || MSRailSignalControl::isMovingBlock((*first)->getPermissions())));
    1335              : 
    1336         8136 :     dw->checkFlanks(link, dw->myForward, visited, true, movingBlock, flankSwitches);
    1337         8136 :     dw->checkFlanks(link, dw->myBidi, visited, false, movingBlock, flankSwitches);
    1338         8136 :     dw->checkFlanks(link, before, visited, true, movingBlock, flankSwitches);
    1339        16869 :     for (MSLink* fsLink : flankSwitches) {
    1340              : #ifdef DEBUG_ADD_FOES
    1341              :         if (DEBUG_COND_DW(dw)) {
    1342              :             std::cout << " fsLink=" << fsLink->getDescription() << "\n";
    1343              :         }
    1344              : #endif
    1345         8733 :         dw->findFlankProtection(fsLink, fsLink, dw->myFlank);
    1346              :     }
    1347              :     std::set<MSLink*> flankSwitchesBidiExtended;
    1348         8136 :     dw->checkFlanks(link, dw->myBidiExtended, visited, false, movingBlock, flankSwitchesBidiExtended);
    1349        12146 :     for (MSLink* const flink : flankSwitchesBidiExtended) {
    1350              : #ifdef DEBUG_ADD_FOES
    1351              :         if (DEBUG_COND_DW(dw)) {
    1352              :             std::cout << " fsLinkExtended=" << flink->getDescription() << "\n";
    1353              :         }
    1354              : #endif
    1355         4010 :         dw->findFlankProtection(flink, flink, dw->myBidiExtended);
    1356              :     }
    1357              : #ifdef DEBUG_BUILD_DRIVEWAY
    1358              :     if (DEBUG_COND_DW(dw)) {
    1359              :         std::cout << SIMTIME << " buildDriveWay " << dw->myID << " link=" << (link == nullptr ? "NULL" : link->getDescription())
    1360              :                   << "\n    route=" << toString(dw->myRoute)
    1361              :                   << "\n    forward=" << toString(dw->myForward)
    1362              :                   << "\n    bidi=" << toString(dw->myBidi)
    1363              :                   << "\n    bidiEx=" << toString(dw->myBidiExtended)
    1364              :                   << "\n    flank=" << toString(dw->myFlank)
    1365              :                   << "\n    flankSwitch=" << MSRailSignal::describeLinks(std::vector<MSLink*>(flankSwitches.begin(), flankSwitches.end()))
    1366              :                   << "\n    coreSize=" << dw->myCoreSize
    1367              :                   << "\n";
    1368              :     }
    1369              : #endif
    1370         8136 :     if (!rs || !rs->isMovingBlock()) {
    1371         8060 :         dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myForward.begin(), dw->myForward.end());
    1372              :     }
    1373         8136 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myBidi.begin(), dw->myBidi.end());
    1374         8136 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myFlank.begin(), dw->myFlank.end());
    1375         8136 :     dw->addBidiFoes(rs, false);
    1376         8136 :     dw->addBidiFoes(rs, true);
    1377              :     // add driveways that start on the same signal / lane
    1378         8136 :     dw->addParallelFoes(link, *first);
    1379              :     // add driveways that reverse along this driveways route
    1380         8136 :     dw->addReversalFoes(movingBlock);
    1381              :     // make foes unique and symmetrical
    1382         8136 :     std::set<MSDriveWay*, ComparatorNumericalIdLess> uniqueFoes(dw->myFoes.begin(), dw->myFoes.end());
    1383              :     dw->myFoes.clear();
    1384              :     // check for self-intersecting forward-section in movingBlock mode
    1385         8136 :     if (movingBlock && uniqueFoes.count(dw) == 0) {
    1386              :         std::set<const MSJunction*> forwardJunctions;
    1387         2506 :         for (const MSLane* fw : dw->myForward) {
    1388         2323 :             if (fw->isNormal()) {
    1389         1289 :                 const MSJunction* fwTo = fw->getEdge().getToJunction();
    1390              :                 if (forwardJunctions.count(fwTo) == 1) {
    1391           21 :                     dw->myFoes.push_back(dw);
    1392              : #ifdef DEBUG_ADD_FOES
    1393              :                     if (DEBUG_COND_DW(dw)) {
    1394              :                         std::cout << " self-intersecting movingBlock for dw=" << dw->getID() << "\n";
    1395              :                     }
    1396              : #endif
    1397           21 :                     break;
    1398              :                 }
    1399              :                 forwardJunctions.insert(fwTo);
    1400              :             }
    1401              :         }
    1402              :     }
    1403         8136 :     std::set<MSLink*> uniqueCLink(dw->myConflictLinks.begin(), dw->myConflictLinks.end());
    1404         8136 :     const MSEdge* lastEdge = &dw->myForward.back()->getEdge();
    1405        14380 :     for (MSDriveWay* foe : uniqueFoes) {
    1406         6244 :         const MSEdge* foeLastEdge = &foe->myForward.back()->getEdge();
    1407         6244 :         const bool sameLast = foeLastEdge == lastEdge;
    1408         6244 :         if (sameLast && !movingBlock) {
    1409         2453 :             dw->myFoes.push_back(foe);
    1410         2453 :             if (foe != dw) {
    1411         2453 :                 foe->myFoes.push_back(dw);
    1412              :             }
    1413              :         } else {
    1414         3791 :             if (foe->bidiBlockedByEnd(*dw)) {
    1415              : #ifdef DEBUG_ADD_FOES
    1416              :                 if (DEBUG_COND_DW(dw)) {
    1417              :                     std::cout << " setting " << dw->getID() << " as foe of " << foe->getID() << "\n";
    1418              :                 }
    1419              : #endif
    1420         1865 :                 foe->myFoes.push_back(dw);
    1421         1865 :                 foe->addSidings(dw);
    1422              :             } else {
    1423         1926 :                 dw->buildSubFoe(foe, movingBlock);
    1424              :             }
    1425         3791 :             if (foe != dw) { // check for movingBlock
    1426         3768 :                 if (dw->bidiBlockedByEnd(*foe)) {
    1427              : #ifdef DEBUG_ADD_FOES
    1428              :                     if (DEBUG_COND_DW(dw)) {
    1429              :                         std::cout << " addFoeCheckSiding " << foe->getID() << "\n";
    1430              :                     }
    1431              : #endif
    1432         2043 :                     dw->myFoes.push_back(foe);
    1433         2043 :                     dw->addSidings(foe);
    1434              :                 } else  {
    1435         1725 :                     foe->buildSubFoe(dw, movingBlock);
    1436              :                 }
    1437              :             }
    1438              :         }
    1439         6244 :         if (link) {
    1440         4563 :             foe->addConflictLink(link);
    1441              :         }
    1442              :         // ignore links that have the same start junction
    1443         6244 :         if (foe->myRoute.front()->getFromJunction() != dw->myRoute.front()->getFromJunction()) {
    1444         7764 :             for (auto ili : foe->myForward.front()->getIncomingLanes()) {
    1445         3569 :                 if (ili.viaLink->getTLLogic() != nullptr) {
    1446              :                     // ignore links that originate on myBidi
    1447         3119 :                     const MSLane* origin = ili.viaLink->getLaneBefore();
    1448         3119 :                     if (std::find(dw->myBidi.begin(), dw->myBidi.end(), origin) == dw->myBidi.end()) {
    1449              :                         uniqueCLink.insert(ili.viaLink);
    1450              :                     }
    1451              :                 }
    1452              :             }
    1453              :         }
    1454              :     }
    1455              :     dw->myConflictLinks.clear();
    1456         8136 :     dw->myConflictLinks.insert(dw->myConflictLinks.begin(), uniqueCLink.begin(), uniqueCLink.end());
    1457         8136 :     myEndingDriveways[lastEdge].push_back(dw);
    1458         8136 :     if (!movingBlock) {
    1459              :         // every driveway is it's own foe (also all driveways that depart in the same block)
    1460        18477 :         for (MSDriveWay* sameEnd : myEndingDriveways[lastEdge]) {
    1461              :             if (uniqueFoes.count(sameEnd) == 0) {
    1462         8115 :                 dw->myFoes.push_back(sameEnd);
    1463         8115 :                 if (sameEnd != dw) {
    1464          206 :                     sameEnd->myFoes.push_back(dw);
    1465              :                 }
    1466              :             }
    1467              :         }
    1468              :     }
    1469              : #ifdef DEBUG_BUILD_DRIVEWAY
    1470              :     if (DEBUG_COND_DW(dw)) {
    1471              :         std::cout << dw->myID << " mb=" << movingBlock << " finalFoes " << toString(dw->myFoes) << "\n";
    1472              :     }
    1473              : #endif
    1474         8136 :     return dw;
    1475         8136 : }
    1476              : 
    1477              : std::string
    1478         3733 : MSDriveWay::getTLLinkID(const MSLink* link) {
    1479         7466 :     return link->getTLLogic()->getID() + "_" + toString(link->getTLIndex());
    1480              : }
    1481              : 
    1482              : std::string
    1483            0 : MSDriveWay::getJunctionLinkID(const MSLink* link) {
    1484            0 :     return link->getJunction()->getID() + "_" + toString(link->getIndex());
    1485              : }
    1486              : 
    1487              : std::string
    1488         5044 : MSDriveWay::getClickableTLLinkID(const MSLink* link) {
    1489        15132 :     return "junction '" +  link->getTLLogic()->getID() + "', link " + toString(link->getTLIndex());
    1490              : }
    1491              : 
    1492              : std::string
    1493            0 : MSDriveWay::formatVisitedMap(const LaneVisitedMap& visited) {
    1494              :     UNUSED_PARAMETER(visited);
    1495              :     /*
    1496              :     std::vector<const MSLane*> lanes(visited.size(), nullptr);
    1497              :     for (auto item : visited) {
    1498              :         lanes[item.second] = item.first;
    1499              :     }
    1500              :     for (auto it = lanes.begin(); it != lanes.end();) {
    1501              :         if (*it == nullptr) {
    1502              :             it = lanes.erase(it);
    1503              :         } else {
    1504              :             it++;
    1505              :         }
    1506              :     }
    1507              :     return toString(lanes);
    1508              :     */
    1509            0 :     return "dummy";
    1510              : }
    1511              : 
    1512              : 
    1513              : void
    1514       104200 : MSDriveWay::appendMapIndex(LaneVisitedMap& map, const MSLane* lane) {
    1515              :     // avoid undefined behavior from evaluation order
    1516       104200 :     const int tmp = (int)map.size();
    1517       104200 :     map[lane] = tmp;
    1518       104200 : }
    1519              : 
    1520              : bool
    1521      9424366 : MSDriveWay::match(MSRouteIterator firstIt, MSRouteIterator endIt) const {
    1522              :     // @todo optimize: it is sufficient to check for specific edges (after each switch)
    1523      9424366 :     auto itRoute = firstIt;
    1524              :     auto itDwRoute = myRoute.begin();
    1525              :     bool match = true;
    1526     64977408 :     while (itRoute != endIt && itDwRoute != myRoute.end()) {
    1527     55743807 :         if (*itRoute != *itDwRoute) {
    1528              :             match = false;
    1529              : #ifdef DEBUG_MATCH
    1530              :             std::cout << "  check dw=" << getID() << " match failed at vehEdge=" << (*itRoute)->getID() << " dwEdge=" << (*itDwRoute)->getID() << "\n";
    1531              : #endif
    1532              :             break;
    1533              :         }
    1534              :         itRoute++;
    1535              :         itDwRoute++;
    1536              :     }
    1537              :     // if the vehicle arrives before the end of this driveway,
    1538              :     // we'd rather build a new driveway to avoid superfluous restrictions
    1539      9233601 :     if (match && itDwRoute == myRoute.end()
    1540     18631358 :             && (itRoute == endIt || myAbortedBuild || myBidiEnded || myFoundJump || myIsSubDriveway)) {
    1541              :         //std::cout << "  using dw=" << "\n";
    1542      9193970 :         if (itRoute != endIt) {
    1543              :             // check whether the current route requires an extended driveway
    1544       142384 :             const MSEdge* next = *itRoute;
    1545       142384 :             const MSEdge* prev = myRoute.back();
    1546          478 :             if (myFoundJump && prev->getBidiEdge() != next && prev->getBidiEdge() != nullptr
    1547       142635 :                     && prev->isConnectedTo(*next, (SUMOVehicleClass)(SVC_RAIL_CLASSES & prev->getPermissions()))) {
    1548              : #ifdef DEBUG_MATCH
    1549              :                 std::cout << "  check dw=" << getID() << " prev=" << prev->getID() << " next=" << next->getID() << "\n";
    1550              : #endif
    1551              :                 return false;
    1552              :             }
    1553       142367 :             if (!myFoundJump && prev->getBidiEdge() == next && prev == &myForward.back()->getEdge()) {
    1554              :                 assert(myIsSubDriveway || myBidiEnded);
    1555              :                 // must not leave driveway via reversal
    1556              : #ifdef DEBUG_MATCH
    1557              :                 std::cout << getID() << " back=" << myForward.back()->getID() << " noMatch route " << toString(ConstMSEdgeVector(firstIt, endIt)) << "\n";
    1558              : #endif
    1559              :                 return false;
    1560              :             }
    1561              :         }
    1562      9193947 :         return true;
    1563              :     }
    1564              :     return false;
    1565              : }
    1566              : 
    1567              : void
    1568        11523 : MSDriveWay::addFoes(const MSLink* link) {
    1569              : #ifdef DEBUG_ADD_FOES
    1570              :     std::cout << "driveway " << myID << " addFoes for link " << link->getDescription() << "\n";
    1571              : #endif
    1572        11523 :     const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(link->getTLLogic());
    1573        11523 :     if (rs != nullptr) {
    1574        15925 :         for (MSDriveWay* foe : rs->retrieveDriveWays(link->getTLIndex())) {
    1575              : #ifdef DEBUG_ADD_FOES
    1576              :             std::cout << "  cand foe=" << foe->myID << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this) << " cc1=" << crossingConflict(*foe) << " cc2=" <<  foe->crossingConflict(*this) << "\n";
    1577              : #endif
    1578         4402 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1579              : #ifdef DEBUG_ADD_FOES
    1580              :                 std::cout << "   foe=" << foe->myID << "\n";
    1581              : #endif
    1582         3333 :                 myFoes.push_back(foe);
    1583              :             }
    1584        11523 :         }
    1585              :     }
    1586        11523 : }
    1587              : 
    1588              : 
    1589              : void
    1590        16272 : MSDriveWay::addBidiFoes(const MSRailSignal* ownSignal, bool extended) {
    1591              : #ifdef DEBUG_ADD_FOES
    1592              :     std::cout << "driveway " << myID << " addBidiFoes extended=" << extended << "\n";
    1593              : #endif
    1594        16272 :     const std::vector<const MSLane*>& bidiLanes = extended ? myBidiExtended : myBidi;
    1595        81062 :     for (const MSLane* bidi : bidiLanes) {
    1596       132674 :         for (auto ili : bidi->getIncomingLanes()) {
    1597        67884 :             const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(ili.viaLink->getTLLogic());
    1598        67884 :             if (rs != nullptr && rs != ownSignal &&
    1599        67884 :                     std::find(bidiLanes.begin(), bidiLanes.end(), ili.lane) != bidiLanes.end()) {
    1600         4255 :                 addFoes(ili.viaLink);
    1601              :             }
    1602              :         }
    1603        64790 :         const MSEdge* bidiEdge = &bidi->getEdge();
    1604              :         if (myDepartureDriveways.count(bidiEdge) != 0) {
    1605         3109 :             for (MSDriveWay* foe : myDepartureDriveways[bidiEdge]) {
    1606         1439 :                 if (flankConflict(*foe)) {
    1607              : #ifdef DEBUG_ADD_FOES
    1608              :                     std::cout << "  foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << "\n";
    1609              : #endif
    1610          855 :                     myFoes.push_back(foe);
    1611              :                 } else {
    1612              : #ifdef DEBUG_ADD_FOES
    1613              :                     std::cout << "  cand foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << " rejected\n";
    1614              : #endif
    1615              :                 }
    1616              :             }
    1617              :         }
    1618              :         if (myDepartureDrivewaysEnds.count(bidiEdge) != 0) {
    1619         2647 :             for (MSDriveWay* foe : myDepartureDrivewaysEnds[bidiEdge]) {
    1620         1368 :                 if (flankConflict(*foe)) {
    1621              : #ifdef DEBUG_ADD_FOES
    1622              :                     std::cout << "  foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << "\n";
    1623              : #endif
    1624          844 :                     myFoes.push_back(foe);
    1625              :                 } else {
    1626              : #ifdef DEBUG_ADD_FOES
    1627              :                     std::cout << "  cand foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << " rejected\n";
    1628              : #endif
    1629              :                 }
    1630              :             }
    1631              :         }
    1632              :     }
    1633        16272 : }
    1634              : 
    1635              : 
    1636              : void
    1637         8136 : MSDriveWay::addParallelFoes(const MSLink* link, const MSEdge* first) {
    1638              : #ifdef DEBUG_ADD_FOES
    1639              :     std::cout << "driveway " << myID << " addParallelFoes\n";
    1640              : #endif
    1641         8136 :     if (link) {
    1642         5044 :         addFoes(link);
    1643              :     } else {
    1644              :         auto it = myDepartureDriveways.find(first);
    1645         3092 :         if (it != myDepartureDriveways.end()) {
    1646         3271 :             for (MSDriveWay* foe : it->second) {
    1647              : #ifdef DEBUG_ADD_FOES
    1648              :                 std::cout << "  foe " << foe->getID() << " departs on first=" << first->getID() << "\n";
    1649              : #endif
    1650          206 :                 myFoes.push_back(foe);
    1651              :             }
    1652              :         }
    1653              :     }
    1654         8136 : }
    1655              : 
    1656              : 
    1657              : void
    1658         8136 : MSDriveWay::addReversalFoes(bool movingBlock) {
    1659              : #ifdef DEBUG_ADD_FOES
    1660              :     std::cout << "driveway " << myID << " addReversalFoes\n";
    1661              : #endif
    1662              :     std::set<const MSEdge*> forward;
    1663        35681 :     for (const MSLane* lane : myForward) {
    1664        27545 :         if (lane->isNormal()) {
    1665        18227 :             forward.insert(&lane->getEdge());
    1666              :         }
    1667              :     }
    1668              :     int i = 0;
    1669        70222 :     for (const MSEdge* e : myRoute) {
    1670        18241 :         if (forward.count(e) != 0 && !movingBlock) {
    1671              :             // reversals in our own forward can be ignored because each driveway
    1672              :             // is automatically a foe of itself by default
    1673              :             continue;
    1674              :         }
    1675        45282 :         if (i == myCoreSize) {
    1676              :             break;
    1677              :         }
    1678        45282 :         i++;
    1679              :         auto it = myReversalDriveWays.find(e);
    1680        45282 :         if (it != myReversalDriveWays.end()) {
    1681         6325 :             for (MSDriveWay* foe : it->second) {
    1682              :                 // check whether the foe reverses into our own forward section
    1683              :                 // (it might reverse again or disappear via arrival)
    1684              : #ifdef DEBUG_ADD_FOES
    1685              :                 //std::cout << "  candidate foe " << foe->getID() << " reverses on edge=" << e->getID() << " forward=" << joinNamedToString(forward, " ") << " foeRoute=" << toString(foe->myRoute) << "\n";
    1686              : #endif
    1687        11144 :                 if (forwardRouteConflict(forward, *foe)) {
    1688              :                     std::set<const MSEdge*> foeForward;
    1689         1118 :                     for (const MSLane* lane : foe->myForward) {
    1690          937 :                         if (lane->isNormal()) {
    1691          588 :                             foeForward.insert(&lane->getEdge());
    1692          588 :                             if (lane->getBidiLane() != nullptr) {
    1693          577 :                                 foeForward.insert(lane->getEdge().getBidiEdge());
    1694              :                             }
    1695              :                         }
    1696              :                     }
    1697              : #ifdef DEBUG_ADD_FOES
    1698              :                     std::cout << "  reversal cand=" << foe->getID() << " foeForward " << toString(foeForward) << "\n";
    1699              : #endif
    1700          362 :                     if (foe->forwardRouteConflict(foeForward, *this, true)) {
    1701              : #ifdef DEBUG_ADD_FOES
    1702              :                         std::cout << "  foe " << foe->getID() << " reverses on edge=" << e->getID() << "\n";
    1703              : #endif
    1704          141 :                         myFoes.push_back(foe);
    1705              :                     }
    1706         5391 :                 } else if (movingBlock && foe == this) {
    1707              : #ifdef DEBUG_ADD_FOES
    1708              :                     std::cout << "  dw " << getID() << " reverses on forward edge=" << e->getID() << " (movingBlock)\n";
    1709              : #endif
    1710           23 :                     myFoes.push_back(foe);
    1711              :                 }
    1712              :             }
    1713              :         }
    1714              :     }
    1715         8136 : }
    1716              : 
    1717              : 
    1718              : bool
    1719         3651 : MSDriveWay::buildSubFoe(MSDriveWay* foe, bool movingBlock) {
    1720              :     // Subdriveways (Teilfahrstraße) model the resolution of a driving conflict
    1721              :     // before a vehicle has left the driveway. This is possible when the driveway diverges from the foe
    1722              :     // driveway at an earlier point (switch or crossing).
    1723              :     //
    1724              :     // We already know that the last edge of this driveway doesn't impact the foe (unless the driveway ends within the block).
    1725              :     // Remove further edges from the end of the driveway (myForward) until the point of conflict is found.
    1726              :     //
    1727              :     // For movingBlock the logic is changed:
    1728              :     // We remove the conflict-free part as before but then keep removing the conflict part until another non-conconflit part is found
    1729         3651 :     if (myForward.size() < foe->myForward.size() &&
    1730         3651 :             myForward == std::vector<const MSLane*>(foe->myForward.begin(), foe->myForward.begin() + myForward.size())) {
    1731              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1732              :         std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " is subpart of foe=" << foe->getID() << "\n";
    1733              : #endif
    1734           31 :         foe->myFoes.push_back(this);
    1735           31 :         return true;
    1736              :     }
    1737         3620 :     int subLast = (int)myForward.size() - 2;
    1738         3620 :     if (movingBlock && myForward.back() == foe->myForward.back()) {
    1739           63 :         subLast++;
    1740              :     }
    1741              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1742              :     if (subLast < 0) {
    1743              :         std::cout << "  " << getID() << " cannot build subDriveWay for foe " << foe->getID() << " because myForward has only a single lane\n";
    1744              :     }
    1745              : #endif
    1746              :     bool foundConflict = false;
    1747              :     bool flankC = false;
    1748              :     bool zipperC = false;
    1749              :     bool crossC = false;
    1750        13009 :     while (subLast >= 0) {
    1751        12257 :         const MSLane* lane = myForward[subLast];
    1752        12257 :         const MSLink* tmpOrigin = subLast > 0 ? myForward[subLast - 1]->getLinkTo(lane) : myOrigin;
    1753        12257 :         MSDriveWay tmp(tmpOrigin, "tmp", true);
    1754        12257 :         tmp.myForward.push_back(lane);
    1755        12257 :         flankC = tmp.flankConflict(*foe);
    1756        12257 :         const bool bidiConflict = std::find(foe->myBidi.begin(), foe->myBidi.end(), lane) != foe->myBidi.end();
    1757        12257 :         crossC = tmp.crossingConflict(*foe);
    1758              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1759              :         std::cout << "  subLast=" << subLast << " lane=" << lane->getID() << " fc=" << flankC << " cc=" << crossC << " bc=" << bidiConflict << "\n";
    1760              : #endif
    1761        12257 :         if (flankC || crossC || bidiConflict) {
    1762              :             foundConflict = true;
    1763         3274 :             if (!movingBlock || bidiConflict) {
    1764              :                 break;
    1765              :             }
    1766          442 :             if (((flankC && lane->getFromJunction()->getType() == SumoXMLNodeType::ZIPPER)
    1767           46 :                     || (!flankC && lane->getToJunction()->getType() == SumoXMLNodeType::ZIPPER))
    1768          518 :                     && (isDepartDriveway()
    1769           18 :                         || getForwardDistance(flankC ? subLast - 1 : subLast) > myMovingBlockMaxDist)) {
    1770              :                 zipperC = true;
    1771              :                 foundConflict = false;
    1772              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1773              :                 std::cout << "     ignored movingBlock zipperConflict\n";
    1774              : #endif
    1775           18 :                 if (!flankC && crossC) {
    1776              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1777              :                     std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " movingBlock-save\n";
    1778              : #endif
    1779              :                     return false;
    1780              :                 }
    1781              :             }
    1782          479 :             if (!flankC && crossC) {
    1783              :                 break;
    1784              :             }
    1785         8983 :         } else if (foundConflict) {
    1786              :             break;
    1787              :         }
    1788         9389 :         subLast--;
    1789        12257 :     }
    1790              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1791              :     std::cout << "  subLastFina=" << subLast << " movingBlock=" << movingBlock << " zipperC=" << zipperC << "\n";
    1792              : #endif
    1793         3611 :     if (subLast < 0) {
    1794          752 :         if (movingBlock && zipperC) {
    1795              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1796              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " movingBlock-save\n";
    1797              : #endif
    1798              :             return false;
    1799          752 :         } else if (&myForward.back()->getEdge() == myRoute.back() && foe->forwardEndOnRoute(this)) {
    1800              :             // driveway ends in the middle of the block and only the final edge overlaps with the foe driveWay
    1801           15 :             foe->myFoes.push_back(this);
    1802              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1803              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " foe endsOnForward\n";
    1804              : #endif
    1805          737 :         } else if (foe->myTerminateRoute) {
    1806          609 :             if (bidiBlockedByEnd(*foe) && bidiBlockedBy(*this) && foe->forwardEndOnRoute(this)) {
    1807           89 :                 foe->myFoes.push_back(this);
    1808              :                 // foe will get the sidings
    1809           89 :                 addSidings(foe, true);
    1810              :             }
    1811              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1812              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " terminates\n";
    1813              : #endif
    1814          128 :         } else if (myTerminateRoute && myBidi.size() <= myForward.size()) {
    1815            0 :             foe->myFoes.push_back(this);
    1816              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1817              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " terminates, foe=" << foe->getID() << "\n";
    1818              : #endif
    1819            0 :             return true;
    1820              :         } else if (foe->myReversals.size() % 2 == 1) {
    1821              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1822              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " has " << foe->myReversals.size() << " reversals\n";
    1823              : #endif
    1824              :         } else {
    1825              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1826              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " failed\n";
    1827              : #endif
    1828              : #ifdef SUBDRIVEWAY_WARN_NOCONFLICT
    1829              :             WRITE_WARNINGF("No point of conflict found between driveway '%' and driveway '%' when creating sub-driveway", getID(), foe->getID());
    1830              : #endif
    1831              :         }
    1832          752 :         return false;
    1833              :     }
    1834         2859 :     int subSize = subLast + 1;
    1835         3630 :     for (MSDriveWay* cand : mySubDriveWays) {
    1836         1438 :         if ((int)cand->myForward.size() == subSize) {
    1837              :             // can re-use existing sub-driveway
    1838          667 :             foe->myFoes.push_back(cand);
    1839          667 :             cand->myFoes.push_back(foe);
    1840              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1841              :             std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " useExisting=" << cand->getID() << "\n";
    1842              : #endif
    1843          667 :             return true;
    1844              :         }
    1845              :     }
    1846         2192 :     std::vector<const MSLane*> forward(myForward.begin(), myForward.begin() + subSize);
    1847              :     std::vector<const MSEdge*> route;
    1848        11157 :     for (const MSLane* lane : forward) {
    1849         8965 :         if (lane->isNormal()) {
    1850         5200 :             route.push_back(&lane->getEdge());
    1851              :         }
    1852              :     }
    1853         2192 :     if (route.empty()) {
    1854           34 :         if (subSize == 1 && crossC
    1855           34 :                 && forward.front()->getFromJunction() == foe->myForward.front()->getFromJunction()
    1856           68 :                 && forward.front()->getFromJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1857              :             assert(myForward.front()->isInternal());
    1858              :             // sub-driveway ends after a single internal lane but since the route cannot be empty we add the next edge
    1859           34 :             route.push_back(foe->myForward.front()->getEdge().getNormalSuccessor());
    1860              :         }  else {
    1861              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1862              :             std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID() << " empty subRoute\n";
    1863              : #endif
    1864            0 :             return false;
    1865              :         }
    1866              :     }
    1867         2192 :     if (myRoute.size() > route.size()) {
    1868              :         // route continues. make sure the subDriveway does not end with a reversal
    1869         2185 :         const MSEdge* lastNormal = route.back();
    1870         2185 :         const MSEdge* nextNormal = myRoute[route.size()];
    1871         2185 :         if (lastNormal->getBidiEdge() == nextNormal) {
    1872              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1873              :             std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID()
    1874              :                       << " lastNormal=" << lastNormal->getID() << " nextNormal=" << nextNormal->getID() << " endWithReversal\n";
    1875              : #endif
    1876              :             return false;
    1877              :         }
    1878              :     }
    1879         4344 :     MSDriveWay* sub = new MSDriveWay(myOrigin, getID() + "." + toString(mySubDriveWays.size()));
    1880         2172 :     sub->myLane = myLane;
    1881         2172 :     sub->myIsSubDriveway = true;
    1882         2172 :     sub->myForward = forward;
    1883         2172 :     sub->myRoute = route;
    1884         2172 :     sub->myCoreSize = (int)sub->myRoute.size();
    1885         2172 :     myLane->addMoveReminder(sub, false);
    1886              : 
    1887              :     // copy trains that are currently on this driveway (and associated entry events)
    1888         2306 :     for (SUMOVehicle* veh : myTrains) {
    1889          134 :         auto itOnSub = std::find(sub->myRoute.begin(), sub->myRoute.end(), veh->getEdge());
    1890          134 :         if (itOnSub != sub->myRoute.end()) {
    1891              :             sub->myTrains.insert(veh);
    1892              :             // non-zero is enough to avoid superfluous activation via activateReminders (and removal)
    1893          117 :             const double pos = sub->myRoute.front()->getLength();
    1894          117 :             dynamic_cast<MSBaseVehicle*>(veh)->addReminder(sub, pos);
    1895          124 :             for (const VehicleEvent& ve : myVehicleEvents) {
    1896            7 :                 if (ve.id == veh->getID()) {
    1897            7 :                     sub->myVehicleEvents.push_back(ve);
    1898              :                 }
    1899              :             }
    1900              :         }
    1901              :     }
    1902              : 
    1903         2172 :     foe->myFoes.push_back(sub);
    1904         2172 :     sub->myFoes.push_back(foe);
    1905         2172 :     mySubDriveWays.push_back(sub);
    1906              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1907              :     std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " sub=" << sub->getID() << " route=" << toString(sub->myRoute) << "\n";
    1908              : #endif
    1909              :     return true;
    1910         2192 : }
    1911              : 
    1912              : 
    1913              : double
    1914           18 : MSDriveWay::getForwardDistance(int lastIndex) const {
    1915              :     assert(lastIndex < (int)myForward.size());
    1916              :     double result = 0;
    1917           54 :     for (int i = 0; i <= lastIndex; i++) {
    1918           36 :         result += myForward[i]->getLength();
    1919              :     }
    1920           18 :     return result;
    1921              : }
    1922              : 
    1923              : 
    1924              : void
    1925         3997 : MSDriveWay::addSidings(MSDriveWay* foe, bool addToFoe) {
    1926         3997 :     const MSEdge* foeEndBidi = foe->myForward.back()->getEdge().getBidiEdge();
    1927              :     int forwardNormals = 0;
    1928        18076 :     for (auto lane : foe->myForward) {
    1929        14079 :         if (lane->isNormal()) {
    1930         9384 :             forwardNormals++;
    1931              :         }
    1932              :     }
    1933         3997 :     if (forwardNormals == (int)foe->myRoute.size()) {
    1934              : #ifdef DEBUG_BUILD_SIDINGS
    1935              :         std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " forwardNormals=" << forwardNormals << " frSize=" << foe->myRoute.size() <<  " aborted\n";
    1936              : #endif
    1937          778 :         return;
    1938              :     }
    1939              :     auto foeSearchBeg = foe->myRoute.begin() + forwardNormals;
    1940              :     auto foeSearchEnd = foe->myRoute.end();
    1941         3219 :     if (foeEndBidi == nullptr) {
    1942            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " noBidi\n");
    1943              :     }
    1944              :     int i;
    1945              :     std::vector<int> start;
    1946              :     std::vector<double> length;
    1947        21047 :     for (i = 0; i < (int)myRoute.size(); i++) {
    1948        21047 :         if (myRoute[i] == foeEndBidi) {
    1949              :             break;
    1950              :         }
    1951              :     }
    1952         3219 :     if (i == (int)myRoute.size()) {
    1953            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " foeEndBidi=" + foeEndBidi->getID() + " not on route\n");
    1954              :     }
    1955         3219 :     const MSEdge* next = myRoute[i];
    1956              : #ifdef DEBUG_BUILD_SIDINGS
    1957              :     std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " next=" << next->getID() << " forwardNormals=" << forwardNormals << " frSize=" << foe->myRoute.size() << " foeSearchBeg=" << (*foeSearchBeg)->getID() << "\n";
    1958              : #endif
    1959         3219 :     i--;
    1960        21047 :     for (; i >= 0; i--) {
    1961        17828 :         const MSEdge* cur = myRoute[i];
    1962        17828 :         if (hasRS(cur, next)) {
    1963         5243 :             if (std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge()) == foeSearchEnd) {
    1964         2876 :                 start.push_back(i);
    1965         2876 :                 length.push_back(0);
    1966              :             }
    1967              :         }
    1968        17828 :         if (!start.empty()) {
    1969         4689 :             auto itFind = std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge());
    1970         4689 :             if (itFind != foeSearchEnd) {
    1971              : #ifdef DEBUG_BUILD_SIDINGS
    1972              :                 std::cout << "endSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " curBidi=" << Named::getIDSecure(cur->getBidiEdge()) << " length=" << toString(length) << "\n";
    1973              : #endif
    1974          707 :                 const int firstIndex = i + 1;
    1975          707 :                 if (addToFoe) {
    1976           69 :                     auto& foeSidings = foe->mySidings[this];
    1977              :                     // indices must be mapped onto foe route;
    1978           69 :                     const MSEdge* first = myRoute[firstIndex];
    1979           69 :                     auto itFirst = std::find(foe->myRoute.begin(), foe->myRoute.end(), first);
    1980           69 :                     if (itFirst != foe->myRoute.end()) {
    1981          279 :                         for (int j = 0; j < (int)length.size(); j++) {
    1982          221 :                             const MSEdge* last = myRoute[start[j]];
    1983          221 :                             auto itLast = std::find(itFirst, foe->myRoute.end(), last);
    1984          221 :                             if (itLast != foe->myRoute.end()) {
    1985          221 :                                 foeSidings.insert(foeSidings.begin(), Siding((int)(itFirst - foe->myRoute.begin()), (int)(itLast - foe->myRoute.begin()), length[j]));
    1986              :                             }
    1987              :                         }
    1988              :                     }
    1989              :                 } else {
    1990          638 :                     auto& foeSidings = mySidings[foe];
    1991         2968 :                     for (int j = 0; j < (int)length.size(); j++) {
    1992         2330 :                         foeSidings.insert(foeSidings.begin(), Siding(firstIndex, start[j], length[j]));
    1993              :                     }
    1994              :                 }
    1995              :                 start.clear();
    1996              :                 length.clear();
    1997          707 :                 foeSearchBeg = itFind;
    1998              :             } else {
    1999        25300 :                 for (int j = 0; j < (int)length.size(); j++) {
    2000        21318 :                     length[j] += cur->getLength();
    2001              :                 }
    2002              :             }
    2003              :         }
    2004              :         next = cur;
    2005              :     }
    2006         3219 : }
    2007              : 
    2008              : 
    2009              : bool
    2010        17828 : MSDriveWay::hasRS(const MSEdge* cur, const MSEdge* next) {
    2011        17828 :     if (cur->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    2012              :         // check if there is a controlled link between cur and next
    2013        13815 :         for (auto lane : cur->getLanes()) {
    2014        14221 :             for (const MSLink* link : lane->getLinkCont()) {
    2015         9935 :                 if (&link->getLane()->getEdge() == next && link->getTLLogic() != nullptr) {
    2016              :                     return true;
    2017              :                 }
    2018              :             }
    2019              :         }
    2020              :     }
    2021              :     return false;
    2022              : }
    2023              : 
    2024              : 
    2025              : bool
    2026          116 : MSDriveWay::forwardEndOnRoute(const MSDriveWay* foe) const {
    2027          116 :     const MSEdge* foeForwardEnd = &foe->myForward.back()->getNormalPredecessorLane()->getEdge();
    2028          116 :     return std::find(myRoute.begin(), myRoute.end(), foeForwardEnd) != myRoute.end();
    2029              : }
    2030              : 
    2031              : void
    2032         4563 : MSDriveWay::addConflictLink(const MSLink* link) {
    2033         4563 :     if (link->getTLLogic() != nullptr) {
    2034              :         // ignore links that originate on myBidi
    2035              :         // and also links from the same junction as my own link
    2036         4563 :         const MSLane* origin = link->getLaneBefore();
    2037         4563 :         if (std::find(myBidi.begin(), myBidi.end(), origin) == myBidi.end()) {
    2038         3655 :             if (link->getJunction() != myRoute.front()->getFromJunction()) {
    2039         2185 :                 if (std::find(myConflictLinks.begin(), myConflictLinks.end(), link) == myConflictLinks.end()) {
    2040         1513 :                     myConflictLinks.push_back(const_cast<MSLink*>(link));
    2041              :                 }
    2042              :             }
    2043              :         }
    2044              :     }
    2045         4563 : }
    2046              : 
    2047              : void
    2048          285 : MSDriveWay::addDWDeadlock(const std::vector<const MSDriveWay*>& deadlockFoes) {
    2049              :     std::set<const MSDriveWay*> filtered;
    2050         1455 :     for (const MSDriveWay* foe : deadlockFoes) {
    2051         1170 :         if (std::find(myFoes.begin(), myFoes.end(), foe) == myFoes.end()) {
    2052              :             filtered.insert(foe);
    2053              :         }
    2054              :     }
    2055          285 :     if (std::find(myDeadlocks.begin(), myDeadlocks.end(), filtered) == myDeadlocks.end()) {
    2056           81 :         myDeadlocks.push_back(filtered);
    2057              :         //std::cout << getID() << " deadlockFoes=" << toString(deadlockFoes) << "\n";
    2058              :     }
    2059          285 : }
    2060              : 
    2061              : const MSDriveWay*
    2062        61638 : MSDriveWay::getDepartureDriveway(const SUMOVehicle* veh, bool init) {
    2063        61638 :     const MSEdge* edge = init ? veh->getRoute().getEdges()[veh->getDepartEdge()] : veh->getEdge();
    2064              :     assert(isRailwayOrShared(edge->getPermissions()));
    2065        61638 :     if (edge->getFromJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    2066         3490 :         for (const MSLane* lane : edge->getLanes()) {
    2067         3799 :             for (auto ili : lane->getIncomingLanes()) {
    2068         2328 :                 const MSLink* entry = ili.viaLink->getCorrespondingEntryLink();
    2069         2328 :                 const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(entry->getTLLogic());
    2070         1693 :                 if (rs != nullptr) {
    2071         1693 :                     const MSDriveWay* dw = &const_cast<MSRailSignal*>(rs)->retrieveDriveWayForVeh(entry->getTLIndex(), veh);
    2072         1693 :                     if (&dw->myForward.front()->getEdge() == edge) {
    2073              :                         return dw;
    2074              :                     }
    2075              :                 }
    2076              :             }
    2077              :         }
    2078              :     }
    2079        64774 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    2080        61682 :         auto matchStart = init ? veh->getRoute().begin() + veh->getDepartEdge() : veh->getCurrentRouteEdge();
    2081        61682 :         if (dw->match(matchStart, veh->getRoute().end())) {
    2082        57998 :             return dw;
    2083              :         }
    2084              :     }
    2085         6184 :     const std::string id = edge->getFromJunction()->getID() + ".d" + toString(myDepartDrivewayIndex[edge->getFromJunction()]++);
    2086         3092 :     MSDriveWay* dw = buildDriveWay(id, nullptr, veh->getCurrentRouteEdge(), veh->getRoute().end());
    2087         3092 :     myDepartureDriveways[edge].push_back(dw);
    2088         3092 :     myDepartureDrivewaysEnds[&dw->myForward.back()->getEdge()].push_back(dw);
    2089              :     dw->setVehicle(veh->getID());
    2090              :     return dw;
    2091              : }
    2092              : 
    2093              : 
    2094              : void
    2095         1412 : MSDriveWay::writeDepatureBlocks(OutputDevice& od, bool writeVehicles) {
    2096         3535 :     for (auto item  : myDepartureDriveways) {
    2097         2123 :         const MSEdge* edge = item.first;
    2098         2123 :         if (item.second.size() > 0) {
    2099         4246 :             od.openTag("departJunction");
    2100         2123 :             od.writeAttr(SUMO_ATTR_ID, edge->getFromJunction()->getID());
    2101         4449 :             for (const MSDriveWay* dw : item.second) {
    2102         2326 :                 if (writeVehicles) {
    2103          233 :                     dw->writeBlockVehicles(od);
    2104              :                 } else {
    2105         2093 :                     dw->writeBlocks(od);
    2106              :                 }
    2107              :             }
    2108         4246 :             od.closeTag(); // departJunction
    2109              :         }
    2110              :     }
    2111         1412 : }
    2112              : 
    2113              : void
    2114          479 : MSDriveWay::saveState(OutputDevice& out) {
    2115              :     // all driveways are in myEndingDriveways which makes it convenient
    2116          554 :     for (auto item : myEndingDriveways) {
    2117          164 :         for (MSDriveWay* dw : item.second) {
    2118           89 :             dw->_saveState(out);
    2119          107 :             for (MSDriveWay* sub : dw->mySubDriveWays) {
    2120           18 :                 sub->_saveState(out);
    2121              :             }
    2122              :         }
    2123              :     }
    2124          479 : }
    2125              : 
    2126              : void
    2127          107 : MSDriveWay::_saveState(OutputDevice& out) const {
    2128          107 :     if (!myTrains.empty() || haveSubTrains()) {
    2129           73 :         out.openTag(myIsSubDriveway ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
    2130           38 :         out.writeAttr(SUMO_ATTR_ID, getID());
    2131           76 :         out.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
    2132           38 :         if (!myTrains.empty()) {
    2133              :             std::vector<std::string> trainIDs;
    2134           76 :             for (SUMOVehicle* veh : myTrains) {
    2135           38 :                 trainIDs.push_back(veh->getID());
    2136              :             }
    2137           38 :             out.writeAttr(SUMO_ATTR_VEHICLES, toString(trainIDs));
    2138           38 :         }
    2139           76 :         out.closeTag();
    2140              :     }
    2141          107 : }
    2142              : 
    2143              : 
    2144              : bool
    2145           69 : MSDriveWay::haveSubTrains() const {
    2146           76 :     for (MSDriveWay* sub : mySubDriveWays) {
    2147            7 :         if (!sub->myTrains.empty()) {
    2148              :             return true;
    2149              :         }
    2150              :     }
    2151              :     return false;
    2152              : }
    2153              : 
    2154              : void
    2155           41 : MSDriveWay::loadState(const SUMOSAXAttributes& attrs, int tag) {
    2156           41 :     if ((int)myDriveWayRouteLookup.size() < myGlobalDriveWayIndex) {
    2157          106 :         for (auto item : myEndingDriveways) {
    2158          168 :             for (MSDriveWay* dw : item.second) {
    2159           86 :                 myDriveWayRouteLookup[dw->myRoute] = dw;
    2160              :             }
    2161              :         }
    2162              :     }
    2163           41 :     MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
    2164              :     bool ok;
    2165           41 :     const std::string id  = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    2166           41 :     const std::string edges = attrs.get<std::string>(SUMO_ATTR_EDGES, id.c_str(), ok);
    2167              :     ConstMSEdgeVector route;
    2168           41 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
    2169           41 :         MSEdge::parseEdgesList(edges, route, id);
    2170              :     }
    2171              :     MSDriveWay* dw = nullptr;
    2172           41 :     if (tag == SUMO_TAG_DRIVEWAY) {
    2173              :         auto it = myDriveWayRouteLookup.find(route);
    2174           38 :         if (it == myDriveWayRouteLookup.end()) {
    2175              :             //WRITE_WARNING(TLF("Unknown driveWay '%' with route '%'", id, edges));
    2176              :             //return;
    2177            0 :             throw ProcessError(TLF("Unknown driveWay '%' with route '%'", id, edges));
    2178              :         }
    2179           38 :         dw = it->second;
    2180           38 :         myDriveWayLookup[id] = dw;
    2181              :     } else {
    2182            3 :         std::string parentID = id.substr(0, id.rfind('.'));
    2183              :         auto it = myDriveWayLookup.find(parentID);
    2184            3 :         if (it == myDriveWayLookup.end()) {
    2185              :             //WRITE_WARNING(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
    2186              :             //return;
    2187            0 :             throw ProcessError(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
    2188              :         }
    2189            3 :         MSDriveWay* parent = it->second;
    2190            3 :         for (MSDriveWay* sub : parent->mySubDriveWays) {
    2191            1 :             if (sub->myRoute == route) {
    2192              :                 dw = sub;
    2193              :                 break;
    2194              :             }
    2195              :         }
    2196            3 :         if (dw == nullptr) {
    2197              :             // missing subdriveways can be ignored. They may have been created
    2198              :             // as foes for driveways that are not relevant at state loading time
    2199              :             return;
    2200              :         }
    2201              :     }
    2202           78 :     const std::string vehicles = attrs.getOpt<std::string>(SUMO_ATTR_VEHICLES, id.c_str(), ok, "");
    2203          117 :     for (const std::string& vehID : StringTokenizer(vehicles).getVector()) {
    2204           39 :         MSBaseVehicle* veh = dynamic_cast<MSBaseVehicle*>(c.getVehicle(vehID));
    2205           39 :         if (veh == nullptr) {
    2206            0 :             throw ProcessError(TLF("Unknown vehicle '%' in driveway '%'", vehID, id));
    2207              :         }
    2208           39 :         if (!dw->hasTrain(veh)) {
    2209            5 :             dw->myTrains.insert(veh);
    2210            5 :             veh->addReminder(dw);
    2211              :         }
    2212           39 :     }
    2213           41 : }
    2214              : 
    2215              : const MSDriveWay*
    2216            0 : MSDriveWay::retrieveDepartDriveWay(const MSEdge* edge, const std::string& id) {
    2217            0 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    2218            0 :         if (dw->getID() == id) {
    2219              :             return dw;
    2220              :         }
    2221              :     }
    2222              :     return nullptr;
    2223              : }
    2224              : 
    2225              : 
    2226              : bool
    2227         1500 : MSDriveWay::hasTrain(SUMOVehicle* veh) const {
    2228         1500 :     return myTrains.count(veh) != 0;
    2229              : }
    2230              : 
    2231              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1