LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSDriveWay.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 96.7 % 946 915
Test Date: 2026-04-16 16:39:47 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        41790 : MSDriveWay::init() {
      81        41790 :     myWriteVehicles = OptionsCont::getOptions().isSet("railsignal-vehicle-output");
      82        41790 :     myMovingBlockMaxDist = OptionsCont::getOptions().getFloat("railsignal.moving-block.max-dist");
      83        41790 : }
      84              : 
      85              : // ===========================================================================
      86              : // MSDriveWay method definitions
      87              : // ===========================================================================
      88              : 
      89              : 
      90        26307 : MSDriveWay::MSDriveWay(const MSLink* origin, const std::string& id, bool temporary) :
      91        52614 :     MSMoveReminder("DriveWay_" + (temporary ? "tmp" : id)),
      92              :     Named(id),
      93        11406 :     myNumericalID(temporary ? -1 : myGlobalDriveWayIndex++),
      94        26307 :     myOrigin(origin),
      95        26307 :     myActive(nullptr),
      96        26307 :     myCoreSize(0),
      97        26307 :     myForwardEdgeCount(0),
      98        26307 :     myFoundSignal(false),
      99        26307 :     myFoundJump(false),
     100        26307 :     myTerminateRoute(false),
     101        26307 :     myAbortedBuild(false),
     102        26307 :     myBidiEnded(false),
     103        90327 :     myParent(nullptr)
     104        26307 : {}
     105              : 
     106              : 
     107        37713 : MSDriveWay::~MSDriveWay() {
     108        28827 :     for (const MSDriveWay* sub : mySubDriveWays) {
     109         2520 :         delete sub;
     110              :     }
     111              :     mySubDriveWays.clear();
     112       116634 : }
     113              : 
     114              : void
     115        41448 : MSDriveWay::cleanup() {
     116        41448 :     myGlobalDriveWayIndex = 0;
     117              :     myBlockLengthWarnings.clear();
     118        41448 :     myWriteVehicles = false;
     119              : 
     120        44600 :     for (auto item : myDepartureDriveways) {
     121         6614 :         for (MSDriveWay* dw : item.second) {
     122         3462 :             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        41448 : }
     132              : 
     133              : void
     134          168 : MSDriveWay::clearState() {
     135          193 :     for (auto item : myEndingDriveways) {
     136           50 :         for (MSDriveWay* dw : item.second) {
     137              :             dw->myTrains.clear();
     138              :         }
     139              :     }
     140          168 : }
     141              : 
     142              : 
     143              : bool
     144        34438 : 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              : 
     149        34438 :     if (veh.isVehicle() && MSRailSignalControl::isUsingDriveWays(veh.getVClass())) {
     150        10129 :         if ((enteredLane == myLane || (MSGlobals::gUseMesoSim && veh.getEdge() == &myLane->getEdge()))
     151        36052 :                 && (reason == NOTIFICATION_DEPARTED || reason == NOTIFICATION_JUNCTION || reason == NOTIFICATION_PARKING)) {
     152        25821 :             SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     153        25821 :             MSRouteIterator firstIt = std::find(sveh.getCurrentRouteEdge(), sveh.getRoute().end(), myLane->getNextNormal());
     154        25821 :             if (match(firstIt, sveh.getRoute().end())) {
     155        16940 :                 if (myTrains.count(&sveh) == 0) {
     156        16900 :                     enterDriveWay(sveh, reason);
     157              :                 }
     158        16920 :                 return true;
     159              :             }
     160         8561 :         } else if (reason == NOTIFICATION_REROUTE) {
     161         1468 :             SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
     162              :             assert(myTrains.count(&sveh) == 0);
     163         1468 :             int movedPast = matchesPastRoute(sveh);
     164              :             // vehicle must still be one the drivway
     165         1468 :             if (movedPast >= 0 && movedPast < myForwardEdgeCount) {
     166          768 :                 enterDriveWay(sveh, reason);
     167          768 :                 return true;
     168              :             }
     169              :         }
     170              :     }
     171              :     return false;
     172              : }
     173              : 
     174              : 
     175              : bool
     176        81440 : MSDriveWay::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/, Notification reason, const MSLane* enteredLane) {
     177              :     UNUSED_PARAMETER(enteredLane);
     178              : #ifdef DEBUG_MOVEREMINDER
     179              :     std::cout << SIMTIME << " notifyLeave " << getDescription() << " veh=" << veh.getID() << " lane=" << Named::getIDSecure(enteredLane) << " reason=" << toString(reason) << "\n";
     180              : #endif
     181        81440 :     if (veh.isVehicle() && MSRailSignalControl::isUsingDriveWays(veh.getVClass())) {
     182              :         // leaving network with departure, teleport etc
     183        81440 :         if (reason != MSMoveReminder::NOTIFICATION_JUNCTION && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     184         5641 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     185         5641 :             if (myWriteVehicles) {
     186          562 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     187              :             }
     188         5641 :             return false;
     189        75799 :         } else if (MSGlobals::gUseMesoSim && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
     190              :             // notifyLeave is called before moving the route iterator
     191         2228 :             const MSLane* leftLane = (*(dynamic_cast<SUMOVehicle&>(veh).getCurrentRouteEdge()))->getLanes().front();
     192         2228 :             return notifyLeaveBack(veh, reason, leftLane);
     193              :         } else {
     194              :             return true;
     195              :         }
     196              :     } else {
     197            0 :         return false;
     198              :     }
     199              : }
     200              : 
     201              : 
     202              : bool
     203        76241 : MSDriveWay::notifyLeaveBack(SUMOTrafficObject& veh, Notification reason, const MSLane* leftLane) {
     204              : #ifdef DEBUG_MOVEREMINDER
     205              :     std::cout << SIMTIME << " notifyLeaveBack " << getDescription() << " veh=" << veh.getID() << " lane=" << Named::getIDSecure(leftLane) << " reason=" << toString(reason) << "\n";
     206              : #endif
     207        76241 :     if (veh.isVehicle() && MSRailSignalControl::isUsingDriveWays(veh.getVClass())) {
     208        76241 :         if (leftLane == myForward.back() && (veh.getBackLane() != leftLane->getBidiLane() || MSGlobals::gUseMesoSim)) {
     209        11348 :             myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
     210        11348 :             if (myWriteVehicles) {
     211         1176 :                 myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
     212              :             }
     213        11348 :             return false;
     214              :         } else {
     215        64893 :             return true;
     216              :         }
     217              :     } else {
     218            0 :         return false;
     219              :     }
     220              : }
     221              : 
     222              : 
     223              : bool
     224         1555 : MSDriveWay::notifyReroute(SUMOTrafficObject& veh) {
     225              : #ifdef DEBUG_MOVEREMINDER
     226              :     std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << "\n";
     227              : #endif
     228              :     assert(veh.isVehicle());
     229         1555 :     if (MSRailSignalControl::isUsingDriveWays(veh.getVClass())) {
     230         1553 :         SUMOVehicle* sveh = dynamic_cast<SUMOVehicle*>(&veh);
     231              :         assert(myTrains.count(sveh) != 0);
     232         1553 :         if (matchesPastRoute(*sveh) >= 0) {
     233              :             //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " valid\n";
     234          819 :             return true;
     235              :         }
     236              :         // no match found, remove
     237              :         myTrains.erase(sveh);
     238          734 :         if (myWriteVehicles) {
     239          100 :             myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), NOTIFICATION_REROUTE));
     240              :         }
     241              :         //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " invalid\n";
     242              :     }
     243              :     return false;
     244              : }
     245              : 
     246              : 
     247              : int
     248         3021 : MSDriveWay::matchesPastRoute(SUMOVehicle& sveh) const {
     249              :     // look backwards along the route to find the driveway lane
     250         3021 :     const ConstMSEdgeVector& routeEdges = sveh.getRoute().getEdges();
     251        18897 :     for (int i = sveh.getRoutePosition(); i >= 0; i--) {
     252        18479 :         if (routeEdges[i] == myLane->getNextNormal()) {
     253              :             MSRouteIterator firstIt = routeEdges.begin() + i;
     254         2603 :             if (match(firstIt, sveh.getRoute().end())) {
     255              :                 // driveway is still valid after rerouting
     256              :                 //std::cout << SIMTIME << " notifyReroute " << getDescription() << " veh=" << veh.getID() << " valid\n";
     257         1674 :                 return sveh.getRoutePosition() - i;
     258              :             }
     259          929 :             break;
     260              :         }
     261              :     }
     262              :     return -1;
     263              : }
     264              : 
     265              : 
     266              : void
     267        17668 : MSDriveWay::enterDriveWay(SUMOVehicle& sveh, Notification reason) {
     268        17668 :     myTrains.insert(&sveh);
     269        17668 :     if (myOrigin != nullptr) {
     270        11033 :         MSRailSignalControl::getInstance().notifyApproach(myOrigin);
     271              :     }
     272        58999 :     for (const MSDriveWay* foe : myFoes) {
     273        41331 :         if (foe->myOrigin != nullptr) {
     274        31320 :             MSRailSignalControl::getInstance().notifyApproach(foe->myOrigin);
     275              :         }
     276              :     }
     277        17668 :     if (myWriteVehicles) {
     278         1804 :         myVehicleEvents.push_back(VehicleEvent(SIMSTEP, true, sveh.getID(), reason));
     279              :     }
     280        17668 : }
     281              : 
     282              : bool
     283       594488 : MSDriveWay::reserve(const Approaching& closest, MSEdgeVector& occupied) {
     284       594488 :     if (foeDriveWayOccupied(true, closest.first, occupied)) {
     285              :         return false;
     286              :     }
     287       524946 :     for (MSLink* foeLink : myConflictLinks) {
     288       145841 :         if (hasLinkConflict(closest, foeLink)) {
     289              : #ifdef DEBUG_SIGNALSTATE
     290              :             if (gDebugFlag4 || DEBUG_HELPER(closest.first)) {
     291              :                 std::cout << getID() << " linkConflict with " << getTLLinkID(foeLink) << "\n";
     292              :             }
     293              : #endif
     294              :             return false;
     295              :         }
     296              :     }
     297       379105 :     myActive = closest.first;
     298       379105 :     return true;
     299              : }
     300              : 
     301              : 
     302              : bool
     303       145841 : MSDriveWay::hasLinkConflict(const Approaching& veh, const MSLink* foeLink) const {
     304              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     305              :     if (gDebugFlag4) {
     306              :         std::cout << "   checkLinkConflict foeLink=" << getTLLinkID(foeLink) << " ego=" << Named::getIDSecure(veh.first) << "\n";
     307              :     }
     308              : #endif
     309       145841 :     if (foeLink->getApproaching().size() > 0) {
     310        50684 :         Approaching foe = foeLink->getClosest();
     311              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     312              :         if (gDebugFlag4) {
     313              :             std::cout << "     approaching foe=" << foe.first->getID() << "\n";
     314              :         }
     315              : #endif
     316        50684 :         if (foe.first == veh.first) {
     317        50684 :             return false;
     318              :         }
     319              :         const MSTrafficLightLogic* foeTLL = foeLink->getTLLogic();
     320              :         assert(foeTLL != nullptr);
     321        45618 :         const MSRailSignal* constFoeRS = dynamic_cast<const MSRailSignal*>(foeTLL);
     322              :         MSRailSignal* foeRS = const_cast<MSRailSignal*>(constFoeRS);
     323        45618 :         if (foeRS != nullptr) {
     324        45618 :             const MSDriveWay& foeDriveWay = foeRS->retrieveDriveWayForVeh(foeLink->getTLIndex(), foe.first);
     325              :             MSEdgeVector occupied;
     326        70611 :             if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied) ||
     327        41627 :                     !foeRS->constraintsAllow(foe.first) ||
     328        32987 :                     !overlap(foeDriveWay) ||
     329        61971 :                     getFoeOrSubFoe(&foeDriveWay) == nullptr ||
     330        13472 :                     canUseSiding(veh.first, getFoeOrSubFoe(&foeDriveWay)).first) {
     331              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     332              :                 if (gDebugFlag4 || veh.first->isSelected()) {
     333              :                     if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied)) {
     334              :                         std::cout << "     foe blocked\n";
     335              :                     } else if (!foeRS->constraintsAllow(foe.first)) {
     336              :                         std::cout << "     foe constrained\n";
     337              :                     } else if (!overlap(foeDriveWay)) {
     338              :                         std::cout << "     no overlap with foeDW=" << foeDriveWay.getID() << "\n";
     339              :                     } else if (getFoeOrSubFoe(&foeDriveWay) == nullptr) {
     340              :                         std::cout << "     foeDW=" << foeDriveWay.getID() << " is not a foe to " << getID() << "\n";
     341              :                     } else if (canUseSiding(veh.first, &foeDriveWay).first) {
     342              :                         std::cout << "     use siding\n";
     343              :                     }
     344              :                 }
     345              : #endif
     346        33654 :                 return false;
     347              :             }
     348              : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     349              :             if (gDebugFlag4) {
     350              :                 std::cout
     351              :                         << "  aSB=" << veh.second.arrivalSpeedBraking << " foeASB=" << foe.second.arrivalSpeedBraking
     352              :                         << "  aT=" << veh.second.arrivalTime << " foeAT=" << foe.second.arrivalTime
     353              :                         << "  aS=" << veh.first->getSpeed() << " foeS=" << foe.first->getSpeed()
     354              :                         << "  aD=" << veh.second.dist << " foeD=" << foe.second.dist
     355              :                         << "  aW=" << veh.first->getWaitingTime() << " foeW=" << foe.first->getWaitingTime()
     356              :                         << "  aN=" << veh.first->getNumericalID() << " foeN=" << foe.first->getNumericalID()
     357              :                         << "\n";
     358              :             }
     359              : #endif
     360        11964 :             const bool yield = mustYield(veh, foe);
     361        11964 :             if (MSRailSignal::storeVehicles()) {
     362          540 :                 MSRailSignal::rivalVehicles().push_back(foe.first);
     363          540 :                 if (yield) {
     364          315 :                     MSRailSignal::priorityVehicles().push_back(foe.first);
     365              :                 }
     366              :             }
     367        11964 :             return yield;
     368        45618 :         }
     369              :     }
     370              :     return false;
     371              : }
     372              : 
     373              : 
     374              : const MSDriveWay*
     375        80539 : MSDriveWay::getFoeOrSubFoe(const MSDriveWay* dw) const {
     376        80539 :     if (std::find(myFoes.begin(), myFoes.end(), dw) != myFoes.end()) {
     377        46751 :         return dw;
     378              :     }
     379        38600 :     for (const MSDriveWay* sub : dw->mySubDriveWays) {
     380        30907 :         const MSDriveWay* foe = getFoeOrSubFoe(sub);
     381        30907 :         if (foe != nullptr) {
     382              :             return foe;
     383              :         }
     384              :     }
     385              :     return nullptr;
     386              : }
     387              : 
     388              : 
     389              : bool
     390        11964 : MSDriveWay::mustYield(const Approaching& veh, const Approaching& foe) {
     391        11964 :     if (foe.second.arrivalSpeedBraking == veh.second.arrivalSpeedBraking) {
     392         8234 :         if (foe.second.arrivalTime == veh.second.arrivalTime) {
     393          566 :             if (foe.first->getSpeed() == veh.first->getSpeed()) {
     394          540 :                 if (foe.second.dist == veh.second.dist) {
     395          504 :                     if (foe.first->getWaitingTime() == veh.first->getWaitingTime()) {
     396          486 :                         return foe.first->getNumericalID() < veh.first->getNumericalID();
     397              :                     } else {
     398           18 :                         return foe.first->getWaitingTime() > veh.first->getWaitingTime();
     399              :                     }
     400              :                 } else {
     401           36 :                     return foe.second.dist < veh.second.dist;
     402              :                 }
     403              :             } else {
     404           26 :                 return foe.first->getSpeed() > veh.first->getSpeed();
     405              :             }
     406              :         } else {
     407         7668 :             return foe.second.arrivalTime < veh.second.arrivalTime;
     408              :         }
     409              :     } else {
     410         3730 :         return foe.second.arrivalSpeedBraking > veh.second.arrivalSpeedBraking;
     411              :     }
     412              : }
     413              : 
     414              : 
     415              : bool
     416        15891 : MSDriveWay::conflictLaneOccupied(bool store, const SUMOVehicle* ego) const {
     417       238653 :     for (const MSLane* lane : myConflictLanes) {
     418              :         if (!lane->isEmpty()) {
     419         4410 :             std::string joinVehicle = "";
     420         4410 :             if (ego != nullptr && !MSGlobals::gUseMesoSim) {
     421            0 :                 const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
     422            0 :                 if (stop != nullptr) {
     423            0 :                     joinVehicle = stop->join;
     424              :                 }
     425              :             }
     426              : #ifdef DEBUG_SIGNALSTATE
     427              :             if (gDebugFlag4) {
     428              :                 std::cout << SIMTIME << " conflictLane " << lane->getID() << " occupied ego=" << Named::getIDSecure(ego) << " vehNumber=" << lane->getVehicleNumber() << "\n";
     429              :                 if (joinVehicle != "") {
     430              :                     std::cout << "  joinVehicle=" << joinVehicle << " occupant=" << toString(lane->getVehiclesSecure()) << "\n";
     431              :                     lane->releaseVehicles();
     432              :                 }
     433              :             }
     434              : #endif
     435         4410 :             if (lane->getVehicleNumberWithPartials() == 1) {
     436         4410 :                 MSVehicle* foe = lane->getLastAnyVehicle();
     437         4410 :                 if (joinVehicle != "") {
     438            0 :                     if (foe->getID() == joinVehicle && foe->isStopped()) {
     439              : #ifdef DEBUG_SIGNALSTATE
     440              :                         if (gDebugFlag4) {
     441              :                             std::cout << "    ignore join-target '" << joinVehicle << "\n";
     442              :                         }
     443              : #endif
     444            0 :                         continue;
     445              :                     }
     446              :                 }
     447         4410 :                 if (ego != nullptr) {
     448            0 :                     if (foe == ego && std::find(myForward.begin(), myForward.end(), lane) == myForward.end()) {
     449              : #ifdef DEBUG_SIGNALSTATE
     450              :                         if (gDebugFlag4) {
     451              :                             std::cout << "    ignore ego as oncoming '" << ego->getID() << "\n";
     452              :                         }
     453              : #endif
     454            0 :                         continue;
     455              :                     }
     456            0 :                     if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
     457              : #ifdef DEBUG_SIGNALSTATE
     458              :                         if (gDebugFlag4) {
     459              :                             std::cout << "    ignore " << foe->getID() << " for which ego is join-target\n";
     460              :                         }
     461              : #endif
     462            0 :                         continue;
     463              :                     }
     464              :                 }
     465              :             }
     466         4410 :             if (MSRailSignal::storeVehicles() && store) {
     467         4410 :                 MSRailSignal::blockingVehicles().push_back(lane->getLastAnyVehicle());
     468              :             }
     469              :             return true;
     470              :         }
     471              :     }
     472        11481 :     return false;
     473              : }
     474              : 
     475              : 
     476              : bool
     477      8342728 : MSDriveWay::foeDriveWayApproached() const {
     478     16805372 :     for (const MSDriveWay* foeDW : myFoes) {
     479     16749225 :         if (foeDW->myOrigin != nullptr && foeDW->myOrigin->getApproaching().size() > 0) {
     480              : #ifdef DEBUG_SIGNALSTATE
     481              :             if (gDebugFlag4) {
     482              :                 std::cout << SIMTIME << " foeLink=" << foeDW->myOrigin->getDescription() << " approachedBy=" << foeDW->myOrigin->getApproaching().begin()->first->getID() << "\n";
     483              :             }
     484              : #endif
     485              :             return true;
     486              :         }
     487              :     }
     488              :     return false;
     489              : }
     490              : 
     491              : 
     492              : bool
     493      9522445 : MSDriveWay::foeDriveWayOccupied(bool store, const SUMOVehicle* ego, MSEdgeVector& occupied) const {
     494     28261391 :     for (const MSDriveWay* foeDW : myFoes) {
     495     19514101 :         if (!foeDW->myTrains.empty()) {
     496              : #ifdef DEBUG_SIGNALSTATE
     497              :             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     498              :                 std::cout << SIMTIME << " " << getID() << " foeDriveWay " << foeDW->getID() << " occupied ego=" << Named::getIDSecure(ego) << " foeVeh=" << toString(foeDW->myTrains) << "\n";
     499              :             }
     500              : #endif
     501       828827 :             if (foeDW->myTrains.size() == 1) {
     502       827066 :                 SUMOVehicle* foe = *foeDW->myTrains.begin();
     503       827066 :                 if (foe == ego) {
     504              : #ifdef DEBUG_SIGNALSTATE
     505              :                     if (gDebugFlag4 || DEBUG_HELPER(ego)) {
     506              :                         std::cout << "    ignore ego as foe '" << Named::getIDSecure(ego) << "\n";
     507              :                     }
     508              : #endif
     509        53760 :                     continue;
     510              :                 }
     511       802719 :                 if (hasJoin(ego, foe)) {
     512           36 :                     continue;
     513              :                 }
     514              :             }
     515       804444 :             std::pair<bool, const MSDriveWay*> useSiding = canUseSiding(ego, foeDW);
     516              : #ifdef DEBUG_SIGNALSTATE
     517              :             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     518              :                 auto it = mySidings.find(foeDW);
     519              :                 int numSidings = 0;
     520              :                 if (it != mySidings.end()) {
     521              :                     numSidings = it->second.size();
     522              :                 }
     523              :                 std::cout << " ego=" << Named::getIDSecure(ego) << " useSiding=" << useSiding.first << " sidingFoe=" << Named::getIDSecure(useSiding.second) << " numSidings=" << numSidings << "\n";
     524              :             }
     525              : #endif
     526       804444 :             if (useSiding.first) {
     527        29377 :                 continue;
     528              :             } else {
     529       775067 :                 if (MSRailSignal::storeVehicles() && store) {
     530          740 :                     for (SUMOVehicle* foe : foeDW->myTrains) {
     531          370 :                         MSRailSignal::blockingVehicles().push_back(foe);
     532              :                     }
     533          370 :                     MSRailSignal::blockingDriveWays().push_back(foeDW);
     534              :                 }
     535      1552676 :                 for (const SUMOVehicle* foe : foeDW->myTrains) {
     536       777609 :                     occupied.push_back(const_cast<MSEdge*>(foe->getEdge()));
     537       777609 :                     MSEdge* bidi = const_cast<MSEdge*>(foe->getEdge()->getBidiEdge());
     538       777609 :                     if (bidi != nullptr) {
     539       433659 :                         occupied.push_back(bidi);
     540              :                     }
     541              :                     /// @todo: if foe occupies more than one edge we should add all of them to the occupied vector
     542              :                 }
     543       282054 :                 if (ego != nullptr && MSGlobals::gTimeToTeleportRSDeadlock > 0
     544      1003082 :                         && (ego->getWaitingTime() > ego->getVehicleType().getCarFollowModel().getStartupDelay() || !ego->isOnRoad())) {
     545              :                     // if there is an occupied siding, it becomes part of the waitRelation
     546       116503 :                     SUMOVehicle* foe = *(useSiding.second == nullptr ? foeDW : useSiding.second)->myTrains.begin();
     547       116503 :                     const MSRailSignal* rs = myOrigin != nullptr ? dynamic_cast<const MSRailSignal*>(myOrigin->getTLLogic()) : nullptr;
     548       116503 :                     MSRailSignalControl::getInstance().addWaitRelation(ego, rs, foe);
     549              :                 }
     550       775067 :                 return true;
     551              :             }
     552     18685274 :         } else if (foeDW != this && isDepartDriveway() && !foeDW->isDepartDriveway()) {
     553        12213 :             if (foeDW->myOrigin->getApproaching().size() > 0) {
     554         2944 :                 Approaching foeA = foeDW->myOrigin->getClosest();
     555         2944 :                 const SUMOVehicle* foe = foeA.first;
     556         2944 :                 if (foeA.second.dist < foe->getBrakeGap(true)) {
     557          274 :                     MSRouteIterator firstIt = std::find(foe->getCurrentRouteEdge(), foe->getRoute().end(), foeDW->myRoute.front());
     558          274 :                     if (firstIt != foe->getRoute().end()) {
     559          274 :                         if (foeDW->match(firstIt, foe->getRoute().end())) {
     560          236 :                             bool useSiding = canUseSiding(ego, foeDW).first;
     561              : #ifdef DEBUG_SIGNALSTATE
     562              :                             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     563              :                                 std::cout << SIMTIME << " " << getID() << " blocked by " << foeDW->getID() << " (approached by " << foe->getID() << ") useSiding=" << useSiding << "\n";
     564              :                             }
     565              : #endif
     566          236 :                             if (useSiding) {
     567              :                                 //std::cout << SIMTIME << " " << getID() << " ego=" << ego->getID() << " foeDW=" << foeDW->getID() << " myFoes=" << toString(myFoes) << "\n";
     568          148 :                                 continue;
     569              :                             } else {
     570           88 :                                 return true;
     571              :                             }
     572              :                         }
     573              :                     }
     574              :                 }
     575              :             }
     576              :         }
     577              :     }
     578      8748182 :     for (const std::set<const MSDriveWay*>& dlFoes : myDeadlocks) {
     579              :         bool allOccupied = true;
     580         7277 :         for (const MSDriveWay* dlFoe : dlFoes) {
     581         5503 :             if (dlFoe->myTrains.empty()) {
     582              :                 allOccupied = false;
     583              :                 //std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << "  deadlockCheck clear " << dlFoe->getID() << "\n";
     584              :                 break;
     585              :             }
     586              :         }
     587         2666 :         if (allOccupied) {
     588              : #ifdef DEBUG_SIGNALSTATE
     589              :             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     590              :                 std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " deadlockCheck " << joinNamedToString(dlFoes, " ") << "\n";
     591              :             }
     592              : #endif
     593         5999 :             for (const MSDriveWay* dlFoe : dlFoes) {
     594         4225 :                 MSRailSignal::blockingDriveWays().push_back(dlFoe);
     595              :             }
     596              :             return true;
     597              :         }
     598              :     }
     599              :     return false;
     600              : }
     601              : 
     602              : 
     603              : bool
     604       802719 : MSDriveWay::hasJoin(const SUMOVehicle* ego, const SUMOVehicle* foe) {
     605       802719 :     if (ego != nullptr && !MSGlobals::gUseMesoSim) {
     606       265030 :         std::string joinVehicle = "";
     607       265030 :         const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
     608       265030 :         if (stop != nullptr) {
     609       105972 :             joinVehicle = stop->join;
     610              :         }
     611       265030 :         if (joinVehicle == "" && !ego->hasDeparted() && ego->getStops().size() > 1) {
     612              :             // check one more stop
     613         7226 :             auto it = ego->getStops().begin();
     614              :             std::advance(it, 1);
     615         7226 :             joinVehicle = it->pars.join;
     616              :         }
     617       265030 :         if (joinVehicle != "") {
     618              : #ifdef DEBUG_SIGNALSTATE
     619              :             if (gDebugFlag4 || DEBUG_COND_DW(ego) || DEBUG_COND_DW(foe)) {
     620              :                 std::cout << "  joinVehicle=" << joinVehicle << "\n";
     621              :             }
     622              : #endif
     623          345 :             if (foe->getID() == joinVehicle && foe->isStopped()) {
     624              : #ifdef DEBUG_SIGNALSTATE
     625              :                 if (gDebugFlag4 || DEBUG_COND_DW(ego) || DEBUG_COND_DW(foe)) {
     626              :                     std::cout << "    ignore join-target '" << joinVehicle << "\n";
     627              :                 }
     628              : #endif
     629              :                 return true;
     630              :             }
     631              :         }
     632              : 
     633       265006 :         if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
     634              : #ifdef DEBUG_SIGNALSTATE
     635              :             if (gDebugFlag4 || DEBUG_COND_DW(ego) || DEBUG_COND_DW(foe)) {
     636              :                 std::cout << "    ignore " << foe->getID() << " for which ego is join-target\n";
     637              :             }
     638              : #endif
     639              :             return true;
     640              :         }
     641              :     }
     642              :     return false;
     643              : }
     644              : 
     645              : 
     646              : std::pair<bool, const MSDriveWay*>
     647       837959 : MSDriveWay::canUseSiding(const SUMOVehicle* ego, const MSDriveWay* foe, const MSEdge* recurseSidingEnd) const {
     648              :     auto it = mySidings.find(foe);
     649       837959 :     if (it != mySidings.end()) {
     650        73274 :         for (auto siding : it->second) {
     651              :             // assume siding is usuable when computing state for unapproached signal (ego == nullptr)
     652        72464 :             if (ego == nullptr || siding.length >= ego->getLength()) {
     653              :                 // if the siding is already "reserved" by another vehicle we cannot use it here
     654        61962 :                 const MSEdge* sidingEnd = myRoute[siding.end];
     655              :                 bool checkNext = false;
     656       156723 :                 for (MSDriveWay* sidingApproach : myEndingDriveways[sidingEnd]) {
     657       113970 :                     if (!sidingApproach->myTrains.empty() && *sidingApproach->myTrains.begin() == ego && sidingEnd == recurseSidingEnd) {
     658              :                         // check next siding if if exists
     659              :                         checkNext = true;
     660              : #ifdef DEBUG_SIGNALSTATE
     661              :                         if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     662              :                             std::cout << "   checkNext\n";
     663              :                         }
     664              : #endif
     665         2566 :                         continue;
     666              :                     }
     667       111404 :                     if (!sidingApproach->myTrains.empty() && *sidingApproach->myTrains.begin() != ego) {
     668              :                         // possibly the foe vehicle can use the other part of the siding
     669        26279 :                         if (recurseSidingEnd == nullptr) {
     670              :                             const SUMOVehicle* foeVeh = nullptr;
     671        19322 :                             if (!foe->myTrains.empty()) {
     672        19163 :                                 foeVeh = *foe->myTrains.begin();
     673          159 :                             } else if (foe->myOrigin != nullptr && foe->myOrigin->getApproaching().size() > 0) {
     674          159 :                                 foeVeh = foe->myOrigin->getClosest().first;
     675              :                             }
     676        19322 :                             if (foeVeh == nullptr) {
     677            0 :                                 WRITE_WARNINGF("Invalid call to canUseSiding dw=% foe=% ego=% time=%", getID(), foe->getID(), Named::getIDSecure(ego), time2string(SIMSTEP));
     678            0 :                                 continue;
     679              :                             }
     680        19322 :                             const MSDriveWay* foe2 = foe->isSubDriveWay() ? foe->myParent : foe;
     681        19322 :                             const MSDriveWay* this2 = foe2->getFoeOrSubFoe(this);
     682              : #ifdef DEBUG_SIGNALSTATE
     683              :                             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     684              :                                 std::cout << "   foe2=" << foe2->getID() << " this2=" << this2->getID() << "\n";
     685              :                             }
     686              : #endif
     687        19322 :                             if (this2 != nullptr && foe2->canUseSiding(foeVeh, this2, sidingEnd).first) {
     688         7070 :                                 continue;
     689              :                             }
     690              :                         }
     691              :                         // possibly the foe vehicle
     692              :                         // @todo: in principle it might still be possible to continue if vehicle that approaches the siding can safely leave the situation
     693              : #ifdef DEBUG_SIGNALSTATE
     694              :                         if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     695              :                             std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
     696              :                                       << " foeVeh=" << toString(foe->myTrains)
     697              :                                       << " sidingEnd=" << sidingEnd->getID() << " sidingApproach=" << sidingApproach->getID() << " approaching=" << toString(sidingApproach->myTrains) << "\n";
     698              :                         }
     699              : #endif
     700        19209 :                         return std::make_pair(false, sidingApproach);
     701              :                     }
     702              :                 }
     703              :                 // vehicles approaching intermediate driveways could also make the siding unusable but would not show up as sidingApproaches
     704        47566 :                 for (int i : siding.intermediateEnds) {
     705         6699 :                     const MSEdge* intermediateEnd = myRoute[i];
     706        13045 :                     for (MSDriveWay* intermediateApproach : myEndingDriveways[intermediateEnd]) {
     707              :                         if (!intermediateApproach->myTrains.empty()
     708         8232 :                                 && (*intermediateApproach->myTrains.begin() != ego || intermediateEnd == recurseSidingEnd)) {
     709              :                             SUMOVehicle* onApproach = *intermediateApproach->myTrains.begin();
     710         2183 :                             if (std::find(onApproach->getCurrentRouteEdge(), onApproach->getRoute().end(), sidingEnd) == onApproach->getRoute().end()) {
     711              :                                 // intermediate vehicle does not make use of the siding
     712           99 :                                 continue;
     713              :                             }
     714              :                             // possibly the foe vehicle can use the other part of the siding
     715         2084 :                             if (recurseSidingEnd == nullptr) {
     716              :                                 const SUMOVehicle* foeVeh = nullptr;
     717          485 :                                 if (!foe->myTrains.empty()) {
     718          456 :                                     foeVeh = *foe->myTrains.begin();
     719           29 :                                 } else if (foe->myOrigin != nullptr && foe->myOrigin->getApproaching().size() > 0) {
     720           29 :                                     foeVeh = foe->myOrigin->getClosest().first;
     721              :                                 }
     722          485 :                                 if (foeVeh == nullptr) {
     723            0 :                                     WRITE_WARNINGF("Invalid call to canUseSiding dw=% foe=% ego=% time=%", getID(), foe->getID(), Named::getIDSecure(ego), time2string(SIMSTEP));
     724            0 :                                     continue;
     725              :                                 }
     726          485 :                                 const MSDriveWay* foe2 = foe->isSubDriveWay() ? foe->myParent : foe;
     727          485 :                                 const MSDriveWay* this2 = foe2->getFoeOrSubFoe(this);
     728          485 :                                 if (this2 != nullptr && foe2->canUseSiding(foeVeh, this2, intermediateEnd).first) {
     729          198 :                                     continue;
     730              :                                 }
     731              :                             }
     732              :                             // @todo: in principle it might still be possible to continue if vehicle that approaches the siding can safely leave the situation
     733              : #ifdef DEBUG_SIGNALSTATE
     734              :                             if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     735              :                                 std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
     736              :                                     << " foeVeh=" << toString(foe->myTrains)
     737              :                                     << " sidingEnd=" << sidingEnd->getID() << " intermediateApproach=" << intermediateApproach->getID() << " approaching=" << toString(intermediateApproach->myTrains) << "\n";
     738              :                             }
     739              : #endif
     740         1886 :                             return std::make_pair(false, intermediateApproach);
     741              :                         }
     742              :                     }
     743              :                 }
     744        40867 :                 if (checkNext) {
     745         2566 :                     continue;
     746              :                 }
     747              : #ifdef DEBUG_SIGNALSTATE
     748              :                 if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     749              :                     std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
     750              :                         << " foeVeh=" << toString(foe->myTrains)
     751              :                         << " sidingEnd=" << sidingEnd->getID() << " usable\n";
     752              :                 }
     753              : #endif
     754        38301 :                 return std::make_pair(true, nullptr);
     755              :             }
     756              :         }
     757              :     }
     758              : #ifdef DEBUG_SIGNALSTATE
     759              :     if (gDebugFlag4 || DEBUG_COND_DW2 || DEBUG_HELPER(ego)) {
     760              :         std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID() << " noSidings\n";
     761              :     }
     762              : #endif
     763       778563 :     return std::make_pair(false, nullptr);
     764              : }
     765              : 
     766              : bool
     767        23897 : MSDriveWay::overlap(const MSDriveWay& other) const {
     768        43501 :     for (int i = 0; i < myCoreSize; i++) {
     769       415214 :         for (int j = 0; j < other.myCoreSize; j++) {
     770       395610 :             const MSEdge* edge = myRoute[i];
     771       395610 :             const MSEdge* edge2 = other.myRoute[j];
     772              :             if (edge->getToJunction() == edge2->getToJunction()
     773       374293 :                     || edge->getToJunction() == edge2->getFromJunction()
     774       767887 :                     || edge->getFromJunction() == edge2->getFromJunction()) {
     775              :                 // XXX might be rail_crossing with parallel tracks
     776              :                 return true;
     777              :             }
     778              :         }
     779              :     }
     780              :     return false;
     781              : }
     782              : 
     783              : 
     784              : bool
     785        80060 : MSDriveWay::flankConflict(const MSDriveWay& other) const {
     786       276852 :     for (const MSLane* lane : myForward) {
     787      1140787 :         for (const MSLane* lane2 : other.myForward) {
     788       933993 :             if (lane == lane2) {
     789              :                 return true;
     790              :             }
     791              :         }
     792      3017713 :         for (const MSLane* lane2 : other.myBidi) {
     793      2816242 :             if (lane == lane2) {
     794         6041 :                 if (bidiBlockedBy(other)) {
     795              :                     // it's only a deadlock if both trains block symmetrically
     796              :                     return true;
     797              :                 }
     798              :             }
     799              :         }
     800      7221347 :         for (const MSLane* lane2 : other.myBidiExtended) {
     801      7024555 :             if (lane == lane2) {
     802        23327 :                 if (bidiBlockedBy(other)) {
     803              :                     // it's only a deadlock if both trains block symmetrically
     804              :                     return true;
     805              :                 }
     806              :             }
     807              :         }
     808              :     }
     809              :     return false;
     810              : }
     811              : 
     812              : 
     813              : bool
     814        68239 : MSDriveWay::crossingConflict(const MSDriveWay& other) const {
     815       251727 :     for (const MSLane* lane : myForward) {
     816       861341 :         for (const MSLane* lane2 : other.myForward) {
     817       677853 :             if (lane->isNormal() && lane2->isNormal() && lane->getEdge().getToJunction() == lane2->getEdge().getToJunction()) {
     818              :                 return true;
     819              :             }
     820              :         }
     821              :     }
     822        60077 :     if (myOrigin != nullptr && other.myOrigin != nullptr
     823        52285 :             && myOrigin->getJunction() == other.myOrigin->getJunction()
     824              :             //&& myForward.front()->isInternal() && other.myForward.front()->isInternal()
     825         1869 :             && myOrigin->getJunction()->getLogic() != nullptr
     826        67953 :             && myOrigin->getJunction()->getLogic()->getFoesFor(myOrigin->getIndex()).test(other.myOrigin->getIndex())) {
     827              :         // switch/crossing is also a rail_signal (direct control)
     828          125 :         if (!(myForward.front()->isInternal() && other.myForward.front()->isInternal())) {
     829           32 :             return false;
     830              :         }
     831              :         return true;
     832              :     }
     833        65959 :     if (other.myOrigin != nullptr && other.myForward.front()->isInternal()) {
     834       155092 :         for (int i = 0; i < (int)myForward.size() - 1; i++) {
     835        99226 :             const MSLane* lane = myForward[i];
     836        99226 :             if (lane->getToJunction() == other.myOrigin->getJunction()) {
     837           58 :                 const MSLane* next = myForward[i + 1];
     838           58 :                 const MSLink* link = lane->getLinkTo(next);
     839           58 :                 if (link && link->getTLLogic() == nullptr) {
     840              :                     // switch/crossing is also a rail_signal (direct control) but own link is uncontrolled
     841           58 :                     if (lane->getToJunction()->getLogic() != nullptr
     842           58 :                             && lane->getToJunction()->getLogic()->getFoesFor(link->getIndex()).test(other.myOrigin->getIndex())) {
     843              :                         // and links are in conflict
     844              :                         return true;
     845              :                     }
     846              :                 }
     847              :             }
     848              :         }
     849              :     }
     850              :     return false;
     851              : }
     852              : 
     853              : 
     854              : bool
     855        30525 : MSDriveWay::bidiBlockedBy(const MSDriveWay& other) const {
     856       539057 :     for (const MSLane* lane : myBidi) {
     857      1967025 :         for (const MSLane* lane2 : other.myForward) {
     858      1458493 :             if (lane == lane2) {
     859              :                 return true;
     860              :             }
     861              :         }
     862              :     }
     863       543529 :     for (const MSLane* lane : myBidiExtended) {
     864      1853785 :         for (const MSLane* lane2 : other.myForward) {
     865      1334761 :             if (lane == lane2) {
     866         4333 :                 if (overlap(other)) {
     867              :                     return true;
     868              :                 }
     869              :             }
     870              :         }
     871              :     }
     872              :     return false;
     873              : }
     874              : 
     875              : 
     876              : bool
     877        17373 : MSDriveWay::bidiBlockedByEnd(const MSDriveWay& other) const {
     878        17373 :     const MSLane* end = other.myForward.back();
     879       152368 :     for (const MSLane* lane : myBidi) {
     880       140924 :         if (lane == end) {
     881              :             return true;
     882              :         }
     883              :     }
     884        98942 :     for (const MSLane* lane : myBidiExtended) {
     885        90428 :         if (lane == end) {
     886         2930 :             if (overlap(other)) {
     887              :                 return true;
     888              :             }
     889              :         }
     890              :     }
     891              :     return false;
     892              : }
     893              : 
     894              : bool
     895         6118 : MSDriveWay::forwardRouteConflict(std::set<const MSEdge*> forward, const MSDriveWay& other, bool secondCheck) {
     896              :     int i = 0;
     897        64833 :     for (const MSEdge* edge2 : other.myRoute) {
     898        64010 :         if (i == other.myCoreSize) {
     899              :             return false;
     900              :         }
     901        64010 :         i++;
     902        64010 :         if (edge2 == myForward.front()->getNextNormal() && !secondCheck) {
     903              :             // foe should not pass from behind through our own forward section
     904              :             return false;
     905              :         }
     906              :         if (forward.count(edge2->getBidiEdge()) != 0) {
     907              :             return true;
     908              :         }
     909              :     }
     910              :     return false;
     911              : }
     912              : 
     913              : void
     914         9072 : MSDriveWay::writeBlocks(OutputDevice& od) const {
     915        15897 :     od.openTag(isSubDriveWay() ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
     916         9072 :     od.writeAttr(SUMO_ATTR_ID, myID);
     917         9072 :     od.writeAttr(SUMO_ATTR_VEHICLE, myFirstVehicle);
     918         9072 :     od.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
     919         9072 :     if (myCoreSize != (int)myRoute.size()) {
     920            0 :         od.writeAttr("core", myCoreSize);
     921              :     }
     922         9072 :     od.openTag("forward");
     923         9072 :     od.writeAttr(SUMO_ATTR_LANES, toString(myForward));
     924        18144 :     od.closeTag();
     925         9072 :     if (!isSubDriveWay()) {
     926         6825 :         od.openTag("bidi");
     927         6825 :         od.writeAttr(SUMO_ATTR_LANES, toString(myBidi));
     928         6825 :         if (myBidiExtended.size() > 0) {
     929         2182 :             od.lf();
     930         2182 :             od << "                   ";
     931         4364 :             od.writeAttr("deadlockCheck", toString(myBidiExtended));
     932              :         }
     933         6825 :         od.closeTag();
     934         6825 :         od.openTag("flank");
     935         6825 :         od.writeAttr(SUMO_ATTR_LANES, toString(myFlank));
     936         6825 :         od.closeTag();
     937              : 
     938        13650 :         od.openTag("conflictLinks");
     939              : 
     940              :         std::vector<std::string> signals;
     941        11319 :         for (MSLink* link : myConflictLinks) {
     942         8988 :             signals.push_back(getTLLinkID(link));
     943              :         }
     944         6825 :         od.writeAttr("signals", joinToStringSorting(signals, " "));
     945        13650 :         od.closeTag();
     946              : 
     947              :         std::vector<std::string> foes;
     948        26509 :         for (MSDriveWay* dw : myFoes) {
     949        19684 :             foes.push_back(dw->myID);
     950              :         }
     951         6825 :         if (foes.size() > 0) {
     952         6684 :             od.openTag("foes");
     953         6684 :             od.writeAttr("driveWays", joinToStringSorting(foes, " "));
     954        13368 :             od.closeTag();
     955              :         }
     956         8493 :         for (auto item : mySidings) {
     957         1668 :             od.openTag("sidings");
     958         1668 :             od.writeAttr("foe", item.first->getID());
     959         4056 :             for (auto siding : item.second) {
     960         2388 :                 od.openTag("siding");
     961         2388 :                 od.writeAttr("start", myRoute[siding.start]->getID());
     962         2388 :                 od.writeAttr("end", myRoute[siding.end]->getID());
     963         2388 :                 od.writeAttr("length", siding.length);
     964         2388 :                 if (siding.intermediateEnds.size() > 0) {
     965              :                     std::vector<std::string> endEdges;
     966         1770 :                     for (int i : siding.intermediateEnds) {
     967          983 :                         endEdges.push_back(myRoute[i]->getID());
     968              :                     }
     969          787 :                     od.writeAttr("intermediateEnds", endEdges);
     970          787 :                 }
     971         4776 :                 od.closeTag();
     972              :             }
     973         3336 :             od.closeTag();
     974              :         }
     975         6918 :         for (auto item : myDeadlocks) {
     976           93 :             od.openTag("deadlock");
     977           93 :             od.writeAttr("foes", joinNamedToStringSorting(item, " "));
     978          186 :             od.closeTag();
     979              :         }
     980         6825 :     }
     981        18144 :     od.closeTag(); // driveWay
     982              : 
     983        11319 :     for (const MSDriveWay* sub : mySubDriveWays) {
     984         2247 :         sub->writeBlocks(od);
     985              :     }
     986              : #ifdef DRIVEWAY_SANITY_CHECK
     987         9072 :     std::set<MSDriveWay*> uFoes(myFoes.begin(), myFoes.end());
     988         9072 :     if (uFoes.size() != myFoes.size()) {
     989            0 :         WRITE_WARNINGF("Duplicate foes in driveway '%'", getID());
     990              : 
     991              :     }
     992              : #endif
     993         9072 : }
     994              : 
     995              : 
     996              : void
     997         1094 : MSDriveWay::writeBlockVehicles(OutputDevice& od) const {
     998         1950 :     od.openTag(isSubDriveWay() ? "subDriveWay" : "driveWay");
     999         1094 :     od.writeAttr(SUMO_ATTR_ID, myID);
    1000         2933 :     for (const VehicleEvent& ve : myVehicleEvents) {
    1001         2758 :         od.openTag(ve.isEntry ? "entry" : "exit");
    1002         1839 :         od.writeAttr(SUMO_ATTR_ID, ve.id);
    1003         1839 :         od.writeAttr(SUMO_ATTR_TIME, time2string(ve.time));
    1004         1839 :         od.writeAttr("reason", Notifications.getString(ve.reason));
    1005         3678 :         od.closeTag(); // event
    1006              :     }
    1007         2188 :     od.closeTag(); // driveWay
    1008              : 
    1009         1332 :     for (const MSDriveWay* sub : mySubDriveWays) {
    1010          238 :         sub->writeBlockVehicles(od);
    1011              :     }
    1012         1094 : }
    1013              : 
    1014              : 
    1015              : void
    1016         8886 : MSDriveWay::buildRoute(const MSLink* origin,
    1017              :                        MSRouteIterator next, MSRouteIterator end,
    1018              :                        LaneVisitedMap& visited,
    1019              :                        std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess>& flankSwitches) {
    1020         8886 :     double length = 0;
    1021              :     bool seekForwardSignal = true;
    1022              :     bool seekBidiSwitch = true;
    1023              :     bool foundUnsafeSwitch = false;
    1024         8886 :     MSLane* toLane = origin ? origin->getViaLaneOrLane() : (*next)->getLanes()[0];
    1025        23196 :     const std::string warnID = origin ? "rail signal " + getClickableTLLinkID(origin) : "insertion lane '" + toLane->getID() + "'";
    1026              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1027              :     if (gDebugFlag4) std::cout << "buildRoute origin=" << warnID << " vehRoute=" << toString(ConstMSEdgeVector(next, end))
    1028              :                                    << " visited=" << formatVisitedMap(visited) << "\n";
    1029              : #endif
    1030              :     while (true) {
    1031       125344 :         if (length > MSGlobals::gMaxRailSignalBlockLength) {
    1032              :             // typical block length in germany on main lines is 3-5km on branch lines up to 7km
    1033              :             // special branches that are used by one train exclusively could also be up to 20km in length
    1034              :             // minimum block size in germany is 37.5m (LZB)
    1035              :             // larger countries (USA, Russia) might see blocks beyond 20km)
    1036          224 :             if (seekForwardSignal && myBlockLengthWarnings.count(myRoute.front()) == 0) {
    1037          234 :                 WRITE_WARNINGF("Block after % exceeds maximum length (stopped searching after edge '%' (length=%m).",
    1038              :                                warnID, toLane->getEdge().getID(), length);
    1039              :                 myBlockLengthWarnings.insert(myRoute.front());
    1040              :             }
    1041          224 :             myAbortedBuild = true;
    1042              :             // length exceeded
    1043              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1044              :             if (gDebugFlag4) {
    1045              :                 std::cout << " abort: length=" << length << "\n";
    1046              :             }
    1047              : #endif
    1048         8886 :             return;
    1049              :         }
    1050              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1051              :         if (gDebugFlag4) {
    1052              :             std::cout << "   toLane=" << toLane->getID() << " visited=" << formatVisitedMap(visited) << "\n";
    1053              :         }
    1054              : #endif
    1055       125120 :         const MSEdge* current = &toLane->getEdge();
    1056       125120 :         if (current->isNormal()) {
    1057        67139 :             myRoute.push_back(current);
    1058        67139 :             if (next != end) {
    1059              :                 next++;
    1060              :             }
    1061              :         }
    1062       125120 :         appendMapIndex(visited, toLane);
    1063       125120 :         length += toLane->getLength();
    1064       125120 :         MSLane* bidi = toLane->getBidiLane();
    1065       125120 :         if (seekForwardSignal) {
    1066        33983 :             if (!foundUnsafeSwitch) {
    1067        33983 :                 myForward.push_back(toLane);
    1068        33983 :                 if (toLane->isNormal()) {
    1069        19964 :                     myForwardEdgeCount++;
    1070              :                 }
    1071        33983 :                 if (myForward.size() == 1) {
    1072         8886 :                     myLane = toLane;
    1073         8886 :                     if (MSGlobals::gUseMesoSim) {
    1074         2690 :                         MESegment* s = MSGlobals::gMesoNet->getSegmentForEdge(myLane->getEdge());
    1075         2690 :                         s->addDetector(this, myLane->getIndex());
    1076              :                     } else {
    1077         6196 :                         toLane->addMoveReminder(this, false);
    1078              :                     }
    1079              :                 }
    1080              :             }
    1081        91137 :         } else if (bidi == nullptr) {
    1082        28169 :             if (toLane->isInternal() && toLane->getIncomingLanes().front().viaLink->isTurnaround()) {
    1083              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1084              :                 if (gDebugFlag4) {
    1085              :                     std::cout << "      continue bidiSearch beyond turnaround\n";
    1086              :                 }
    1087              : #endif
    1088              :             } else {
    1089              :                 seekBidiSwitch = false;
    1090              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1091              :                 if (gDebugFlag4) {
    1092              :                     std::cout << "      noBidi, abort search for bidiSwitch\n";
    1093              :                 }
    1094              : #endif
    1095              :             }
    1096              :         }
    1097       125120 :         if (bidi != nullptr) {
    1098        82762 :             if (!seekForwardSignal && !foundUnsafeSwitch && bidi->isNormal()) {
    1099              :                 // look for switch that could protect from oncoming vehicles
    1100        24554 :                 for (const MSLink* const link : bidi->getLinkCont()) {
    1101        13811 :                     if (link->getDirection() == LinkDirection::TURN) {
    1102          531 :                         continue;
    1103              :                     }
    1104        26402 :                     if (!myBidi.empty() && link->getViaLaneOrLane() != myBidi.back()) {
    1105         2434 :                         myCoreSize = (int)myRoute.size() - 1;
    1106         2434 :                         MSLink* used = const_cast<MSLink*>(bidi->getLinkTo(myBidi.back()));
    1107              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1108              :                         if (gDebugFlag4) {
    1109              :                             std::cout << "      found unsafe switch " << link->getDescription() << " (used=" << (used == nullptr ? "NULL" : used->getDescription()) << ")\n";
    1110              :                         }
    1111              : #endif
    1112              :                         // trains along our route beyond this switch might create deadlock
    1113              :                         foundUnsafeSwitch = true;
    1114              :                         // the switch itself must still be guarded to ensure safety
    1115         2434 :                         if (used != nullptr) {
    1116              :                             // possibly nullptr if there was an intermediate section of unidirectional edges
    1117              :                             flankSwitches.insert(used);
    1118              :                         }
    1119              :                         break;
    1120              :                     }
    1121              :                 }
    1122              :             }
    1123        80328 :             if (foundUnsafeSwitch) {
    1124        40689 :                 myBidiExtended.push_back(bidi);
    1125              :             } else {
    1126        42073 :                 myBidi.push_back(bidi);
    1127              :             }
    1128              :         }
    1129       125120 :         const std::vector<MSLink*>& links = toLane->getLinkCont();
    1130       125120 :         toLane = nullptr;
    1131       132009 :         for (const MSLink* const link : links) {
    1132       123703 :             if ((next != end && &link->getLane()->getEdge() == *next)) {
    1133       116814 :                 toLane = link->getViaLaneOrLane();
    1134       116814 :                 if (link->getTLLogic() != nullptr && link->getTLIndex() >= 0 && link->getTLLogic()->getLogicType() == TrafficLightType::RAIL_SIGNAL) {
    1135        27669 :                     if (link == origin) {
    1136          356 :                         if (seekForwardSignal) {
    1137            0 :                             WRITE_WARNINGF(TL("Found circular block after % (% edges, length %)"), warnID, toString(myRoute.size()), toString(length));
    1138              :                         }
    1139              :                         //std::cout << getClickableTLLinkID(origin) << " circularBlock2=" << toString(myRoute) << "\n";
    1140          356 :                         myAbortedBuild = true;
    1141              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1142              :                         if (gDebugFlag4) {
    1143              :                             std::cout << " abort: found circle\n";
    1144              :                         }
    1145              : #endif
    1146          356 :                         return;
    1147              :                     }
    1148              :                     seekForwardSignal = false;
    1149        27313 :                     myFoundSignal = true;
    1150              :                     seekBidiSwitch = bidi != nullptr;
    1151              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1152              :                     if (gDebugFlag4) {
    1153              :                         std::cout << "      found forwardSignal " << link->getTLLogic()->getID() << " seekBidiSwitch=" << seekBidiSwitch << "\n";
    1154              :                     }
    1155              : #endif
    1156              :                 }
    1157              :                 //if (links.size() > 1 && !foundUnsafeSwitch) {
    1158       116458 :                 if (isSwitch(link)) {
    1159              :                     // switch on driveway
    1160              :                     //std::cout << "mySwitchDriveWays " << getID() << " link=" << link->getDescription() << "\n";
    1161        72754 :                     mySwitchDriveWays[link].push_back(this);
    1162              :                 }
    1163       116458 :                 if (link->getLane()->getBidiLane() != nullptr && &link->getLane()->getEdge() == current->getBidiEdge()) {
    1164              :                     // reversal on driveway
    1165         1519 :                     myReversalDriveWays[current].push_back(this);
    1166         1519 :                     myReversals.push_back(current);
    1167              :                 }
    1168              :                 break;
    1169              :             }
    1170              :         }
    1171       124764 :         if (toLane == nullptr) {
    1172         8306 :             if (next != end) {
    1173              :                 // no connection found, jump to next route edge
    1174              :                 toLane = (*next)->getLanes()[0];
    1175              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1176              :                 if (gDebugFlag4) {
    1177              :                     std::cout << "      abort: turn-around or jump\n";
    1178              :                 }
    1179              : #endif
    1180           90 :                 myFoundJump = true;
    1181           90 :                 return;
    1182              :             } else {
    1183              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1184              :                 if (gDebugFlag4) {
    1185              :                     std::cout << "      abort: no next lane available\n";
    1186              :                 }
    1187              : #endif
    1188         8216 :                 myTerminateRoute = true;
    1189         8216 :                 return;
    1190              :             }
    1191              :         }
    1192       116458 :     }
    1193              :     myBidiEnded = !seekBidiSwitch;
    1194              : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1195              :     if (gDebugFlag4) {
    1196              :         std::cout << " normalEnd myBidiEnded=" << myBidiEnded << "\n";
    1197              :     }
    1198              : #endif
    1199              : }
    1200              : 
    1201              : 
    1202              : bool
    1203       129018 : MSDriveWay::isSwitch(const MSLink* link) {
    1204       250332 :     for (const MSLink* other : link->getLaneBefore()->getNormalPredecessorLane()->getLinkCont()) {
    1205       141744 :         if (other->getLane() != link->getLane() && !other->isTurnaround()) {
    1206              :             return true;
    1207              :         }
    1208              :     }
    1209       168825 :     for (auto ili : link->getLane()->getIncomingLanes()) {
    1210       122600 :         if (ili.viaLink != link && !ili.viaLink->isTurnaround()) {
    1211              :             return true;
    1212              :         }
    1213              :     }
    1214        46225 :     const MSLane* bidi = link->getLane()->getBidiLane();
    1215        46225 :     if (bidi != nullptr) {
    1216        69608 :         for (const MSLink* other : bidi->getLinkCont()) {
    1217        35965 :             if (other->getLane() != link->getLaneBefore()->getNormalPredecessorLane()->getBidiLane() && !other->isTurnaround()) {
    1218              :                 return true;
    1219              :             }
    1220              :         }
    1221              :     }
    1222              :     return false;
    1223              : }
    1224              : 
    1225              : 
    1226              : void
    1227        35544 : MSDriveWay::checkFlanks(const MSLink* originLink, const std::vector<const MSLane*>& lanes, const LaneVisitedMap& visited,
    1228              :                         bool allFoes, bool movingBlock, std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess>& flankSwitches) const {
    1229              : #ifdef DEBUG_CHECK_FLANKS
    1230              :     if (gDebugFlag4) std::cout << " checkFlanks lanes=" << toString(lanes) << " allFoes=" << allFoes << "\n";
    1231              : #endif
    1232        21696 :     const MSLink* reverseOriginLink = originLink != nullptr && originLink->getLane()->getBidiLane() != nullptr && originLink->getLaneBefore()->getBidiLane() != nullptr
    1233        47420 :                                       ? originLink->getLane()->getBidiLane()->getLinkTo(originLink->getLaneBefore()->getBidiLane())
    1234              :                                       : nullptr;
    1235              :     //std::cout << "   originLink=" << originLink->getDescription() << "\n";
    1236        11876 :     if (reverseOriginLink != nullptr) {
    1237        11876 :         reverseOriginLink = reverseOriginLink->getCorrespondingExitLink();
    1238              :         //std::cout << "   reverseOriginLink=" << reverseOriginLink->getDescription() << "\n";
    1239              :     }
    1240       155340 :     for (int i = 0; i < (int)lanes.size(); i++) {
    1241       119796 :         const MSLane* lane = lanes[i];
    1242       119796 :         const MSLane* prev = i > 0 ? lanes[i - 1] : nullptr;
    1243       119796 :         const MSLane* next = i + 1 < (int)lanes.size() ? lanes[i + 1] : nullptr;
    1244       119796 :         if (lane->isInternal()) {
    1245        51614 :             continue;
    1246              :         }
    1247       141748 :         for (auto ili : lane->getIncomingLanes()) {
    1248        83412 :             if (ili.viaLink == originLink
    1249        72657 :                     || ili.viaLink == reverseOriginLink
    1250        69351 :                     || ili.viaLink->getDirection() == LinkDirection::TURN
    1251        63827 :                     || ili.viaLink->getDirection() == LinkDirection::TURN_LEFTHAND
    1252       137393 :                     || (originLink == nullptr && i == 0 && movingBlock)) {
    1253         9846 :                 continue;
    1254              :             }
    1255        63720 :             if (ili.lane != prev && ili.lane != next) {
    1256              : #ifdef DEBUG_CHECK_FLANKS
    1257              :                 if (gDebugFlag4) std::cout << " add flankSwitch junction=" << ili.viaLink->getJunction()->getID() << " index=" << ili.viaLink->getIndex() << " iLane=" << ili.lane->getID() << " prev=" << Named::getIDSecure(prev) <<  " targetLane=" << lane->getID() << " next=" << Named::getIDSecure(next) << "\n";
    1258              : #endif
    1259              :                 flankSwitches.insert(ili.viaLink);
    1260        51174 :             } else if (allFoes) {
    1261              :                 // link is part of the driveway, find foes that cross the driveway without entering
    1262        14756 :                 checkCrossingFlanks(ili.viaLink, visited, flankSwitches);
    1263              :             }
    1264              :         }
    1265              :     }
    1266        35544 : }
    1267              : 
    1268              : 
    1269              : void
    1270        14756 : MSDriveWay::checkCrossingFlanks(MSLink* dwLink, const LaneVisitedMap& visited, std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess>& flankSwitches) const {
    1271              : #ifdef DEBUG_CHECK_FLANKS
    1272              :     if (gDebugFlag4) std::cout << "  checkCrossingFlanks  dwLink=" << dwLink->getDescription() << " visited=" << formatVisitedMap(visited) << "\n";
    1273              : #endif
    1274              :     const MSJunction* junction = dwLink->getJunction();
    1275        14756 :     if (junction == nullptr) {
    1276              :         return; // unregulated junction;
    1277              :     }
    1278        14678 :     const MSJunctionLogic* logic = junction->getLogic();
    1279        14678 :     if (logic == nullptr) {
    1280              :         return; // unregulated junction;
    1281              :     }
    1282        79294 :     for (const MSEdge* in : junction->getIncoming()) {
    1283        64684 :         if (in->isInternal()) {
    1284        32817 :             continue;
    1285              :         }
    1286        64152 :         for (MSLane* inLane : in->getLanes()) {
    1287        32285 :             const MSLane* inBidi = inLane->getBidiLane();
    1288        44486 :             if (isRailwayOrShared(inLane->getPermissions()) && visited.count(inLane) == 0 && (inBidi == nullptr || visited.count(inBidi) == 0)) {
    1289         9457 :                 for (MSLink* link : inLane->getLinkCont()) {
    1290         4946 :                     if (link->getIndex() >= 0 && logic->getFoesFor(dwLink->getIndex()).test(link->getIndex())
    1291        12673 :                             && visited.count(link->getLane()) == 0) {
    1292              : #ifdef DEBUG_CHECK_FLANKS
    1293              :                         if (gDebugFlag4) std::cout << " add crossing flankSwitch junction=" << junction->getID() << " index=" << link->getIndex() << "\n";
    1294              : #endif
    1295          676 :                         if (link->getViaLane() == nullptr) {
    1296              :                             flankSwitches.insert(link);
    1297              :                         } else {
    1298              :                             flankSwitches.insert(link->getViaLane()->getLinkCont().front());
    1299              :                         }
    1300              :                     }
    1301              :                 }
    1302              :             }
    1303              :         }
    1304              :     }
    1305              : }
    1306              : 
    1307              : void
    1308        15658 : MSDriveWay::findFlankProtection(MSLink* link, MSLink* origLink, std::vector<const MSLane*>& flank) {
    1309              : #ifdef DEBUG_CHECK_FLANKS
    1310              :     if (gDebugFlag4) std::cout << "  findFlankProtection link=" << link->getDescription() << " origLink=" << origLink->getDescription() << "\n";
    1311              : #endif
    1312        15658 :     if (link->getCorrespondingEntryLink()->getTLLogic() != nullptr && link->getJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1313         2331 :         MSLink* entry = const_cast<MSLink*>(link->getCorrespondingEntryLink());
    1314              :         // guarded by signal
    1315              : #ifdef DEBUG_CHECK_FLANKS
    1316              :         if (gDebugFlag4) std::cout << "   flank guarded by " << entry->getTLLogic()->getID() << "\n";
    1317              : #endif
    1318              :         // @note, technically it's enough to collect links from foe driveways
    1319              :         // but this also adds "unused" conflict links which may aid comprehension
    1320         2331 :         myConflictLinks.push_back(entry);
    1321         2331 :         addFoes(entry);
    1322              :     } else {
    1323              :         const MSLane* lane = link->getLaneBefore();
    1324              :         std::vector<MSLink*> predLinks;
    1325        26676 :         for (auto ili : lane->getIncomingLanes()) {
    1326        13349 :             if (!ili.viaLink->isTurnaround()) {
    1327        13006 :                 predLinks.push_back(ili.viaLink);
    1328              :             }
    1329              :         }
    1330        13327 :         if (predLinks.size() > 1) {
    1331              :             // this is a switch
    1332              : #ifdef DEBUG_ADD_FOES
    1333              :             if (gDebugFlag4) std::cout << "    predecessors of " << link->getDescription() << " isSwitch\n";
    1334              : #endif
    1335          669 :             for (MSLink* pred : predLinks) {
    1336          446 :                 addSwitchFoes(pred);
    1337              :             }
    1338        13104 :         } else if (predLinks.size() == 1) {
    1339        12560 :             if (isSwitch(link)) {
    1340        11153 :                 addSwitchFoes(link);
    1341              :             } else {
    1342              :                 // continue upstream via single predecessor
    1343         1407 :                 findFlankProtection(predLinks.front(), origLink, flank);
    1344              :             }
    1345              :         }
    1346              :         // check for insertions
    1347        13327 :         if (myDepartureDriveways.count(&lane->getEdge()) != 0) {
    1348          342 :             for (MSDriveWay* foe : myDepartureDriveways[&lane->getEdge()]) {
    1349          177 :                 if (flankConflict(*foe) || crossingConflict(*foe)) {
    1350              : #ifdef DEBUG_ADD_FOES
    1351              :                     if (gDebugFlag4) std::cout << "  foe " << foe->getID() << " departs on flank=" << lane->getID() << "\n";
    1352              : #endif
    1353          123 :                     myFoes.push_back(foe);
    1354              :                 } else {
    1355              : #ifdef DEBUG_ADD_FOES
    1356              :                     if (gDebugFlag4) std::cout << "  cand foe " << foe->getID() << " departs on flank=" << lane->getID() << " rejected\n";
    1357              : #endif
    1358              :                 }
    1359              :             }
    1360              :         }
    1361        13327 :     }
    1362        15658 : }
    1363              : 
    1364              : 
    1365              : void
    1366        11599 : MSDriveWay::addSwitchFoes(MSLink* link) {
    1367              :     auto it = mySwitchDriveWays.find(link);
    1368        11599 :     if (it != mySwitchDriveWays.end()) {
    1369              : #ifdef DEBUG_ADD_FOES
    1370              :         if (gDebugFlag4) std::cout << "   driveway " << myID << " addSwitchFoes for link " << link->getDescription() << "\n";
    1371              : #endif
    1372        36182 :         for (MSDriveWay* foe : it->second) {
    1373        31874 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1374              : #ifdef DEBUG_ADD_FOES
    1375              :                 if (gDebugFlag4) std::cout << "   foe=" << foe->myID
    1376              :                           << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this)
    1377              :                           << " cc1=" << crossingConflict(*foe) << " cc2=" << foe->crossingConflict(*this) << "\n";
    1378              : #endif
    1379         4729 :                 myFoes.push_back(foe);
    1380              :             } else {
    1381              : #ifdef DEBUG_ADD_FOES
    1382              :                 if (gDebugFlag4) std::cout << "   cand=" << foe->myID << "\n";
    1383              : #endif
    1384              :             }
    1385              :         }
    1386              :     }
    1387        11599 : }
    1388              : 
    1389              : 
    1390              : MSDriveWay*
    1391         8886 : MSDriveWay::buildDriveWay(const std::string& id, const MSLink* link, MSRouteIterator first, MSRouteIterator end) {
    1392              :     // collect lanes and links that are relevant for setting this signal for the current driveWay
    1393              :     // For each driveway we collect
    1394              :     //   - conflictLanes (signal must be red if any conflict lane is occupied)
    1395              :     //   - conflictLinks (signal must be red if any conflict link is approached by a vehicle
    1396              :     //      - that cannot break in time (arrivalSpeedBraking > 0)
    1397              :     //      - approached by a vehicle with higher switching priority (see #3941)
    1398              :     // These objects are construct in steps:
    1399              :     //
    1400              :     // forwardBlock
    1401              :     // - search forward recursive from outgoing lane until controlled railSignal link found
    1402              :     //   -> add all found lanes to conflictLanes
    1403              :     //
    1404              :     // bidiBlock (if any forwardBlock edge has bidi edge)
    1405              :     // - search bidi backward recursive until first switch
    1406              :     //   - from switch search backward recursive all other incoming until controlled rail signal link
    1407              :     //     -> add final links to conflictLinks
    1408              :     //
    1409              :     // flanks
    1410              :     // - search backward recursive from flanking switches
    1411              :     //   until controlled railSignal link or protecting switch is found
    1412              :     //   -> add all found lanes to conflictLanes
    1413              :     //   -> add final links to conflictLinks
    1414         8886 :     MSDriveWay* dw = new MSDriveWay(link, id);
    1415              :     LaneVisitedMap visited;
    1416              :     std::vector<const MSLane*> before;
    1417         8886 :     MSLane* fromBidi = nullptr;
    1418         8886 :     if (link != nullptr) {
    1419         5424 :         appendMapIndex(visited, link->getLaneBefore());
    1420         5424 :         fromBidi = link->getLaneBefore()->getBidiLane();
    1421              :     }
    1422              :     std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess> flankSwitches; // list of switches that threaten the driveway and for which protection must be found
    1423              : 
    1424         8886 :     if (fromBidi != nullptr) {
    1425         3051 :         before.push_back(fromBidi);
    1426              :     }
    1427              : #ifdef DEBUG_BUILD_DRIVEWAY
    1428              :     gDebugFlag4 = DEBUG_COND_DW(dw);
    1429              : #endif
    1430         8886 :     dw->buildRoute(link, first, end, visited, flankSwitches);
    1431         8886 :     dw->myCoreSize = (int)dw->myRoute.size();
    1432              : 
    1433         8886 :     MSRailSignal* rs = link ? const_cast<MSRailSignal*>(static_cast<const MSRailSignal*>(link->getTLLogic())) : nullptr;
    1434         8886 :     const bool movingBlock = (rs && rs->isMovingBlock()) || (!rs &&
    1435        12348 :             (OptionsCont::getOptions().getBool("railsignal-moving-block")
    1436         3358 :              || MSRailSignalControl::isMovingBlock((*first)->getPermissions())));
    1437              : 
    1438         8886 :     dw->checkFlanks(link, dw->myForward, visited, true, movingBlock, flankSwitches);
    1439         8886 :     dw->checkFlanks(link, dw->myBidi, visited, false, movingBlock, flankSwitches);
    1440         8886 :     dw->checkFlanks(link, before, visited, true, movingBlock, flankSwitches);
    1441        18785 :     for (MSLink* fsLink : flankSwitches) {
    1442              : #ifdef DEBUG_ADD_FOES
    1443              :         if (DEBUG_COND_DW(dw)) {
    1444              :             std::cout << " fsLink=" << fsLink->getDescription() << "\n";
    1445              :         }
    1446              : #endif
    1447         9899 :         dw->findFlankProtection(fsLink, fsLink, dw->myFlank);
    1448              :     }
    1449              :     std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess> flankSwitchesBidiExtended;
    1450         8886 :     dw->checkFlanks(link, dw->myBidiExtended, visited, false, movingBlock, flankSwitchesBidiExtended);
    1451        13238 :     for (MSLink* const flink : flankSwitchesBidiExtended) {
    1452              : #ifdef DEBUG_ADD_FOES
    1453              :         if (DEBUG_COND_DW(dw)) {
    1454              :             std::cout << " fsLinkExtended=" << flink->getDescription() << "\n";
    1455              :         }
    1456              : #endif
    1457         4352 :         dw->findFlankProtection(flink, flink, dw->myBidiExtended);
    1458              :     }
    1459              : #ifdef DEBUG_BUILD_DRIVEWAY
    1460              :     if (DEBUG_COND_DW(dw)) {
    1461              :         std::cout << SIMTIME << " buildDriveWay " << dw->myID << " link=" << (link == nullptr ? "NULL" : link->getDescription())
    1462              :                   << "\n    route=" << toString(dw->myRoute)
    1463              :                   << "\n    forward=" << toString(dw->myForward)
    1464              :                   << "\n    bidi=" << toString(dw->myBidi)
    1465              :                   << "\n    bidiEx=" << toString(dw->myBidiExtended)
    1466              :                   << "\n    flank=" << toString(dw->myFlank)
    1467              :                   << "\n    flankSwitch=" << MSRailSignal::describeLinks(std::vector<MSLink*>(flankSwitches.begin(), flankSwitches.end()))
    1468              :                   << "\n    coreSize=" << dw->myCoreSize
    1469              :                   << "\n";
    1470              :     }
    1471              : #endif
    1472         8886 :     if (!rs || !rs->isMovingBlock()) {
    1473         8780 :         dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myForward.begin(), dw->myForward.end());
    1474              :     }
    1475         8886 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myBidi.begin(), dw->myBidi.end());
    1476         8886 :     dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myFlank.begin(), dw->myFlank.end());
    1477         8886 :     dw->addBidiFoes(rs, false);
    1478         8886 :     dw->addBidiFoes(rs, true);
    1479         8886 :     if (!movingBlock) {
    1480              :         // add driveways that start on the same signal / lane
    1481         8598 :         dw->addParallelFoes(link, *first);
    1482              :     }
    1483              :     // add driveways that reverse along this driveways route
    1484         8886 :     dw->addReversalFoes(movingBlock);
    1485              :     // make foes unique and symmetrical
    1486         8886 :     std::set<MSDriveWay*, ComparatorNumericalIdLess> uniqueFoes(dw->myFoes.begin(), dw->myFoes.end());
    1487              :     dw->myFoes.clear();
    1488              :     // check for self-intersecting forward-section in movingBlock mode
    1489         8886 :     if (movingBlock && uniqueFoes.count(dw) == 0) {
    1490              :         std::set<const MSJunction*> forwardJunctions;
    1491         2838 :         for (const MSLane* fw : dw->myForward) {
    1492         2594 :             if (fw->isNormal()) {
    1493         1379 :                 const MSJunction* fwTo = fw->getEdge().getToJunction();
    1494              :                 if (forwardJunctions.count(fwTo) == 1) {
    1495           21 :                     dw->myFoes.push_back(dw);
    1496              : #ifdef DEBUG_ADD_FOES
    1497              :                     if (DEBUG_COND_DW(dw)) {
    1498              :                         std::cout << " self-intersecting movingBlock for dw=" << dw->getID() << "\n";
    1499              :                     }
    1500              : #endif
    1501           21 :                     break;
    1502              :                 }
    1503              :                 forwardJunctions.insert(fwTo);
    1504              :             }
    1505              :         }
    1506              :     }
    1507         8886 :     std::set<MSLink*, MSLink::ComparatorNumericalLaneIdLess> uniqueCLink(dw->myConflictLinks.begin(), dw->myConflictLinks.end());
    1508         8886 :     const MSEdge* lastEdge = &dw->myForward.back()->getEdge();
    1509        16080 :     for (MSDriveWay* foe : uniqueFoes) {
    1510         7194 :         const MSEdge* foeLastEdge = &foe->myForward.back()->getEdge();
    1511         7194 :         const bool sameLast = foeLastEdge == lastEdge;
    1512         7194 :         if (sameLast && !movingBlock) {
    1513         2696 :             dw->myFoes.push_back(foe);
    1514         2696 :             if (foe != dw) {
    1515         2696 :                 foe->myFoes.push_back(dw);
    1516              :             }
    1517              :         } else {
    1518         4498 :             if (foe->bidiBlockedByEnd(*dw)) {
    1519              : #ifdef DEBUG_ADD_FOES
    1520              :                 if (DEBUG_COND_DW(dw)) {
    1521              :                     std::cout << " setting " << dw->getID() << " as foe of " << foe->getID() << "\n";
    1522              :                 }
    1523              : #endif
    1524         2178 :                 foe->myFoes.push_back(dw);
    1525         2178 :                 foe->addSidings(dw);
    1526              :             } else {
    1527         2320 :                 dw->buildSubFoe(foe, movingBlock);
    1528              :             }
    1529         4498 :             if (foe != dw) { // check for movingBlock
    1530         4475 :                 if (dw->bidiBlockedByEnd(*foe)) {
    1531              : #ifdef DEBUG_ADD_FOES
    1532              :                     if (DEBUG_COND_DW(dw)) {
    1533              :                         std::cout << " addFoeCheckSiding " << foe->getID() << "\n";
    1534              :                     }
    1535              : #endif
    1536         2422 :                     dw->myFoes.push_back(foe);
    1537         2422 :                     dw->addSidings(foe);
    1538              :                 } else  {
    1539         2053 :                     foe->buildSubFoe(dw, movingBlock);
    1540              :                 }
    1541              :             }
    1542              :         }
    1543         7194 :         if (link) {
    1544         5158 :             foe->addConflictLink(link);
    1545              :         }
    1546              :         // ignore links that have the same start junction
    1547         7194 :         if (foe->myRoute.front()->getFromJunction() != dw->myRoute.front()->getFromJunction()) {
    1548         8918 :             for (auto ili : foe->myForward.front()->getIncomingLanes()) {
    1549         4023 :                 if (ili.viaLink->getTLLogic() != nullptr) {
    1550              :                     // ignore links that originate on myBidi
    1551         3563 :                     const MSLane* origin = ili.viaLink->getLaneBefore();
    1552         3563 :                     if (std::find(dw->myBidi.begin(), dw->myBidi.end(), origin) == dw->myBidi.end()) {
    1553              :                         uniqueCLink.insert(ili.viaLink);
    1554              :                     }
    1555              :                 }
    1556              :             }
    1557              :         }
    1558              :     }
    1559              :     dw->myConflictLinks.clear();
    1560         8886 :     dw->myConflictLinks.insert(dw->myConflictLinks.begin(), uniqueCLink.begin(), uniqueCLink.end());
    1561         8886 :     myEndingDriveways[lastEdge].push_back(dw);
    1562         8886 :     if (!movingBlock) {
    1563              :         // every driveway is it's own foe (also all driveways that depart in the same block)
    1564        20186 :         for (MSDriveWay* sameEnd : myEndingDriveways[lastEdge]) {
    1565              :             if (uniqueFoes.count(sameEnd) == 0) {
    1566         8892 :                 dw->myFoes.push_back(sameEnd);
    1567         8892 :                 if (sameEnd != dw) {
    1568          294 :                     sameEnd->myFoes.push_back(dw);
    1569              :                 }
    1570              :             }
    1571              :         }
    1572              :     }
    1573              : #ifdef DEBUG_BUILD_DRIVEWAY
    1574              :     if (DEBUG_COND_DW(dw)) {
    1575              :         std::cout << dw->myID << " mb=" << movingBlock << " finalFoes " << toString(dw->myFoes) << "\n";
    1576              :     }
    1577              :     gDebugFlag4 = false;
    1578              : #endif
    1579         8886 :     return dw;
    1580         8886 : }
    1581              : 
    1582              : std::string
    1583         4494 : MSDriveWay::getTLLinkID(const MSLink* link) {
    1584         8988 :     return link->getTLLogic()->getID() + "_" + toString(link->getTLIndex());
    1585              : }
    1586              : 
    1587              : std::string
    1588            0 : MSDriveWay::getJunctionLinkID(const MSLink* link) {
    1589            0 :     return link->getJunction()->getID() + "_" + toString(link->getIndex());
    1590              : }
    1591              : 
    1592              : std::string
    1593         5424 : MSDriveWay::getClickableTLLinkID(const MSLink* link) {
    1594        16272 :     return "junction '" +  link->getTLLogic()->getID() + "', link " + toString(link->getTLIndex());
    1595              : }
    1596              : 
    1597              : std::string
    1598            0 : MSDriveWay::formatVisitedMap(const LaneVisitedMap& visited) {
    1599              :     UNUSED_PARAMETER(visited);
    1600              :     /*
    1601              :     std::vector<const MSLane*> lanes(visited.size(), nullptr);
    1602              :     for (auto item : visited) {
    1603              :         lanes[item.second] = item.first;
    1604              :     }
    1605              :     for (auto it = lanes.begin(); it != lanes.end();) {
    1606              :         if (*it == nullptr) {
    1607              :             it = lanes.erase(it);
    1608              :         } else {
    1609              :             it++;
    1610              :         }
    1611              :     }
    1612              :     return toString(lanes);
    1613              :     */
    1614            0 :     return "dummy";
    1615              : }
    1616              : 
    1617              : 
    1618              : void
    1619       130544 : MSDriveWay::appendMapIndex(LaneVisitedMap& map, const MSLane* lane) {
    1620              :     // avoid undefined behavior from evaluation order
    1621       130544 :     const int tmp = (int)map.size();
    1622       130544 :     map[lane] = tmp;
    1623       130544 : }
    1624              : 
    1625              : bool
    1626      9242247 : MSDriveWay::match(MSRouteIterator firstIt, MSRouteIterator endIt) const {
    1627              :     // @todo optimize: it is sufficient to check for specific edges (after each switch)
    1628      9242247 :     auto itRoute = firstIt;
    1629              :     auto itDwRoute = myRoute.begin();
    1630              :     bool match = true;
    1631     64229851 :     while (itRoute != endIt && itDwRoute != myRoute.end()) {
    1632     55179943 :         if (*itRoute != *itDwRoute) {
    1633              :             match = false;
    1634              : #ifdef DEBUG_MATCH
    1635              :             std::cout << "  check dw=" << getID() << " match failed at vehEdge=" << (*itRoute)->getID() << " dwEdge=" << (*itDwRoute)->getID() << "\n";
    1636              : #endif
    1637              :             break;
    1638              :         }
    1639              :         itRoute++;
    1640              :         itDwRoute++;
    1641              :     }
    1642              :     // if the vehicle arrives before the end of this driveway,
    1643              :     // we'd rather build a new driveway to avoid superfluous restrictions
    1644      9049908 :     if (match && itDwRoute == myRoute.end()
    1645     18268806 :             && (itRoute == endIt || myAbortedBuild || myBidiEnded || myFoundJump || isSubDriveWay())) {
    1646              :         //std::cout << "  using dw=" << "\n";
    1647      9015059 :         if (itRoute != endIt) {
    1648              :             // check whether the current route requires an extended driveway
    1649       143213 :             const MSEdge* next = *itRoute;
    1650       143213 :             const MSEdge* prev = myRoute.back();
    1651          414 :             if (myFoundJump && prev->getBidiEdge() != next && prev->getBidiEdge() != nullptr
    1652       143475 :                     && prev->isConnectedTo(*next, (SUMOVehicleClass)(SVC_RAIL_CLASSES & prev->getPermissions()))) {
    1653              : #ifdef DEBUG_MATCH
    1654              :                 std::cout << "  check dw=" << getID() << " prev=" << prev->getID() << " next=" << next->getID() << "\n";
    1655              : #endif
    1656              :                 return false;
    1657              :             }
    1658       143189 :             if (!myFoundJump && prev->getBidiEdge() == next && prev == &myForward.back()->getEdge()) {
    1659              :                 assert(isSubDriveWay() || myBidiEnded);
    1660              :                 // must not leave driveway via reversal
    1661              : #ifdef DEBUG_MATCH
    1662              :                 std::cout << getID() << " back=" << myForward.back()->getID() << " noMatch route " << toString(ConstMSEdgeVector(firstIt, endIt)) << "\n";
    1663              : #endif
    1664              :                 return false;
    1665              :             }
    1666       143183 :             if (myForward.back()->isInternal() && myForward.back()->getNextNormal() != (*itRoute)) {
    1667              :                 // driveway is part of a direct-control conflict and continues elsewhere
    1668              : #ifdef DEBUG_MATCH
    1669              :                 std::cout << getID() << " back=" << myForward.back()->getID() << " noMatch route " << toString(ConstMSEdgeVector(firstIt, itRoute)) << " (direct control)\n";
    1670              : #endif
    1671              :                 return false;
    1672              :             }
    1673              :         }
    1674      9015027 :         return true;
    1675              :     }
    1676              :     return false;
    1677              : }
    1678              : 
    1679              : void
    1680        12569 : MSDriveWay::addFoes(const MSLink* link) {
    1681              : #ifdef DEBUG_ADD_FOES
    1682              :     if (gDebugFlag4) std::cout << "driveway " << myID << " addFoes for link " << link->getDescription() << "\n";
    1683              : #endif
    1684        12569 :     const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(link->getTLLogic());
    1685        12569 :     if (rs != nullptr) {
    1686        17407 :         for (MSDriveWay* foe : rs->retrieveDriveWays(link->getTLIndex())) {
    1687              : #ifdef DEBUG_ADD_FOES
    1688              :             if (gDebugFlag4) std::cout << "  cand foe=" << foe->myID << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this) << " cc1=" << crossingConflict(*foe) << " cc2=" <<  foe->crossingConflict(*this) << "\n";
    1689              : #endif
    1690         4838 :             if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
    1691              : #ifdef DEBUG_ADD_FOES
    1692              :                 if (gDebugFlag4) std::cout << "   foe=" << foe->myID << "\n";
    1693              : #endif
    1694         3760 :                 myFoes.push_back(foe);
    1695              :             }
    1696        12569 :         }
    1697              :     }
    1698        12569 : }
    1699              : 
    1700              : 
    1701              : void
    1702        17772 : MSDriveWay::addBidiFoes(const MSRailSignal* ownSignal, bool extended) {
    1703              : #ifdef DEBUG_ADD_FOES
    1704              :     if (gDebugFlag4) std::cout << "driveway " << myID << " addBidiFoes extended=" << extended << "\n";
    1705              : #endif
    1706        17772 :     const std::vector<const MSLane*>& bidiLanes = extended ? myBidiExtended : myBidi;
    1707       100534 :     for (const MSLane* bidi : bidiLanes) {
    1708       168949 :         for (auto ili : bidi->getIncomingLanes()) {
    1709        86187 :             const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(ili.viaLink->getTLLogic());
    1710        86187 :             if (rs != nullptr && rs != ownSignal &&
    1711        86187 :                     std::find(bidiLanes.begin(), bidiLanes.end(), ili.lane) != bidiLanes.end()) {
    1712         4920 :                 addFoes(ili.viaLink);
    1713              :             }
    1714              :         }
    1715        82762 :         const MSEdge* bidiEdge = &bidi->getEdge();
    1716              :         if (myDepartureDriveways.count(bidiEdge) != 0) {
    1717         3586 :             for (MSDriveWay* foe : myDepartureDriveways[bidiEdge]) {
    1718         1666 :                 if (flankConflict(*foe)) {
    1719              : #ifdef DEBUG_ADD_FOES
    1720              :                     if (gDebugFlag4) std::cout << "  foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << "\n";
    1721              : #endif
    1722         1060 :                     myFoes.push_back(foe);
    1723              :                 } else {
    1724              : #ifdef DEBUG_ADD_FOES
    1725              :                     if (gDebugFlag4) std::cout << "  cand foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << " rejected\n";
    1726              : #endif
    1727              :                 }
    1728              :             }
    1729              :         }
    1730              :         if (myDepartureDrivewaysEnds.count(bidiEdge) != 0) {
    1731         3069 :             for (MSDriveWay* foe : myDepartureDrivewaysEnds[bidiEdge]) {
    1732         1579 :                 if (flankConflict(*foe)) {
    1733              : #ifdef DEBUG_ADD_FOES
    1734              :                     if (gDebugFlag4) std::cout << "  foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << "\n";
    1735              : #endif
    1736         1037 :                     myFoes.push_back(foe);
    1737              :                 } else {
    1738              : #ifdef DEBUG_ADD_FOES
    1739              :                     if (gDebugFlag4) std::cout << "  cand foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << " rejected\n";
    1740              : #endif
    1741              :                 }
    1742              :             }
    1743              :         }
    1744              :     }
    1745        17772 : }
    1746              : 
    1747              : 
    1748              : void
    1749         8598 : MSDriveWay::addParallelFoes(const MSLink* link, const MSEdge* first) {
    1750              : #ifdef DEBUG_ADD_FOES
    1751              :     if (gDebugFlag4) std::cout << "driveway " << myID << " addParallelFoes\n";
    1752              : #endif
    1753         8598 :     if (link) {
    1754         5318 :         addFoes(link);
    1755              :     } else {
    1756              :         auto it = myDepartureDriveways.find(first);
    1757         3280 :         if (it != myDepartureDriveways.end()) {
    1758         3505 :             for (MSDriveWay* foe : it->second) {
    1759              : #ifdef DEBUG_ADD_FOES
    1760              :                 if (gDebugFlag4) std::cout << "  foe " << foe->getID() << " departs on first=" << first->getID() << "\n";
    1761              : #endif
    1762          264 :                 myFoes.push_back(foe);
    1763              :             }
    1764              :         }
    1765              :     }
    1766         8598 : }
    1767              : 
    1768              : 
    1769              : void
    1770         8886 : MSDriveWay::addReversalFoes(bool movingBlock) {
    1771              : #ifdef DEBUG_ADD_FOES
    1772              :     if (gDebugFlag4) std::cout << "driveway " << myID << " addReversalFoes\n";
    1773              : #endif
    1774              :     std::set<const MSEdge*> forward;
    1775        42869 :     for (const MSLane* lane : myForward) {
    1776        33983 :         if (lane->isNormal()) {
    1777        19964 :             forward.insert(&lane->getEdge());
    1778              :         }
    1779              :     }
    1780              :     int i = 0;
    1781        76025 :     for (const MSEdge* e : myRoute) {
    1782        19974 :         if (forward.count(e) != 0 && !movingBlock) {
    1783              :             // reversals in our own forward can be ignored because each driveway
    1784              :             // is automatically a foe of itself by default
    1785              :             continue;
    1786              :         }
    1787        48692 :         if (i == myCoreSize) {
    1788              :             break;
    1789              :         }
    1790        48692 :         i++;
    1791              :         auto it = myReversalDriveWays.find(e);
    1792        48692 :         if (it != myReversalDriveWays.end()) {
    1793         6721 :             for (MSDriveWay* foe : it->second) {
    1794              :                 // check whether the foe reverses into our own forward section
    1795              :                 // (it might reverse again or disappear via arrival)
    1796              : #ifdef DEBUG_ADD_FOES
    1797              :                 //std::cout << "  candidate foe " << foe->getID() << " reverses on edge=" << e->getID() << " forward=" << joinNamedToString(forward, " ") << " foeRoute=" << toString(foe->myRoute) << "\n";
    1798              : #endif
    1799        11760 :                 if (forwardRouteConflict(forward, *foe)) {
    1800              :                     std::set<const MSEdge*> foeForward;
    1801         1795 :                     for (const MSLane* lane : foe->myForward) {
    1802         1557 :                         if (lane->isNormal()) {
    1803          801 :                             foeForward.insert(&lane->getEdge());
    1804          801 :                             if (lane->getBidiLane() != nullptr) {
    1805          790 :                                 foeForward.insert(lane->getEdge().getBidiEdge());
    1806              :                             }
    1807              :                         }
    1808              :                     }
    1809              : #ifdef DEBUG_ADD_FOES
    1810              :                     if (gDebugFlag4) std::cout << "  reversal cand=" << foe->getID() << " foeForward " << toString(foeForward) << "\n";
    1811              : #endif
    1812          476 :                     if (foe->forwardRouteConflict(foeForward, *this, true)) {
    1813              : #ifdef DEBUG_ADD_FOES
    1814              :                         if (gDebugFlag4) std::cout << "  foe " << foe->getID() << " reverses on edge=" << e->getID() << "\n";
    1815              : #endif
    1816          192 :                         myFoes.push_back(foe);
    1817              :                     }
    1818         5642 :                 } else if (movingBlock && foe == this) {
    1819              : #ifdef DEBUG_ADD_FOES
    1820              :                     if (gDebugFlag4) std::cout << "  dw " << getID() << " reverses on forward edge=" << e->getID() << " (movingBlock)\n";
    1821              : #endif
    1822           23 :                     myFoes.push_back(foe);
    1823              :                 }
    1824              :             }
    1825              :         }
    1826              :     }
    1827         8886 : }
    1828              : 
    1829              : 
    1830              : bool
    1831         4373 : MSDriveWay::buildSubFoe(MSDriveWay* foe, bool movingBlock) {
    1832              :     // Subdriveways (Teilfahrstraße) model the resolution of a driving conflict
    1833              :     // before a vehicle has left the driveway. This is possible when the driveway diverges from the foe
    1834              :     // driveway at an earlier point (switch or crossing).
    1835              :     //
    1836              :     // We already know that the last edge of this driveway doesn't impact the foe (unless the driveway ends within the block).
    1837              :     // Remove further edges from the end of the driveway (myForward) until the point of conflict is found.
    1838              :     //
    1839              :     // For movingBlock the logic is changed:
    1840              :     // We remove the conflict-free part as before but then keep removing the conflict part until another non-conconflit part is found
    1841         4373 :     if (myForward.size() < foe->myForward.size() &&
    1842         4373 :             myForward == std::vector<const MSLane*>(foe->myForward.begin(), foe->myForward.begin() + myForward.size())) {
    1843              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1844              :         if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " is subpart of foe=" << foe->getID() << "\n";
    1845              : #endif
    1846           82 :         foe->myFoes.push_back(this);
    1847           82 :         return true;
    1848              :     }
    1849         4291 :     int subLast = (int)myForward.size() - 2;
    1850         4291 :     if (movingBlock && myForward.back() == foe->myForward.back()) {
    1851           69 :         subLast++;
    1852              :     }
    1853              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1854              :     if (subLast < 0) {
    1855              :         if (gDebugFlag4) std::cout << "  " << getID() << " cannot build subDriveWay for foe " << foe->getID() << " because myForward has only a single lane\n";
    1856              :     }
    1857              : #endif
    1858              :     bool foundConflict = false;
    1859              :     bool flankC = false;
    1860              :     bool zipperC = false;
    1861              :     bool crossC = false;
    1862        15067 :     while (subLast >= 0) {
    1863        14901 :         const MSLane* lane = myForward[subLast];
    1864        14901 :         const MSLink* tmpOrigin = subLast > 0 ? myForward[subLast - 1]->getLinkTo(lane) : myOrigin;
    1865        14901 :         MSDriveWay tmp(tmpOrigin, "tmp", true);
    1866        14901 :         tmp.myForward.push_back(lane);
    1867        14901 :         tmp.myBidi = myBidi;
    1868        14901 :         tmp.myBidiExtended = myBidiExtended;
    1869        14901 :         tmp.myRoute.push_back(lane->getNextNormal());
    1870        14901 :         tmp.myCoreSize = 1;
    1871        14901 :         flankC = tmp.flankConflict(*foe);
    1872        14901 :         const bool bidiConflict = std::find(foe->myBidi.begin(), foe->myBidi.end(), lane) != foe->myBidi.end();
    1873        14901 :         crossC = tmp.crossingConflict(*foe);
    1874              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1875              :         if (gDebugFlag4) std::cout << "  subLast=" << subLast << " lane=" << lane->getID() << " fc=" << flankC << " cc=" << crossC << " bc=" << bidiConflict << "\n";
    1876              : #endif
    1877        14901 :         if (flankC || crossC || bidiConflict) {
    1878              :             foundConflict = true;
    1879         4433 :             if (!movingBlock || bidiConflict) {
    1880              :                 break;
    1881              :             }
    1882          356 :             if (((flankC && lane->getFromJunction()->getType() == SumoXMLNodeType::ZIPPER)
    1883           46 :                     || (!flankC && lane->getToJunction()->getType() == SumoXMLNodeType::ZIPPER))
    1884          429 :                     && (isDepartDriveway()
    1885           15 :                         || getForwardDistance(flankC ? subLast - 1 : subLast) > myMovingBlockMaxDist)) {
    1886              :                 zipperC = true;
    1887              :                 foundConflict = false;
    1888              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1889              :                 if (gDebugFlag4) std::cout << "     ignored movingBlock zipperConflict\n";
    1890              : #endif
    1891           18 :                 if (!flankC && crossC) {
    1892              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1893              :                     if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " movingBlock-save\n";
    1894              : #endif
    1895              :                     return false;
    1896              :                 }
    1897              :             }
    1898          393 :             if (!flankC && crossC) {
    1899              :                 break;
    1900              :             }
    1901        10468 :         } else if (foundConflict) {
    1902              :             break;
    1903              :         }
    1904        10776 :         subLast--;
    1905        14901 :     }
    1906              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1907              :     if (gDebugFlag4) std::cout << "  subLastFinal=" << subLast << " movingBlock=" << movingBlock << " zipperC=" << zipperC << "\n";
    1908              : #endif
    1909         4282 :     if (bidiBlockedByEnd(*foe) && bidiBlockedBy(*this) && foe->forwardEndOnRoute(this)) {
    1910              :         //std::set<const MSEdge*> firstEdge;
    1911              :         //firstEdge.insert(foe->myRoute.front());
    1912              : 
    1913          248 :         ConstMSEdgeVector forward(myRoute.begin(), myRoute.begin() + myForwardEdgeCount);
    1914              :         ConstMSEdgeVector foeAfterForward(foe->myRoute.begin() + foe->myForwardEdgeCount,
    1915          248 :                 foe->myRoute.begin() + MIN2(foe->myRoute.size(), foe->myForwardEdgeCount + forward.size()));
    1916              :         // @todo the check for forwardRouteConflict correctly reduces waiting in
    1917              :         // test rail/reversal/consecutive_before_reversal but creates deadlock in
    1918              :         // test rail/reversal/reversal_onRoute_beyond_core3b
    1919          248 :         if (forward == foeAfterForward /*&& forwardRouteConflict(firstEdge, *this, true)*/) {
    1920          126 :             foe->myFoes.push_back(this);
    1921              :             // foe will get the sidings
    1922          126 :             addSidings(foe, true);
    1923              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1924              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " special case 1\n";
    1925              : #endif
    1926              :             return true;
    1927              :         }
    1928          248 :     }
    1929         4156 :     if (subLast < 0) {
    1930          136 :         if (movingBlock && zipperC) {
    1931              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1932              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " movingBlock-save\n";
    1933              : #endif
    1934              :             return false;
    1935          136 :         } else if (&myForward.back()->getEdge() == myRoute.back() && foe->forwardEndOnRoute(this)) {
    1936              :             // driveway ends in the middle of the block and only the final edge overlaps with the foe driveWay
    1937           15 :             foe->myFoes.push_back(this);
    1938              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1939              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " foe endsOnForward\n";
    1940              : #endif
    1941          121 :         } else if (foe->myTerminateRoute) {
    1942          121 :             if (bidiBlockedByEnd(*foe) && bidiBlockedBy(*this) && foe->forwardEndOnRoute(this)) {
    1943            5 :                 foe->myFoes.push_back(this);
    1944              :                 // foe will get the sidings
    1945            5 :                 addSidings(foe, true);
    1946              :             }
    1947              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1948              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " terminates\n";
    1949              : #endif
    1950            0 :         } else if (myTerminateRoute && myBidi.size() <= myForward.size()) {
    1951            0 :             foe->myFoes.push_back(this);
    1952              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1953              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " terminates, foe=" << foe->getID() << "\n";
    1954              : #endif
    1955            0 :             return true;
    1956              :         } else if (foe->myReversals.size() % 2 == 1) {
    1957              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1958              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " has " << foe->myReversals.size() << " reversals\n";
    1959              : #endif
    1960              :         } else {
    1961              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1962              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " failed\n";
    1963              : #endif
    1964              : #ifdef SUBDRIVEWAY_WARN_NOCONFLICT
    1965              :             WRITE_WARNINGF("No point of conflict found between driveway '%' and driveway '%' when creating sub-driveway", getID(), foe->getID());
    1966              : #endif
    1967              :         }
    1968          136 :         return false;
    1969              :     }
    1970         4020 :     int subSize = subLast + 1;
    1971         4952 :     for (MSDriveWay* cand : mySubDriveWays) {
    1972         2409 :         if ((int)cand->myForward.size() == subSize) {
    1973              :             // can re-use existing sub-driveway
    1974         1477 :             foe->myFoes.push_back(cand);
    1975         1477 :             if (foe->bidiBlockedByEnd(*cand)) {
    1976         1343 :                 foe->addSidings(cand);
    1977              :             }
    1978         1477 :             cand->myFoes.push_back(foe);
    1979              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    1980              :             if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " useExisting=" << cand->getID() << "\n";
    1981              : #endif
    1982         1477 :             return true;
    1983              :         }
    1984              :     }
    1985         2543 :     std::vector<const MSLane*> forward(myForward.begin(), myForward.begin() + subSize);
    1986              :     std::vector<const MSEdge*> route;
    1987        14722 :     for (const MSLane* lane : forward) {
    1988        12179 :         if (lane->isNormal()) {
    1989         6007 :             route.push_back(&lane->getEdge());
    1990              :         }
    1991              :     }
    1992         2543 :     if (route.empty()) {
    1993           55 :         if (subSize == 1 && crossC
    1994           52 :                 && forward.front()->getFromJunction() == foe->myForward.front()->getFromJunction()
    1995          107 :                 && forward.front()->getFromJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    1996              :             assert(myForward.front()->isInternal());
    1997              :             // sub-driveway ends after a single internal lane but since the route cannot be empty we add the next edge
    1998           52 :             route.push_back(foe->myForward.front()->getEdge().getNormalSuccessor());
    1999              :         }  else {
    2000              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2001              :             if (gDebugFlag4) std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID() << " empty subRoute\n";
    2002              : #endif
    2003            3 :             return false;
    2004              :         }
    2005              :     }
    2006         2540 :     if (myRoute.size() > route.size()) {
    2007              :         // route continues. make sure the subDriveway does not end with a reversal
    2008         2530 :         const MSEdge* lastNormal = route.back();
    2009         2530 :         const MSEdge* nextNormal = myRoute[route.size()];
    2010         2530 :         if (lastNormal->getBidiEdge() == nextNormal) {
    2011              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2012              :             if (gDebugFlag4) std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID()
    2013              :                       << " lastNormal=" << lastNormal->getID() << " nextNormal=" << nextNormal->getID() << " endWithReversal\n";
    2014              : #endif
    2015              :             return false;
    2016              :         }
    2017              :     }
    2018         5040 :     MSDriveWay* sub = new MSDriveWay(myOrigin, getID() + "." + toString(mySubDriveWays.size()));
    2019         2520 :     sub->myLane = myLane;
    2020         2520 :     sub->myParent = this;
    2021         2520 :     sub->myForward = forward;
    2022         2520 :     sub->myRoute = route;
    2023         2520 :     sub->myCoreSize = (int)sub->myRoute.size();
    2024         2520 :     myLane->addMoveReminder(sub, false);
    2025              : 
    2026              :     // copy trains that are currently on this driveway (and associated entry events)
    2027         2665 :     for (SUMOVehicle* veh : myTrains) {
    2028          145 :         auto itOnSub = std::find(sub->myRoute.begin(), sub->myRoute.end(), veh->getEdge());
    2029          145 :         if (itOnSub != sub->myRoute.end()) {
    2030              :             sub->myTrains.insert(veh);
    2031              :             // non-zero is enough to avoid superfluous activation via activateReminders (and removal)
    2032          125 :             const double pos = sub->myRoute.front()->getLength();
    2033          125 :             dynamic_cast<MSBaseVehicle*>(veh)->addReminder(sub, pos);
    2034          143 :             for (const VehicleEvent& ve : myVehicleEvents) {
    2035           18 :                 if (ve.id == veh->getID()) {
    2036           18 :                     sub->myVehicleEvents.push_back(ve);
    2037              :                 }
    2038              :             }
    2039              :         }
    2040              :     }
    2041              : 
    2042         2520 :     foe->myFoes.push_back(sub);
    2043         2520 :     if (foe->bidiBlockedByEnd(*sub)) {
    2044         1759 :         foe->addSidings(sub);
    2045              :     }
    2046         2520 :     sub->myFoes.push_back(foe);
    2047         2520 :     mySubDriveWays.push_back(sub);
    2048              : #ifdef DEBUG_BUILD_SUBDRIVEWAY
    2049              :     if (gDebugFlag4) std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " sub=" << sub->getID() << " route=" << toString(sub->myRoute) << "\n";
    2050              : #endif
    2051              :     return true;
    2052         2543 : }
    2053              : 
    2054              : 
    2055              : double
    2056           15 : MSDriveWay::getForwardDistance(int lastIndex) const {
    2057              :     assert(lastIndex < (int)myForward.size());
    2058              :     double result = 0;
    2059           57 :     for (int i = 0; i <= lastIndex; i++) {
    2060           42 :         result += myForward[i]->getLength();
    2061              :     }
    2062           15 :     return result;
    2063              : }
    2064              : 
    2065              : 
    2066              : void
    2067         7833 : MSDriveWay::addSidings(MSDriveWay* foe, bool addToFoe) {
    2068         7833 :     const ConstMSEdgeVector& foeRoute = foe->isSubDriveWay() ? foe->myParent->myRoute : foe->myRoute;
    2069         7833 :     const MSEdge* foeEndBidi = foe->myForward.back()->getEdge().getBidiEdge();
    2070              :     int foeForwardNormals = 0;
    2071        41444 :     for (auto lane : foe->myForward) {
    2072        33611 :         if (lane->isNormal()) {
    2073        17868 :             foeForwardNormals++;
    2074              :         }
    2075              :     }
    2076         7833 :     if (foeForwardNormals == (int)foeRoute.size()) {
    2077              : #ifdef DEBUG_BUILD_SIDINGS
    2078              :         if (gDebugFlag4) std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " foeForwardNormals=" << foeForwardNormals << " frSize=" << foeRoute.size() <<  " aborted\n";
    2079              : #endif
    2080         1005 :         return;
    2081              :     }
    2082              :     auto foeSearchBeg = foeRoute.begin() + foeForwardNormals;
    2083              :     auto foeSearchEnd = foeRoute.end();
    2084         6978 :     if (foeEndBidi == nullptr) {
    2085            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " noBidi\n");
    2086              :     }
    2087              :     // if foe is a subDriveway, the forward section may end on an internal edge which would not be found on myRoute
    2088         6978 :     foeEndBidi = foeEndBidi->getNormalSuccessor();
    2089         6978 :     std::set<const MSEdge*> foeForwardEdges(foeRoute.begin(), foeRoute.begin() + foeForwardNormals);
    2090              :     int forwardNormals = 0;
    2091        48243 :     for (auto lane : myForward) {
    2092        41415 :         if (lane->isNormal()) {
    2093        22073 :             forwardNormals++;
    2094              :             if (foeForwardEdges.count(&lane->getEdge()) != 0) {
    2095              : #ifdef DEBUG_BUILD_SIDINGS
    2096              :                 if (gDebugFlag4) std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " forwardEdge=" << lane->getEdge().getID() << " on foeForward (sidings unsafe)\n";
    2097              : #endif
    2098              :                 return;
    2099              :             }
    2100              :         }
    2101              :     }
    2102              :     int i;
    2103              :     std::vector<int> start;
    2104              :     std::vector<double> length;
    2105              :     std::vector<std::vector<int> > intermediateRS;
    2106        40478 :     for (i = 0; i < (int)myRoute.size(); i++) {
    2107        40478 :         if (myRoute[i] == foeEndBidi) {
    2108              :             break;
    2109              :         }
    2110              :     }
    2111         6828 :     if (i == (int)myRoute.size()) {
    2112            0 :         throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " foeEndBidi=" + foeEndBidi->getID() + " not on route\n");
    2113              :     }
    2114         6828 :     const MSEdge* next = myRoute[i];
    2115              : #ifdef DEBUG_BUILD_SIDINGS
    2116              :     if (gDebugFlag4) std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " next=" << next->getID() << " foeForwardNormals=" << foeForwardNormals << " frSize=" << foeRoute.size() << " foeSearchBeg=" << (*foeSearchBeg)->getID() << "\n";
    2117              : #endif
    2118         6828 :     i--;
    2119              :     // look backward along our route starting at the final edge of the foe dw
    2120        40478 :     for (; i >= 0; i--) {
    2121        33650 :         const MSEdge* cur = myRoute[i];
    2122        33650 :         const bool curHasRS = hasRS(cur, next);
    2123        33650 :         if (curHasRS) {
    2124        11495 :             if (std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge()) == foeSearchEnd) {
    2125              :                 // we found a rail signal in a safe spot (not on the foe route)
    2126         7923 :                 start.push_back(i);
    2127         7923 :                 length.push_back(0);
    2128         7923 :                 intermediateRS.push_back({});
    2129              :             }
    2130              :         }
    2131        33650 :         if (!start.empty()) {
    2132              :             // move further backwards along the route to find the spot where it merges again with the foe route
    2133        12718 :             auto itFind = std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge());
    2134        12718 :             if (itFind != foeSearchEnd) {
    2135              : #ifdef DEBUG_BUILD_SIDINGS
    2136              :                 if (gDebugFlag4) std::cout << "endSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " curBidi=" << Named::getIDSecure(cur->getBidiEdge()) << " length=" << toString(length) << "\n";
    2137              : #endif
    2138         2144 :                 const int firstIndex = i + 1;
    2139         2144 :                 if (addToFoe) {
    2140          131 :                     auto& foeSidings = foe->mySidings[this];
    2141              :                     // indices must be mapped onto foe route;
    2142          131 :                     const MSEdge* first = myRoute[firstIndex];
    2143          131 :                     auto itFirst = std::find(foeRoute.begin(), foeRoute.end(), first);
    2144          131 :                     if (itFirst != foeRoute.end()) {
    2145          473 :                         for (int j = 0; j < (int)length.size(); j++) {
    2146          342 :                             const MSEdge* last = myRoute[start[j]];
    2147          342 :                             auto itLast = std::find(itFirst, foeRoute.end(), last);
    2148          342 :                             if (itLast != foeRoute.end()) {
    2149              :                                 // @todo are intermediateRS relevant here?
    2150          684 :                                 foeSidings.insert(foeSidings.begin(), Siding((int)(itFirst - foeRoute.begin()), (int)(itLast - foeRoute.begin()), length[j], {}));
    2151          342 :                                 if (foeSidings.size() > 2) {
    2152              :                                     foeSidings.pop_back();  // only need the first 2 sidings
    2153              :                                 }
    2154              :                             }
    2155              :                         }
    2156              :                     }
    2157              :                 } else {
    2158              :                     // pick up further rail signals between the start of the siding and the start of the current driveway
    2159              :                     std::vector<int> furtherRS;
    2160         2013 :                     if (curHasRS) {
    2161          128 :                         furtherRS.push_back(i);
    2162              :                     }
    2163              :                     const MSEdge* next2 = cur;
    2164         5670 :                     for (int i2 = i - 1; i2 >= forwardNormals; i2--) {
    2165         3657 :                         const MSEdge* cur2 = myRoute[i2];
    2166         3657 :                         if (hasRS(cur2, next2)) {
    2167         1711 :                             furtherRS.push_back(i2);
    2168              :                         }
    2169              :                         next2 = cur2;
    2170              :                     }
    2171         2013 :                     auto& foeSidings = mySidings[foe];
    2172         8896 :                     for (int j = 0; j < (int)length.size(); j++) {
    2173         6883 :                         intermediateRS[j].insert(intermediateRS[j].end(), furtherRS.begin(), furtherRS.end());
    2174        13766 :                         foeSidings.insert(foeSidings.begin(), Siding(firstIndex, start[j], length[j], intermediateRS[j]));
    2175         6883 :                         if (foeSidings.size() > 2) {
    2176              :                             foeSidings.pop_back();  // only need the first 2 sidings
    2177              :                         }
    2178              :                     }
    2179         2013 :                 }
    2180              :                 start.clear();
    2181              :                 length.clear();
    2182              :                 intermediateRS.clear();
    2183         2144 :                 foeSearchBeg = itFind;
    2184              :             } else {
    2185        75447 :                 for (int j = 0; j < (int)length.size(); j++) {
    2186        64873 :                     length[j] += cur->getLength();
    2187        64873 :                     if (curHasRS && i != start[j]) {
    2188        45431 :                         intermediateRS[j].push_back(i);
    2189              :                     }
    2190              :                 }
    2191              :             }
    2192              :         }
    2193              :         next = cur;
    2194              :     }
    2195         6828 : }
    2196              : 
    2197              : 
    2198              : bool
    2199        37307 : MSDriveWay::hasRS(const MSEdge* cur, const MSEdge* next) {
    2200        37307 :     if (cur->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    2201              :         // check if there is a controlled link between cur and next
    2202        25288 :         for (auto lane : cur->getLanes()) {
    2203        25699 :             for (const MSLink* link : lane->getLinkCont()) {
    2204        19658 :                 if (&link->getLane()->getEdge() == next && link->getTLLogic() != nullptr) {
    2205              :                     return true;
    2206              :                 }
    2207              :             }
    2208              :         }
    2209              :     }
    2210              :     return false;
    2211              : }
    2212              : 
    2213              : 
    2214              : bool
    2215          372 : MSDriveWay::forwardEndOnRoute(const MSDriveWay* foe) const {
    2216          372 :     const MSEdge* foeForwardEnd = &foe->myForward.back()->getNormalPredecessorLane()->getEdge();
    2217          372 :     return std::find(myRoute.begin(), myRoute.end(), foeForwardEnd) != myRoute.end();
    2218              : }
    2219              : 
    2220              : void
    2221         5158 : MSDriveWay::addConflictLink(const MSLink* link) {
    2222         5158 :     if (link->getTLLogic() != nullptr) {
    2223              :         // ignore links that originate on myBidi
    2224              :         // and also links from the same junction as my own link
    2225         5158 :         const MSLane* origin = link->getLaneBefore();
    2226         5158 :         if (std::find(myBidi.begin(), myBidi.end(), origin) == myBidi.end()) {
    2227         4132 :             if (link->getJunction() != myRoute.front()->getFromJunction()) {
    2228         2632 :                 if (std::find(myConflictLinks.begin(), myConflictLinks.end(), link) == myConflictLinks.end()) {
    2229         1870 :                     myConflictLinks.push_back(const_cast<MSLink*>(link));
    2230              :                 }
    2231              :             }
    2232              :         }
    2233              :     }
    2234         5158 : }
    2235              : 
    2236              : void
    2237          333 : MSDriveWay::addDWDeadlock(const std::vector<const MSDriveWay*>& deadlockFoes) {
    2238              :     std::set<const MSDriveWay*> filtered;
    2239         1641 :     for (const MSDriveWay* foe : deadlockFoes) {
    2240         1308 :         if (std::find(myFoes.begin(), myFoes.end(), foe) == myFoes.end()) {
    2241              :             filtered.insert(foe);
    2242              :         }
    2243              :     }
    2244          333 :     if (std::find(myDeadlocks.begin(), myDeadlocks.end(), filtered) == myDeadlocks.end()) {
    2245           93 :         myDeadlocks.push_back(filtered);
    2246              :         //std::cout << getID() << " deadlockFoes=" << toString(deadlockFoes) << "\n";
    2247              :     }
    2248          333 : }
    2249              : 
    2250              : const MSDriveWay*
    2251        65787 : MSDriveWay::getDepartureDriveway(const SUMOVehicle* veh, bool init) {
    2252        65787 :     const MSEdge* edge = init ? veh->getRoute().getEdges()[veh->getDepartEdge()] : veh->getEdge();
    2253              :     assert(isRailwayOrShared(edge->getPermissions()));
    2254        65787 :     if (edge->getFromJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    2255         4055 :         for (const MSLane* lane : edge->getLanes()) {
    2256         4366 :             for (auto ili : lane->getIncomingLanes()) {
    2257         2568 :                 const MSLink* entry = ili.viaLink->getCorrespondingEntryLink();
    2258         2568 :                 const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(entry->getTLLogic());
    2259         1754 :                 if (rs != nullptr) {
    2260         1754 :                     const MSDriveWay* dw = &const_cast<MSRailSignal*>(rs)->retrieveDriveWayForVeh(entry->getTLIndex(), veh);
    2261         1754 :                     if (&dw->myForward.front()->getEdge() == edge) {
    2262              :                         return dw;
    2263              :                     }
    2264              :                 }
    2265              :             }
    2266              :         }
    2267              :     }
    2268        68663 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    2269        65201 :         auto matchStart = init ? veh->getRoute().begin() + veh->getDepartEdge() : veh->getCurrentRouteEdge();
    2270        65201 :         if (dw->match(matchStart, veh->getRoute().end())) {
    2271        61866 :             return dw;
    2272              :         }
    2273              :     }
    2274         6924 :     const std::string id = edge->getFromJunction()->getID() + ".d" + toString(myDepartDrivewayIndex[edge->getFromJunction()]++);
    2275         3462 :     MSDriveWay* dw = buildDriveWay(id, nullptr, veh->getCurrentRouteEdge(), veh->getRoute().end());
    2276         3462 :     myDepartureDriveways[edge].push_back(dw);
    2277         3462 :     myDepartureDrivewaysEnds[&dw->myForward.back()->getEdge()].push_back(dw);
    2278              :     dw->setVehicle(veh->getID());
    2279              :     return dw;
    2280              : }
    2281              : 
    2282              : 
    2283              : void
    2284         1532 : MSDriveWay::writeDepatureBlocks(OutputDevice& od, bool writeVehicles) {
    2285         3946 :     for (auto item  : myDepartureDriveways) {
    2286         2414 :         const MSEdge* edge = item.first;
    2287         2414 :         if (item.second.size() > 0) {
    2288         4828 :             od.openTag("departJunction");
    2289         2414 :             od.writeAttr(SUMO_ATTR_ID, edge->getFromJunction()->getID());
    2290         5129 :             for (const MSDriveWay* dw : item.second) {
    2291         2715 :                 if (writeVehicles) {
    2292          398 :                     dw->writeBlockVehicles(od);
    2293              :                 } else {
    2294         2317 :                     dw->writeBlocks(od);
    2295              :                 }
    2296              :             }
    2297         4828 :             od.closeTag(); // departJunction
    2298              :         }
    2299              :     }
    2300         1532 : }
    2301              : 
    2302              : void
    2303          496 : MSDriveWay::saveState(OutputDevice& out) {
    2304              :     // all driveways are in myEndingDriveways which makes it convenient
    2305          572 :     for (auto item : myEndingDriveways) {
    2306          167 :         for (MSDriveWay* dw : item.second) {
    2307           91 :             dw->_saveState(out);
    2308          109 :             for (MSDriveWay* sub : dw->mySubDriveWays) {
    2309           18 :                 sub->_saveState(out);
    2310              :             }
    2311              :         }
    2312              :     }
    2313          496 : }
    2314              : 
    2315              : void
    2316          109 : MSDriveWay::_saveState(OutputDevice& out) const {
    2317          109 :     if (!myTrains.empty() || haveSubTrains()) {
    2318           69 :         out.openTag(isSubDriveWay() ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
    2319           36 :         out.writeAttr(SUMO_ATTR_ID, getID());
    2320           36 :         out.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
    2321           36 :         if (!myTrains.empty()) {
    2322              :             std::vector<std::string> trainIDs;
    2323           72 :             for (SUMOVehicle* veh : myTrains) {
    2324           36 :                 trainIDs.push_back(veh->getID());
    2325              :             }
    2326           36 :             out.writeAttr(SUMO_ATTR_VEHICLES, toString(trainIDs));
    2327           36 :         }
    2328           72 :         out.closeTag();
    2329              :     }
    2330          109 : }
    2331              : 
    2332              : 
    2333              : bool
    2334           73 : MSDriveWay::haveSubTrains() const {
    2335           84 :     for (MSDriveWay* sub : mySubDriveWays) {
    2336           11 :         if (!sub->myTrains.empty()) {
    2337              :             return true;
    2338              :         }
    2339              :     }
    2340              :     return false;
    2341              : }
    2342              : 
    2343              : void
    2344           36 : MSDriveWay::loadState(const SUMOSAXAttributes& attrs, int tag) {
    2345           36 :     if ((int)myDriveWayRouteLookup.size() < myGlobalDriveWayIndex) {
    2346           87 :         for (auto item : myEndingDriveways) {
    2347          136 :             for (MSDriveWay* dw : item.second) {
    2348           70 :                 myDriveWayRouteLookup[dw->myRoute] = dw;
    2349              :             }
    2350              :         }
    2351              :     }
    2352           36 :     MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
    2353              :     bool ok;
    2354           36 :     const std::string id  = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    2355           36 :     const std::string edges = attrs.get<std::string>(SUMO_ATTR_EDGES, id.c_str(), ok);
    2356              :     ConstMSEdgeVector route;
    2357           36 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
    2358           36 :         MSEdge::parseEdgesList(edges, route, id);
    2359              :     }
    2360              :     // missing driveways and subdriveways can be ignored. They may have been created
    2361              :     // for vehicles that are not relevant at state loading time
    2362              :     MSDriveWay* dw = nullptr;
    2363           36 :     if (tag == SUMO_TAG_DRIVEWAY) {
    2364              :         auto it = myDriveWayRouteLookup.find(route);
    2365           33 :         if (it == myDriveWayRouteLookup.end()) {
    2366              :             //WRITE_WARNING(TLF("Unknown driveWay '%' with route '%'", id, edges));
    2367              :             return;
    2368              :         }
    2369           32 :         dw = it->second;
    2370           32 :         myDriveWayLookup[id] = dw;
    2371              :     } else {
    2372            3 :         std::string parentID = id.substr(0, id.rfind('.'));
    2373              :         auto it = myDriveWayLookup.find(parentID);
    2374            3 :         if (it == myDriveWayLookup.end()) {
    2375              :             //WRITE_WARNING(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
    2376              :             return;
    2377              :         }
    2378            3 :         MSDriveWay* parent = it->second;
    2379            3 :         for (MSDriveWay* sub : parent->mySubDriveWays) {
    2380            1 :             if (sub->myRoute == route) {
    2381              :                 dw = sub;
    2382              :                 break;
    2383              :             }
    2384              :         }
    2385            3 :         if (dw == nullptr) {
    2386              :             // missing subdriveways can be ignored. They may have been created
    2387              :             // as foes for driveways that are not relevant at state loading time
    2388              :             return;
    2389              :         }
    2390              :     }
    2391           66 :     const std::string vehicles = attrs.getOpt<std::string>(SUMO_ATTR_VEHICLES, id.c_str(), ok, "");
    2392           99 :     for (const std::string& vehID : StringTokenizer(vehicles).getVector()) {
    2393           33 :         MSBaseVehicle* veh = dynamic_cast<MSBaseVehicle*>(c.getVehicle(vehID));
    2394           33 :         if (veh == nullptr) {
    2395            0 :             throw ProcessError(TLF("Unknown vehicle '%' in driveway '%'", vehID, id));
    2396              :         }
    2397           33 :         if (!dw->hasTrain(veh)) {
    2398            4 :             dw->myTrains.insert(veh);
    2399            4 :             veh->addReminder(dw);
    2400              :         }
    2401           33 :     }
    2402           36 : }
    2403              : 
    2404              : const MSDriveWay*
    2405            0 : MSDriveWay::retrieveDepartDriveWay(const MSEdge* edge, const std::string& id) {
    2406            0 :     for (MSDriveWay* dw : myDepartureDriveways[edge]) {
    2407            0 :         if (dw->getID() == id) {
    2408              :             return dw;
    2409              :         }
    2410              :     }
    2411              :     return nullptr;
    2412              : }
    2413              : 
    2414              : 
    2415              : bool
    2416         1555 : MSDriveWay::hasTrain(SUMOVehicle* veh) const {
    2417         1555 :     return myTrains.count(veh) != 0;
    2418              : }
    2419              : 
    2420              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1