LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSRailSignal.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 548 615 89.1 %
Date: 2024-05-19 15:37:39 Functions: 50 63 79.4 %

          Line data    Source code
       1             : /****************************************************************************/
       2             : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3             : // Copyright (C) 2001-2024 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    MSRailSignal.cpp
      15             : /// @author  Melanie Weber
      16             : /// @author  Andreas Kendziorra
      17             : /// @author  Jakob Erdmann
      18             : /// @date    Jan 2015
      19             : ///
      20             : // A rail signal logic
      21             : /****************************************************************************/
      22             : #include <config.h>
      23             : 
      24             : #include <cassert>
      25             : #include <utility>
      26             : #include <vector>
      27             : #include <bitset>
      28             : #ifdef HAVE_FOX
      29             : #include <utils/foxtools/MFXWorkerThread.h>
      30             : #endif
      31             : #include <utils/iodevices/OutputDevice_COUT.h>
      32             : #include <microsim/MSEventControl.h>
      33             : #include <microsim/MSNet.h>
      34             : #include <microsim/MSEdge.h>
      35             : #include <microsim/MSEdgeControl.h>
      36             : #include <microsim/MSLane.h>
      37             : #include <microsim/MSLink.h>
      38             : #include <microsim/MSJunctionLogic.h>
      39             : #include <microsim/MSVehicle.h>
      40             : #include <microsim/devices/MSDevice_Routing.h>
      41             : #include <microsim/devices/MSRoutingEngine.h>
      42             : #include <microsim/MSLane.h>
      43             : 
      44             : #include "MSTLLogicControl.h"
      45             : #include "MSTrafficLightLogic.h"
      46             : #include "MSPhaseDefinition.h"
      47             : #include "MSTLLogicControl.h"
      48             : #include "MSRailSignalConstraint.h"
      49             : #include "MSRailSignalControl.h"
      50             : #include "MSRailSignal.h"
      51             : 
      52             : // typical block length in germany on main lines is 3-5km on branch lines up to 7km
      53             : // special branches that are used by one train exclusively could also be up to 20km in length
      54             : // minimum block size in germany is 37.5m (LZB)
      55             : // larger countries (USA, Russia) might see blocks beyond 20km)
      56             : #define MAX_BLOCK_LENGTH 20000
      57             : #define MAX_SIGNAL_WARNINGS 10
      58             : 
      59             : //#define DEBUG_SELECT_DRIVEWAY
      60             : //#define DEBUG_BUILD_DRIVEWAY
      61             : //#define DEBUG_DRIVEWAY_UPDATE
      62             : //#define DEBUG_DRIVEWAY_BUILDROUTE
      63             : //#define DEBUG_CHECK_FLANKS
      64             : 
      65             : //#define DEBUG_SIGNALSTATE
      66             : //#define DEBUG_SIGNALSTATE_PRIORITY
      67             : //#define DEBUG_FIND_PROTECTION
      68             : //#define DEBUG_REROUTE
      69             : 
      70             : #define DEBUG_COND DEBUG_HELPER(this)
      71             : #define DEBUG_COND_LINKINFO DEBUG_HELPER(myLink->getTLLogic())
      72             : #define DEBUG_HELPER(obj) ((obj)->isSelected())
      73             : //#define DEBUG_HELPER(obj) ((obj)->getID() == "")
      74             : //#define DEBUG_HELPER(obj) (true)
      75             : 
      76             : // ===========================================================================
      77             : // static value definitions
      78             : // ===========================================================================
      79             : int MSRailSignal::myNumWarnings(0);
      80             : 
      81             : int MSRailSignal::myDriveWayIndex(0);
      82             : 
      83             : bool MSRailSignal::myStoreVehicles(false);
      84             : MSRailSignal::VehicleVector MSRailSignal::myBlockingVehicles;
      85             : MSRailSignal::VehicleVector MSRailSignal::myRivalVehicles;
      86             : MSRailSignal::VehicleVector MSRailSignal::myPriorityVehicles;
      87             : std::string MSRailSignal::myConstraintInfo;
      88             : 
      89             : // ===========================================================================
      90             : // method definitions
      91             : // ===========================================================================
      92        2725 : MSRailSignal::MSRailSignal(MSTLLogicControl& tlcontrol,
      93             :                            const std::string& id, const std::string& programID, SUMOTime delay,
      94        2725 :                            const Parameterised::Map& parameters) :
      95             :     MSTrafficLightLogic(tlcontrol, id, programID, 0, TrafficLightType::RAIL_SIGNAL, delay, parameters),
      96        8175 :     myCurrentPhase(DELTA_T, std::string(SUMO_MAX_CONNECTIONS, 'X')), // dummy phase
      97        5450 :     myPhaseIndex(0) {
      98        2725 :     myDefaultCycleTime = DELTA_T;
      99        2725 :     myMovingBlock = OptionsCont::getOptions().getBool("railsignal-moving-block");
     100        2725 :     MSRailSignalControl::getInstance().addSignal(this);
     101        2725 : }
     102             : 
     103             : void
     104        2725 : MSRailSignal::init(NLDetectorBuilder&) {
     105        2725 :     if (myLanes.size() == 0) {
     106           9 :         WRITE_WARNINGF(TL("Rail signal at junction '%' does not control any links"), getID());
     107             :     }
     108        6647 :     for (LinkVector& links : myLinks) { //for every link index
     109        3922 :         if (links.size() != 1) {
     110           0 :             throw ProcessError("At railSignal '" + getID() + "' found " + toString(links.size())
     111           0 :                                + " links controlled by index " + toString(links[0]->getTLIndex()));
     112             :         }
     113        7844 :         myLinkInfos.push_back(LinkInfo(links[0]));
     114             :     }
     115        2725 :     updateCurrentPhase();
     116        2725 :     setTrafficLightSignals(MSNet::getInstance()->getCurrentTimeStep());
     117        2725 :     myNumLinks = (int)myLinks.size();
     118        2725 : }
     119             : 
     120             : 
     121        5450 : MSRailSignal::~MSRailSignal() {
     122        2725 :     removeConstraints();
     123        8175 : }
     124             : 
     125             : 
     126             : // ----------- Handling of controlled links
     127             : void
     128           0 : MSRailSignal::adaptLinkInformationFrom(const MSTrafficLightLogic& logic) {
     129           0 :     MSTrafficLightLogic::adaptLinkInformationFrom(logic);
     130           0 :     updateCurrentPhase();
     131           0 : }
     132             : 
     133             : 
     134             : // ------------ Switching and setting current rows
     135             : SUMOTime
     136    27557889 : MSRailSignal::trySwitch() {
     137    27557889 :     updateCurrentPhase();
     138    27557889 :     return DELTA_T;
     139             : }
     140             : 
     141             : 
     142             : 
     143             : void
     144    27560619 : MSRailSignal::updateCurrentPhase() {
     145             : #ifdef DEBUG_SIGNALSTATE
     146             :     gDebugFlag4 = DEBUG_COND;
     147             : #endif
     148             :     // green by default so vehicles can be inserted at the borders of the network
     149             :     std::string state(myLinks.size(), 'G');
     150    81132771 :     for (LinkInfo& li : myLinkInfos) {
     151    53572152 :         if (li.myLink->getApproaching().size() > 0) {
     152     6839968 :             Approaching closest = getClosest(li.myLink);
     153     6839968 :             DriveWay& driveway = li.getDriveWay(closest.first);
     154             :             //std::cout << SIMTIME << " signal=" << getTLLinkID(li.myLink) << " veh=" << closest.first->getID() << " dw:\n";
     155             :             //driveway.writeBlocks(*OutputDevice_COUT::getDevice());
     156     6839968 :             const bool mustWait = !constraintsAllow(closest.first);
     157             :             MSEdgeVector occupied;
     158     6839968 :             if (mustWait || !driveway.reserve(closest, occupied)) {
     159     6564527 :                 state[li.myLink->getTLIndex()] = 'r';
     160     6564527 :                 if (occupied.size() > 0) {
     161       15240 :                     li.reroute(const_cast<SUMOVehicle*>(closest.first), occupied);
     162             :                 }
     163             : #ifdef DEBUG_SIGNALSTATE
     164             :                 if (gDebugFlag4) {
     165             :                     std::cout << SIMTIME << " rsl=" << li.getID() << " veh=" << closest.first->getID() << " notReserved\n";
     166             :                 }
     167             : #endif
     168             :             } else {
     169      275441 :                 state[li.myLink->getTLIndex()] = 'G';
     170      275441 :                 if (driveway.myFlank.size() > 0 && myCurrentPhase.getState()[li.myLink->getTLIndex()] != 'G') {
     171             :                     // schedule recheck
     172        2806 :                     MSRailSignalControl::getInstance().addGreenFlankSwitch(li.myLink, driveway.myNumericalID);
     173             :                 }
     174             : #ifdef DEBUG_SIGNALSTATE
     175             :                 if (gDebugFlag4) {
     176             :                     std::cout << SIMTIME << " rsl=" << li.getID() << " veh=" << closest.first->getID() << " reserved\n";
     177             :                 }
     178             : #endif
     179             :             }
     180             :         } else {
     181    46732184 :             if (li.myDriveways.empty()) {
     182             : #ifdef DEBUG_SIGNALSTATE
     183             :                 if (gDebugFlag4) {
     184             :                     std::cout << SIMTIME << " rsl=" << li.getID() << " red for unitialized signal (no driveways yet)\n";
     185             :                 }
     186             : #endif
     187    26168683 :                 state[li.myLink->getTLIndex()] = 'r';
     188             :             } else {
     189             :                 DriveWay& driveway = li.myDriveways.front();
     190    41127002 :                 if (driveway.conflictLaneOccupied() || driveway.conflictLinkApproached()) {
     191             : #ifdef DEBUG_SIGNALSTATE
     192             :                     if (gDebugFlag4) {
     193             :                         std::cout << SIMTIME << " rsl=" << li.getID() << " red for default driveway (" << toString(driveway.myRoute) << ")\n";
     194             :                     }
     195             : #endif
     196     6707440 :                     state[li.myLink->getTLIndex()] = 'r';
     197             :                 } else {
     198             : #ifdef DEBUG_SIGNALSTATE
     199             :                     if (gDebugFlag4) {
     200             :                         std::cout << SIMTIME << " rsl=" << li.getID() << " green for default driveway (" << toString(driveway.myRoute) << ")\n";
     201             :                     }
     202             : #endif
     203             :                 }
     204             :             }
     205             :         }
     206             :     }
     207    27560619 :     if (myCurrentPhase.getState() != state) {
     208             :         myCurrentPhase.setState(state);
     209       18122 :         myPhaseIndex = 1 - myPhaseIndex;
     210             :     }
     211             : #ifdef DEBUG_SIGNALSTATE
     212             :     gDebugFlag4 = false;
     213             : #endif
     214    27560619 : }
     215             : 
     216             : 
     217             : bool
     218     6859538 : MSRailSignal::constraintsAllow(const SUMOVehicle* veh) const {
     219     6859538 :     if (myConstraints.size() == 0) {
     220             :         return true;
     221             :     } else {
     222    12977682 :         const std::string tripID = veh->getParameter().getParameter("tripId", veh->getID());
     223             :         auto it = myConstraints.find(tripID);
     224     6488841 :         if (it != myConstraints.end()) {
     225     6507886 :             for (MSRailSignalConstraint* c : it->second) {
     226             :                 // ignore insertion constraints here
     227     6483512 :                 if (!c->isInsertionConstraint() && !c->cleared()) {
     228             : #ifdef DEBUG_SIGNALSTATE
     229             :                     if (gDebugFlag4) {
     230             :                         std::cout << "  constraint '" << c->getDescription() << "' not cleared\n";
     231             :                     }
     232             : #endif
     233     6459471 :                     if (myStoreVehicles) {
     234           0 :                         myConstraintInfo = c->getDescription();
     235             :                     }
     236             :                     return false;
     237             :                 }
     238             :             }
     239             :         }
     240       29370 :         return true;
     241             :     }
     242             : }
     243             : 
     244             : 
     245             : void
     246         945 : MSRailSignal::addConstraint(const std::string& tripId, MSRailSignalConstraint* constraint) {
     247         945 :     myConstraints[tripId].push_back(constraint);
     248         945 : }
     249             : 
     250             : 
     251             : bool
     252         257 : MSRailSignal::removeConstraint(const std::string& tripId, MSRailSignalConstraint* constraint) {
     253             :     if (myConstraints.count(tripId) != 0) {
     254         257 :         auto& constraints = myConstraints[tripId];
     255         257 :         auto it = std::find(constraints.begin(), constraints.end(), constraint);
     256         257 :         if (it != constraints.end()) {
     257         257 :             delete *it;
     258             :             constraints.erase(it);
     259             :             return true;
     260             :         }
     261             :     }
     262             :     return false;
     263             : }
     264             : 
     265             : void
     266        2755 : MSRailSignal::removeConstraints() {
     267        3532 :     for (auto item : myConstraints) {
     268        1465 :         for (MSRailSignalConstraint* c : item.second) {
     269         688 :             delete c;
     270             :         }
     271         777 :     }
     272             :     myConstraints.clear();
     273        2755 : }
     274             : 
     275             : 
     276             : // ------------ Static Information Retrieval
     277             : int
     278           0 : MSRailSignal::getPhaseNumber() const {
     279           0 :     return 0;
     280             : }
     281             : 
     282             : const MSTrafficLightLogic::Phases&
     283        2725 : MSRailSignal::getPhases() const {
     284        2725 :     return myPhases;
     285             : }
     286             : 
     287             : const MSPhaseDefinition&
     288           0 : MSRailSignal::getPhase(int) const {
     289           0 :     return myCurrentPhase;
     290             : }
     291             : 
     292             : // ------------ Dynamic Information Retrieval
     293             : int
     294    55141612 : MSRailSignal::getCurrentPhaseIndex() const {
     295    55141612 :     return myPhaseIndex;
     296             : }
     297             : 
     298             : const MSPhaseDefinition&
     299       86656 : MSRailSignal::getCurrentPhaseDef() const {
     300       86656 :     return myCurrentPhase;
     301             : }
     302             : 
     303             : // ------------ Conversion between time and phase
     304             : SUMOTime
     305           0 : MSRailSignal::getPhaseIndexAtTime(SUMOTime) const {
     306           0 :     return 0;
     307             : }
     308             : 
     309             : SUMOTime
     310           0 : MSRailSignal::getOffsetFromIndex(int) const {
     311           0 :     return 0;
     312             : }
     313             : 
     314             : int
     315           0 : MSRailSignal::getIndexFromOffset(SUMOTime) const {
     316           0 :     return 0;
     317             : }
     318             : 
     319             : 
     320             : void
     321        3922 : MSRailSignal::addLink(MSLink* link, MSLane* lane, int pos) {
     322        3922 :     if (pos >= 0) {
     323        3922 :         MSTrafficLightLogic::addLink(link, lane, pos);
     324             :     } // ignore uncontrolled link
     325        3922 : }
     326             : 
     327             : 
     328             : std::string
     329         908 : MSRailSignal::getTLLinkID(MSLink* link) {
     330        1816 :     return link->getTLLogic()->getID() + "_" + toString(link->getTLIndex());
     331             : }
     332             : 
     333             : std::string
     334         421 : MSRailSignal::getJunctionLinkID(MSLink* link) {
     335         842 :     return link->getJunction()->getID() + "_" + toString(link->getIndex());
     336             : }
     337             : 
     338             : std::string
     339           7 : MSRailSignal::getClickableTLLinkID(MSLink* link) {
     340          14 :     return "junction '" +  link->getTLLogic()->getID() + "', link " + toString(link->getTLIndex());
     341             : }
     342             : 
     343             : std::string
     344           0 : MSRailSignal::describeLinks(std::vector<MSLink*> links) {
     345             :     std::string result;
     346           0 :     for (MSLink* link : links) {
     347           0 :         result += link->getDescription() + " ";
     348             :     }
     349           0 :     return result;
     350             : }
     351             : 
     352             : std::string
     353           0 : MSRailSignal::formatVisitedMap(const LaneVisitedMap& visited) {
     354           0 :     std::vector<const MSLane*> lanes(visited.size(), nullptr);
     355           0 :     for (auto item : visited) {
     356           0 :         lanes[item.second] = item.first;
     357             :     }
     358           0 :     return toString(lanes);
     359             : }
     360             : 
     361             : 
     362             : void
     363       49315 : MSRailSignal::appendMapIndex(LaneVisitedMap& map, const MSLane* lane) {
     364             :     // avoid undefined behavior from evaluation order
     365       49315 :     const int tmp = (int)map.size();
     366       49315 :     map[lane] = tmp;
     367       49315 : }
     368             : 
     369             : 
     370             : MSRailSignal::Approaching
     371     6878360 : MSRailSignal::getClosest(MSLink* link) {
     372             :     assert(link->getApproaching().size() > 0);
     373             :     double minDist = std::numeric_limits<double>::max();
     374             :     auto closestIt = link->getApproaching().begin();
     375    13767653 :     for (auto apprIt = link->getApproaching().begin(); apprIt != link->getApproaching().end(); apprIt++) {
     376     6889293 :         if (apprIt->second.dist < minDist) {
     377             :             minDist = apprIt->second.dist;
     378             :             closestIt = apprIt;
     379             :         }
     380             :     }
     381             :     // maybe a parallel link has a closer vehicle
     382             :     /*
     383             :     for (MSLink* link2 : link->getLaneBefore()->getLinkCont()) {
     384             :         if (link2 != link) {
     385             :             for (auto apprIt2 = link2->getApproaching().begin(); apprIt2 != link2->getApproaching().end(); apprIt2++) {
     386             :                 if (apprIt2->second.dist < minDist) {
     387             :                     minDist = apprIt2->second.dist;
     388             :                     closestIt = apprIt2;
     389             :                 }
     390             :             }
     391             :         }
     392             :     }
     393             :     */
     394     6878360 :     return *closestIt;
     395             : }
     396             : 
     397             : 
     398             : void
     399        1469 : MSRailSignal::writeBlocks(OutputDevice& od) const {
     400        2938 :     od.openTag("railSignal");
     401             :     od.writeAttr(SUMO_ATTR_ID, getID());
     402        3231 :     for (const LinkInfo& li : myLinkInfos) {
     403        1762 :         MSLink* link = li.myLink;
     404        3524 :         od.openTag("link");
     405        3524 :         od.writeAttr(SUMO_ATTR_TLLINKINDEX, link->getTLIndex());
     406             :         od.writeAttr(SUMO_ATTR_FROM, link->getLaneBefore()->getID());
     407             :         od.writeAttr(SUMO_ATTR_TO, link->getViaLaneOrLane()->getID());
     408        3224 :         for (const DriveWay& dw : li.myDriveways) {
     409        1462 :             dw.writeBlocks(od);
     410             :         }
     411        3524 :         od.closeTag(); // link
     412             :     }
     413        1469 :     od.closeTag(); // railSignal
     414        1469 : }
     415             : 
     416             : 
     417             : void
     418        3326 : MSRailSignal::initDriveWays(const SUMOVehicle* ego, bool update) {
     419        3326 :     const ConstMSEdgeVector& edges = ego->getRoute().getEdges();
     420        3326 :     int endIndex = ego->getParameter().arrivalEdge;
     421        3326 :     if (endIndex < 0) {
     422        3326 :         endIndex = (int)edges.size() - 1;
     423             :     }
     424       22855 :     for (int i = ego->getParameter().departEdge; i <= endIndex - 1; i++) {
     425       19529 :         const MSEdge* e = edges[i];
     426       19529 :         if (e->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
     427       10927 :             const MSEdge* e2 = edges[i + 1];
     428       21854 :             for (MSLane* lane : e->getLanes()) {
     429       22362 :                 for (MSLink* link : lane->getLinkCont()) {
     430       11435 :                     if (&link->getLane()->getEdge() == e2) {
     431        8569 :                         MSRailSignal* rs = const_cast<MSRailSignal*>(dynamic_cast<const MSRailSignal*>(link->getTLLogic()));
     432        8569 :                         if (rs != nullptr) {
     433        8569 :                             LinkInfo& li = rs->myLinkInfos[link->getTLIndex()];
     434        8569 :                             if (li.myDriveways.empty()) {
     435             :                                 // init driveway
     436        2196 :                                 li.getDriveWay(ego);
     437        2196 :                                 if (update && rs->isActive()) {
     438             :                                     // vehicle may have rerouted its intial trip
     439             :                                     // after the states have been set
     440             :                                     // @note: This is a hack because it could lead to invalid tls-output
     441             :                                     // (it's still an improvement over switching based on default driveways)
     442        1550 :                                     rs->trySwitch();
     443        1550 :                                     rs->setTrafficLightSignals(SIMSTEP);
     444             :                                 }
     445             :                             }
     446             :                         }
     447             :                     }
     448             :                 }
     449             :             }
     450             :         }
     451             :     }
     452        3326 : }
     453             : 
     454             : 
     455             : bool
     456        2976 : MSRailSignal::hasOncomingRailTraffic(MSLink* link, const MSVehicle* ego, bool& brakeBeforeSignal) {
     457             :     // @note: this check is intended to prevent deadlock / collision by an inserted vehicle that
     458             :     // waits at a red signal and thus checks different things than ::reverse()
     459             :     bool hadOncoming = false;
     460        2976 :     if (link->getJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
     461        2976 :         const MSEdge* bidi = link->getLaneBefore()->getEdge().getBidiEdge();
     462        2976 :         if (bidi == nullptr) {
     463        1530 :             brakeBeforeSignal = false;
     464        2496 :             return false;
     465             :         }
     466        1446 :         const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(link->getTLLogic());
     467        1360 :         if (rs != nullptr) {
     468        1360 :             const LinkInfo& li = rs->myLinkInfos[link->getTLIndex()];
     469        1756 :             for (const DriveWay& dw : li.myDriveways) {
     470             :                 //std::cout << SIMTIME << " hasOncomingRailTraffic link=" << getTLLinkID(link) << " dwRoute=" << toString(dw.myRoute) << " bidi=" << toString(dw.myBidi) << "\n";
     471        9233 :                 for (const MSLane* lane : dw.myBidi) {
     472             :                     if (!lane->isEmpty()) {
     473         641 :                         MSVehicle* veh = lane->getFirstAnyVehicle();
     474         641 :                         if (std::find(veh->getCurrentRouteEdge(), veh->getRoute().end(), bidi) != veh->getRoute().end()) {
     475             : #ifdef DEBUG_SIGNALSTATE
     476             :                             if (DEBUG_HELPER(rs)) {
     477             :                                 std::cout << " oncoming vehicle on bidi-lane " << lane->getID() << "\n";
     478             :                             }
     479             : #endif
     480             :                             return true;
     481             :                         }
     482             :                     }
     483             :                 }
     484        4510 :                 for (const MSLane* lane : dw.myFlank) {
     485             :                     if (!lane->isEmpty()) {
     486         126 :                         MSVehicle* veh = lane->getFirstAnyVehicle();
     487         126 :                         if (std::find(veh->getCurrentRouteEdge(), veh->getRoute().end(), bidi) != veh->getRoute().end()) {
     488             : #ifdef DEBUG_SIGNALSTATE
     489             :                             if (DEBUG_HELPER(rs)) {
     490             :                                 std::cout << " oncoming vehicle on flank-lane " << lane->getID() << "\n";
     491             :                             }
     492             : #endif
     493             :                             return true;
     494             :                         }
     495             :                     }
     496             :                 }
     497         597 :                 if (dw.myProtectingSwitchesBidi.size() > 0) {
     498             : #ifdef DEBUG_SIGNALSTATE
     499             :                     gDebugFlag4 = DEBUG_HELPER(rs);
     500             : #endif
     501             :                     // yield to all foeLinks beyond switch
     502             :                     Approaching approaching(ego,
     503         351 :                                             MSLink::ApproachingVehicleInformation(SIMSTEP, 0, 0, 0, false, 0, 0, std::numeric_limits<double>::max(), 0, 0));
     504         561 :                     for (MSLink* const switchLink : dw.myProtectingSwitchesBidi) {
     505             :                         myBlockingVehicles.clear();
     506             :                         myRivalVehicles.clear();
     507             :                         myPriorityVehicles.clear();
     508             :                         myConstraintInfo = "";
     509         387 :                         myStoreVehicles = true;
     510         387 :                         const bool hasProtection = dw.findProtection(approaching, switchLink);
     511         387 :                         myStoreVehicles = false;
     512         387 :                         if (!hasProtection) {
     513         184 :                             for (const SUMOVehicle* veh : myBlockingVehicles) {
     514             :                                 hadOncoming = true;
     515         177 :                                 if (!brakeBeforeSignal || std::find(veh->getCurrentRouteEdge(), veh->getRoute().end(), bidi) != veh->getRoute().end()) {
     516             : #ifdef DEBUG_SIGNALSTATE
     517             :                                     if (DEBUG_HELPER(rs)) {
     518             :                                         std::cout << "  no protection at bidi-switch " << switchLink->getDescription() << " from veh=" << veh->getID() << "\n";
     519             :                                         gDebugFlag4 = false;
     520             :                                     }
     521             : #endif
     522             :                                     return true;
     523             :                                 }
     524             :                             }
     525          14 :                             for (const SUMOVehicle* veh : myRivalVehicles) {
     526             :                                 hadOncoming = true;
     527           7 :                                 if (!brakeBeforeSignal || std::find(veh->getCurrentRouteEdge(), veh->getRoute().end(), bidi) != veh->getRoute().end()) {
     528             : #ifdef DEBUG_SIGNALSTATE
     529             :                                     if (DEBUG_HELPER(rs)) {
     530             :                                         std::cout << "  no protection at bidi-switch " << switchLink->getDescription() << " from linkRival veh=" << veh->getID() << "\n";
     531             :                                         gDebugFlag4 = false;
     532             :                                     }
     533             : #endif
     534             :                                     return true;
     535             :                                 }
     536             :                             }
     537             :                         }
     538             :                     }
     539             : #ifdef DEBUG_SIGNALSTATE
     540             :                     gDebugFlag4 = false;
     541             : #endif
     542             :                 }
     543         755 :                 for (MSLink* foeLink : dw.myConflictLinks) {
     544         359 :                     if (foeLink->getApproaching().size() != 0) {
     545          95 :                         Approaching closest = getClosest(foeLink);
     546          95 :                         const SUMOVehicle* veh = closest.first;
     547         128 :                         if (veh->getSpeed() > 0 && closest.second.arrivalSpeedBraking > 0
     548         128 :                                 && std::find(veh->getCurrentRouteEdge(), veh->getRoute().end(), bidi) != veh->getRoute().end()) {
     549             : #ifdef DEBUG_SIGNALSTATE
     550             :                             if (DEBUG_HELPER(rs)) {
     551             :                                 std::cout << " oncoming vehicle approaching foe link " << foeLink->getDescription() << "\n";
     552             :                             }
     553             : #endif
     554          24 :                             return true;
     555             :                         }
     556             :                     }
     557             :                 }
     558             :             }
     559             :         }
     560             :     }
     561         480 :     brakeBeforeSignal = hadOncoming;
     562         480 :     return false;
     563             : }
     564             : 
     565             : bool
     566       17265 : MSRailSignal::hasInsertionConstraint(MSLink* link, const MSVehicle* veh, std::string& info, bool& isInsertionOrder) {
     567       17265 :     if (link->getJunction() != nullptr && link->getJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
     568        5632 :         const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(link->getTLLogic());
     569        5040 :         if (rs != nullptr && rs->myConstraints.size() > 0) {
     570        5474 :             const std::string tripID = veh->getParameter().getParameter("tripId", veh->getID());
     571             :             auto it = rs->myConstraints.find(tripID);
     572        2737 :             if (it != rs->myConstraints.end()) {
     573        2993 :                 for (MSRailSignalConstraint* c : it->second) {
     574        2082 :                     if (c->isInsertionConstraint() && !c->cleared()) {
     575             : #ifdef DEBUG_SIGNALSTATE
     576             :                         if (DEBUG_HELPER(rs)) {
     577             :                             std::cout << SIMTIME << " rsl=" << rs->getID() << " insertion constraint '" << c->getDescription() << "' for vehicle '" << veh->getID() << "' not cleared\n";
     578             :                         }
     579             : #endif
     580        3604 :                         info = c->getDescription();
     581        1802 :                         isInsertionOrder = c->getType() == MSRailSignalConstraint::ConstraintType::INSERTION_ORDER;
     582             :                         return true;
     583             :                     }
     584             :                 }
     585             :             }
     586             :         }
     587             :     }
     588             :     return false;
     589             : }
     590             : 
     591             : // ===========================================================================
     592             : // LinkInfo method definitions
     593             : // ===========================================================================
     594             : 
     595        3922 : MSRailSignal::LinkInfo::LinkInfo(MSLink* link):
     596        3922 :     myLink(link) {
     597        3922 :     reset();
     598        3922 : }
     599             : 
     600             : 
     601             : void
     602        3927 : MSRailSignal::LinkInfo::reset() {
     603        3927 :     myLastRerouteTime = -1;
     604        3927 :     myLastRerouteVehicle = nullptr;
     605             :     myDriveways.clear();
     606        3927 : }
     607             : 
     608             : 
     609             : std::string
     610         180 : MSRailSignal::LinkInfo::getID() const {
     611         360 :     return myLink->getTLLogic()->getID() + "_" + toString(myLink->getTLIndex());
     612             : }
     613             : 
     614             : 
     615             : MSRailSignal::DriveWay&
     616     6864917 : MSRailSignal::LinkInfo::getDriveWay(const SUMOVehicle* veh) {
     617     6864917 :     MSEdge* first = &myLink->getLane()->getEdge();
     618     6864917 :     MSRouteIterator firstIt = std::find(veh->getCurrentRouteEdge(), veh->getRoute().end(), first);
     619     6864917 :     if (firstIt == veh->getRoute().end()) {
     620             :         // possibly the vehicle has already gone past the first edge (i.e.
     621             :         // because first is short or the step-length is high)
     622             :         // lets look backward along the route
     623             :         // give some slack because the vehicle might have been braking from a higher speed and using ballistic integration
     624          60 :         double lookBack = SPEED2DIST(veh->getSpeed() + 10);
     625          60 :         int routeIndex = veh->getRoutePosition() - 1;
     626          66 :         while (lookBack > 0 && routeIndex > 0) {
     627          59 :             const MSEdge* prevEdge = veh->getRoute().getEdges()[routeIndex];
     628          59 :             if (prevEdge == first) {
     629          53 :                 firstIt = veh->getRoute().begin() + routeIndex;
     630          53 :                 break;
     631             :             }
     632           6 :             lookBack -= prevEdge->getLength();
     633           6 :             routeIndex--;
     634             :         }
     635             :     }
     636     6864917 :     if (firstIt == veh->getRoute().end()) {
     637          14 :         WRITE_WARNING("Invalid approach information to rail signal '" + getClickableTLLinkID(myLink) + "' after rerouting for vehicle '" + veh->getID()
     638             :                       + "' first driveway edge '" + first->getID() + "' time=" + time2string(MSNet::getInstance()->getCurrentTimeStep()) + ".");
     639           7 :         if (myDriveways.empty()) {
     640             :             ConstMSEdgeVector dummyRoute;
     641           0 :             dummyRoute.push_back(&myLink->getLane()->getEdge());
     642           0 :             DriveWay dw = buildDriveWay(dummyRoute.begin(), dummyRoute.end());
     643           0 :             myDriveways.push_back(dw);
     644           0 :         }
     645           7 :         return myDriveways.front();
     646             :     }
     647             :     //std::cout << SIMTIME << " veh=" << veh->getID() << " rsl=" << getID() << " dws=" << myDriveways.size() << "\n";
     648     6890481 :     for (DriveWay& dw : myDriveways) {
     649             :         // @todo optimize: it is sufficient to check for specific edges (after each switch)
     650             :         auto itRoute = firstIt;
     651             :         auto itDwRoute = dw.myRoute.begin();
     652             :         bool match = true;
     653    41882378 :         while (itRoute != veh->getRoute().end() && itDwRoute != dw.myRoute.end()) {
     654    35016707 :             if (*itRoute != *itDwRoute) {
     655             :                 match = false;
     656             :                 //std::cout << "  check dw=" << " match failed at vehEdge=" << (*itRoute)->getID() << " dwEdge=" << (*itDwRoute)->getID() << "\n";
     657             :                 break;
     658             :             }
     659             :             itRoute++;
     660             :             itDwRoute++;
     661             :         }
     662             :         // if the vehicle arrives before the end of this driveway,
     663             :         // we'd rather build a new driveway to avoid superfluous restrictions
     664     6865671 :         if (match && itDwRoute == dw.myRoute.end()
     665    13753713 :                 && (itRoute == veh->getRoute().end() || dw.myFoundSignal || dw.myFoundReversal)) {
     666             :             //std::cout << "  using dw=" << "\n";
     667             :             return dw;
     668             :         }
     669             : #ifdef DEBUG_SELECT_DRIVEWAY
     670             :         std::cout << SIMTIME << " rs=" << getID() << " veh=" << veh->getID() << " other dwSignal=" << dw.myFoundSignal << " dwRoute=" << toString(dw.myRoute) << " route=" << toString(veh->getRoute().getEdges()) << "\n";
     671             : #endif
     672             :     }
     673        2439 :     DriveWay dw = buildDriveWay(firstIt, veh->getRoute().end());
     674             : #ifdef DEBUG_SELECT_DRIVEWAY
     675             :     std::cout << SIMTIME << " rs=" << getID() << " veh=" << veh->getID() << " new dwSignal=" << dw.myFoundSignal << " dwRoute=" << toString(dw.myRoute) << " route=" << toString(veh->getRoute().getEdges()) << "\n";
     676             : #endif
     677        2439 :     myDriveways.push_back(dw);
     678             :     return myDriveways.back();
     679        2439 : }
     680             : 
     681             : 
     682             : MSRailSignal::DriveWay
     683        2498 : MSRailSignal::LinkInfo::buildDriveWay(MSRouteIterator first, MSRouteIterator end) {
     684             :     // collect lanes and links that are relevant for setting this signal for the current driveWay
     685             :     // For each driveway we collect
     686             :     //   - conflictLanes (signal must be red if any conflict lane is occupied)
     687             :     //   - conflictLinks (signal must be red if any conflict link is approached by a vehicle
     688             :     //      - that cannot break in time (arrivalSpeedBraking > 0)
     689             :     //      - approached by a vehicle with higher switching priority (see #3941)
     690             :     // These objects are construct in steps:
     691             :     //
     692             :     // forwardBlock
     693             :     // - search forward recursive from outgoing lane until controlled railSignal link found
     694             :     //   -> add all found lanes to conflictLanes
     695             :     //
     696             :     // bidiBlock (if any forwardBlock edge has bidi edge)
     697             :     // - search bidi backward recursive until first switch
     698             :     //   - from switch search backward recursive all other incoming until controlled rail signal link
     699             :     //     -> add final links to conflictLinks
     700             :     //
     701             :     // flanks
     702             :     // - search backward recursive from flanking switches
     703             :     //   until controlled railSignal link or protecting switch is found
     704             :     //   -> add all found lanes to conflictLanes
     705             :     //   -> add final links to conflictLinks
     706             : 
     707        2498 :     DriveWay dw;
     708             :     LaneVisitedMap visited;
     709             :     std::vector<const MSLane*> before;
     710        2498 :     appendMapIndex(visited, myLink->getLaneBefore());
     711        2498 :     MSLane* fromBidi = myLink->getLaneBefore()->getBidiLane();
     712        2498 :     if (fromBidi != nullptr) {
     713             :         // do not extend to forward block beyond the entering track (in case of a loop)
     714        1341 :         appendMapIndex(visited, fromBidi);
     715        1341 :         before.push_back(fromBidi);
     716             :     }
     717        2498 :     dw.buildRoute(myLink, 0., first, end, visited);
     718        2498 :     if (dw.myProtectedBidi == nullptr) {
     719        2215 :         dw.myCoreSize = (int)dw.myRoute.size();
     720             :     }
     721        2498 :     dw.checkFlanks(myLink, dw.myForward, visited, true, dw.myFlankSwitches);
     722        2498 :     dw.checkFlanks(myLink, dw.myBidi, visited, false, dw.myFlankSwitches);
     723        2498 :     dw.checkFlanks(myLink, before, visited, true, dw.myFlankSwitches);
     724        5613 :     for (MSLink* link : dw.myFlankSwitches) {
     725             :         //std::cout << getID() << " flankSwitch=" << link->getDescription() << "\n";
     726        3115 :         dw.findFlankProtection(link, 0, visited, link, dw.myFlank);
     727             :     }
     728             :     std::vector<MSLink*> flankSwitchesBidiExtended;
     729        2498 :     dw.checkFlanks(myLink, dw.myBidiExtended, visited, false, flankSwitchesBidiExtended);
     730        2619 :     for (MSLink* link : flankSwitchesBidiExtended) {
     731             :         //std::cout << getID() << " flankSwitchBEx=" << link->getDescription() << "\n";
     732         121 :         dw.findFlankProtection(link, 0, visited, link, dw.myBidiExtended);
     733             :     }
     734             : 
     735             : #ifdef DEBUG_BUILD_DRIVEWAY
     736             :     if (DEBUG_COND_LINKINFO) {
     737             :         std::cout << "  buildDriveWay railSignal=" << getID()
     738             :                   << "\n    route=" << toString(dw.myRoute)
     739             :                   << "\n    forward=" << toString(dw.myForward)
     740             :                   << "\n    bidi=" << toString(dw.myBidi)
     741             :                   << "\n    flank=" << toString(dw.myFlank)
     742             :                   << "\n    flankSwitch=" << describeLinks(dw.myFlankSwitches)
     743             :                   << "\n    protSwitch=" << describeLinks(dw.myProtectingSwitches)
     744             :                   << "\n    coreSize=" << dw.myCoreSize
     745             :                   << "\n";
     746             :     }
     747             : #endif
     748        2498 :     MSRailSignal* rs = const_cast<MSRailSignal*>(static_cast<const MSRailSignal*>(myLink->getTLLogic()));
     749        2498 :     if (!rs->myMovingBlock) {
     750        2473 :         dw.myConflictLanes.insert(dw.myConflictLanes.end(), dw.myForward.begin(), dw.myForward.end());
     751             :     }
     752        2498 :     dw.myConflictLanes.insert(dw.myConflictLanes.end(), dw.myBidi.begin(), dw.myBidi.end());
     753        2498 :     dw.myConflictLanes.insert(dw.myConflictLanes.end(), dw.myFlank.begin(), dw.myFlank.end());
     754        2498 :     if (dw.myProtectedBidi != nullptr) {
     755         283 :         MSRailSignalControl::getInstance().registerProtectedDriveway(rs, dw.myNumericalID, dw.myProtectedBidi);
     756             :     }
     757             : 
     758        2498 :     return dw;
     759           0 : }
     760             : 
     761             : 
     762             : void
     763       15240 : MSRailSignal::LinkInfo::reroute(SUMOVehicle* veh, const MSEdgeVector& occupied) {
     764       15240 :     MSDevice_Routing* rDev = static_cast<MSDevice_Routing*>(veh->getDevice(typeid(MSDevice_Routing)));
     765       15240 :     const SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
     766             :     if (rDev != nullptr
     767       11214 :             && rDev->mayRerouteRailSignal()
     768       22956 :             && (myLastRerouteVehicle != veh
     769             :                 // reroute each vehicle only once if no periodic routing is allowed,
     770             :                 // otherwise with the specified period
     771        7596 :                 || (rDev->getPeriod() > 0 && myLastRerouteTime + rDev->getPeriod() <= now))) {
     772         180 :         myLastRerouteVehicle = veh;
     773         180 :         myLastRerouteTime = now;
     774             : 
     775             : #ifdef DEBUG_REROUTE
     776             :         ConstMSEdgeVector oldRoute = veh->getRoute().getEdges();
     777             :         if (DEBUG_COND_LINKINFO) {
     778             :             std::cout << SIMTIME << " reroute veh=" << veh->getID() << " rs=" << getID() << " occupied=" << toString(occupied) << "\n";
     779             :         }
     780             : #endif
     781         360 :         MSRoutingEngine::reroute(*veh, now, "railSignal:" + getID(), false, true, occupied);
     782             : #ifdef DEBUG_REROUTE
     783             :         // attention this works only if we are not parallel!
     784             :         if (DEBUG_COND_LINKINFO) {
     785             :             if (veh->getRoute().getEdges() != oldRoute) {
     786             :                 std::cout << "    rerouting successful\n";
     787             :             }
     788             :         }
     789             : #endif
     790             :     }
     791       15240 : }
     792             : 
     793             : 
     794             : // ===========================================================================
     795             : // DriveWay method definitions
     796             : // ===========================================================================
     797             : 
     798             : bool
     799      396159 : MSRailSignal::DriveWay::reserve(const Approaching& closest, MSEdgeVector& occupied) {
     800      396159 :     std::string joinVehicle = "";
     801      396159 :     if (!MSGlobals::gUseMesoSim) {
     802      395446 :         const SUMOVehicleParameter::Stop* stop = closest.first->getNextStopParameter();
     803      395446 :         if (stop != nullptr) {
     804      281813 :             joinVehicle = stop->join;
     805             :         }
     806             :     }
     807      396159 :     if (conflictLaneOccupied(joinVehicle, true, closest.first)) {
     808      932994 :         for (const MSLane* bidi : myBidi) {
     809      823536 :             if (!bidi->empty() && bidi->getBidiLane() != nullptr) {
     810       15300 :                 occupied.push_back(&bidi->getBidiLane()->getEdge());
     811             :             }
     812             :         }
     813             : #ifdef DEBUG_SIGNALSTATE
     814             :         if (gDebugFlag4) {
     815             :             std::cout << "  conflictLaneOccupied by=" << toString(myBlockingVehicles) << " ego=" << Named::getIDSecure(closest.first) << "\n";
     816             :         }
     817             : #endif
     818             :         return false;
     819             :     }
     820      348718 :     for (MSLink* link : myProtectingSwitches) {
     821       65111 :         if (!findProtection(closest, link)) {
     822             : #ifdef DEBUG_SIGNALSTATE
     823             :             if (gDebugFlag4) {
     824             :                 std::cout << "  no protection at switch " << link->getDescription() << "\n";
     825             :             }
     826             : #endif
     827             :             return false;
     828             :         }
     829             :     }
     830      369558 :     for (MSLink* foeLink : myConflictLinks) {
     831       90397 :         if (hasLinkConflict(closest, foeLink)) {
     832             : #ifdef DEBUG_SIGNALSTATE
     833             :             if (gDebugFlag4) {
     834             :                 std::cout << "  linkConflict with " << getTLLinkID(foeLink) << "\n";
     835             :             }
     836             : #endif
     837             :             return false;
     838             :         }
     839             :     }
     840      279161 :     if (deadlockLaneOccupied()) {
     841             :         return false;
     842             :     }
     843      277673 :     myActive = closest.first;
     844      277673 :     return true;
     845             : }
     846             : 
     847             : 
     848             : bool
     849    20338879 : MSRailSignal::DriveWay::conflictLinkApproached() const {
     850    27202719 :     for (MSLink* foeLink : myConflictLinks) {
     851    13328823 :         if (foeLink->getApproaching().size() > 0) {
     852             : #ifdef DEBUG_SIGNALSTATE
     853             :             if (gDebugFlag4) {
     854             :                 std::cout << SIMTIME << " foeLink=" << foeLink->getDescription() << " approachedBy=" << foeLink->getApproaching().begin()->first->getID() << "\n";
     855             :             }
     856             : #endif
     857             :             return true;
     858             :         }
     859             :     }
     860             :     return false;
     861             : }
     862             : 
     863             : 
     864             : bool
     865       90397 : MSRailSignal::DriveWay::hasLinkConflict(const Approaching& veh, MSLink* foeLink) const {
     866             : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     867             :     if (gDebugFlag4) {
     868             :         std::cout << "   checkLinkConflict foeLink=" << getTLLinkID(foeLink) << "\n";
     869             :     }
     870             : #endif
     871       90397 :     if (foeLink->getApproaching().size() > 0) {
     872       20308 :         Approaching foe = getClosest(foeLink);
     873             : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     874             :         if (gDebugFlag4) {
     875             :             std::cout << "     approaching foe=" << foe.first->getID() << "\n";
     876             :         }
     877             : #endif
     878             :         const MSTrafficLightLogic* foeTLL = foeLink->getTLLogic();
     879             :         assert(foeTLL != nullptr);
     880       20308 :         const MSRailSignal* constFoeRS = dynamic_cast<const MSRailSignal*>(foeTLL);
     881             :         MSRailSignal* foeRS = const_cast<MSRailSignal*>(constFoeRS);
     882       20308 :         if (foeRS != nullptr) {
     883       20308 :             const DriveWay& foeDriveWay = foeRS->myLinkInfos[foeLink->getTLIndex()].getDriveWay(foe.first);
     884       58861 :             if (foeDriveWay.conflictLaneOccupied("", false, foe.first) ||
     885       35370 :                     foeDriveWay.deadlockLaneOccupied(false) ||
     886       45412 :                     !foeRS->constraintsAllow(foe.first) ||
     887        7979 :                     !overlap(foeDriveWay)) {
     888             : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     889             :                 if (gDebugFlag4) {
     890             :                     if (foeDriveWay.conflictLaneOccupied("", false, foe.first)) {
     891             :                         std::cout << "     foe blocked\n";
     892             :                     } else if (!foeRS->constraintsAllow(foe.first)) {
     893             :                         std::cout << "     foe constrained\n";
     894             :                     } else {
     895             :                         std::cout << "     no overlap\n";
     896             :                     }
     897             :                 }
     898             : #endif
     899       20308 :                 return false;
     900             :             }
     901             : #ifdef DEBUG_SIGNALSTATE_PRIORITY
     902             :             if (gDebugFlag4) {
     903             :                 std::cout
     904             :                         << "  aSB=" << veh.second.arrivalSpeedBraking << " foeASB=" << foe.second.arrivalSpeedBraking
     905             :                         << "  aT=" << veh.second.arrivalTime << " foeAT=" << foe.second.arrivalTime
     906             :                         << "  aS=" << veh.first->getSpeed() << " foeS=" << foe.first->getSpeed()
     907             :                         << "  aD=" << veh.second.dist << " foeD=" << foe.second.dist
     908             :                         << "  aW=" << veh.first->getWaitingTime() << " foeW=" << foe.first->getWaitingTime()
     909             :                         << "  aN=" << veh.first->getNumericalID() << " foeN=" << foe.first->getNumericalID()
     910             :                         << "\n";
     911             :             }
     912             : #endif
     913        7793 :             const bool yield = mustYield(veh, foe);
     914        7793 :             if (myStoreVehicles) {
     915        1155 :                 myRivalVehicles.push_back(foe.first);
     916        1155 :                 if (yield) {
     917         487 :                     myPriorityVehicles.push_back(foe.first);
     918             :                 }
     919             :             }
     920        7793 :             return yield;
     921             :         }
     922             :     }
     923             :     return false;
     924             : }
     925             : 
     926             : 
     927             : bool
     928        7796 : MSRailSignal::DriveWay::mustYield(const Approaching& veh, const Approaching& foe) {
     929        7796 :     if (foe.second.arrivalSpeedBraking == veh.second.arrivalSpeedBraking) {
     930        3425 :         if (foe.second.arrivalTime == veh.second.arrivalTime) {
     931         325 :             if (foe.first->getSpeed() == veh.first->getSpeed()) {
     932         304 :                 if (foe.second.dist == veh.second.dist) {
     933         276 :                     if (foe.first->getWaitingTime() == veh.first->getWaitingTime()) {
     934         261 :                         return foe.first->getNumericalID() < veh.first->getNumericalID();
     935             :                     } else {
     936          15 :                         return foe.first->getWaitingTime() > veh.first->getWaitingTime();
     937             :                     }
     938             :                 } else {
     939          28 :                     return foe.second.dist < veh.second.dist;
     940             :                 }
     941             :             } else {
     942          21 :                 return foe.first->getSpeed() > veh.first->getSpeed();
     943             :             }
     944             :         } else {
     945        3100 :             return foe.second.arrivalTime < veh.second.arrivalTime;
     946             :         }
     947             :     } else {
     948        4371 :         return foe.second.arrivalSpeedBraking > veh.second.arrivalSpeedBraking;
     949             :     }
     950             : }
     951             : 
     952             : 
     953             : bool
     954    20999564 : MSRailSignal::DriveWay::conflictLaneOccupied(const std::string& joinVehicle, bool store, const SUMOVehicle* ego) const {
     955   159875711 :     for (const MSLane* lane : myConflictLanes) {
     956             :         if (!lane->isEmpty()) {
     957             : #ifdef DEBUG_SIGNALSTATE
     958             :             if (gDebugFlag4) {
     959             :                 std::cout << SIMTIME << " conflictLane " << lane->getID() << " occupied ego=" << Named::getIDSecure(ego) << " vehNumber=" << lane->getVehicleNumber() << "\n";
     960             :                 if (joinVehicle != "") {
     961             :                     std::cout << "  joinVehicle=" << joinVehicle << " occupant=" << toString(lane->getVehiclesSecure()) << "\n";
     962             :                     lane->releaseVehicles();
     963             :                 }
     964             :             }
     965             : #endif
     966      362516 :             if (lane->getVehicleNumberWithPartials() == 1) {
     967      361442 :                 MSVehicle* foe = lane->getLastAnyVehicle();
     968      361442 :                 if (joinVehicle != "") {
     969         108 :                     if (foe->getID() == joinVehicle && foe->isStopped()) {
     970             : #ifdef DEBUG_SIGNALSTATE
     971             :                         if (gDebugFlag4) {
     972             :                             std::cout << "    ignore join-target '" << joinVehicle << "\n";
     973             :                         }
     974             : #endif
     975           9 :                         continue;
     976             :                     }
     977             :                 }
     978      361433 :                 if (ego != nullptr) {
     979      112264 :                     if (foe == ego && std::find(myBidi.begin(), myBidi.end(), lane) != myBidi.end()) {
     980             : #ifdef DEBUG_SIGNALSTATE
     981             :                         if (gDebugFlag4) {
     982             :                             std::cout << "    ignore ego as oncoming '" << ego->getID() << "\n";
     983             :                         }
     984             : #endif
     985         999 :                         continue;
     986             :                     }
     987      111265 :                     if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
     988             : #ifdef DEBUG_SIGNALSTATE
     989             :                         if (gDebugFlag4) {
     990             :                             std::cout << "    ignore " << foe->getID() << " for which ego is join-target\n";
     991             :                         }
     992             : #endif
     993           9 :                         continue;
     994             :                     }
     995             :                 }
     996             :             }
     997      361499 :             if (myStoreVehicles && store) {
     998        6463 :                 myBlockingVehicles.push_back(lane->getLastAnyVehicle());
     999             :             }
    1000      361499 :             return true;
    1001             :         }
    1002             :     }
    1003    20638065 :     return false;
    1004             : }
    1005             : 
    1006             : bool
    1007      297406 : MSRailSignal::DriveWay::deadlockLaneOccupied(bool store) const {
    1008      318443 :     for (const MSLane* lane : myBidiExtended) {
    1009       23645 :         if (!lane->empty()) {
    1010             :             assert(myBidi.size() != 0);
    1011        2608 :             const MSEdge* lastBidi = myBidi.back()->getNextNormal();
    1012        2608 :             MSVehicle* foe = lane->getVehiclesSecure().front();
    1013             : #ifdef DEBUG_SIGNALSTATE
    1014             :             if (gDebugFlag4) {
    1015             :                 std::cout << "  check for deadlock with " << foe->getID() << "\n";
    1016             :             }
    1017             : #endif
    1018             :             // check of foe will enter myBidi (need to check at most
    1019             :             // myBidiExtended.size edges)
    1020        2608 :             const int minEdges = (int)myBidiExtended.size();
    1021        2608 :             auto foeIt = foe->getCurrentRouteEdge() + 1;
    1022        2608 :             auto foeEnd = foe->getRoute().end();
    1023             :             bool conflict = false;
    1024        5068 :             for (int i = 0; i < minEdges && foeIt != foeEnd; i++) {
    1025        5068 :                 if ((*foeIt) == lastBidi) {
    1026             : #ifdef DEBUG_SIGNALSTATE
    1027             :                     if (gDebugFlag4) {
    1028             :                         std::cout << "    vehicle will enter " << lastBidi->getID() << "\n";
    1029             :                     }
    1030             : #endif
    1031             :                     conflict = true;
    1032             :                     break;
    1033             :                 }
    1034             :                 foeIt++;
    1035             :             }
    1036        2608 :             lane->releaseVehicles();
    1037        2608 :             if (conflict) {
    1038        2608 :                 if (myStoreVehicles && store) {
    1039           0 :                     myBlockingVehicles.push_back(foe);
    1040             :                 }
    1041        2608 :                 return true;
    1042             :             }
    1043             :         }
    1044             :     }
    1045             :     return false;
    1046             : }
    1047             : 
    1048             : 
    1049             : bool
    1050       65498 : MSRailSignal::DriveWay::findProtection(const Approaching& veh, MSLink* link) const {
    1051             :     double flankApproachingDist = std::numeric_limits<double>::max();
    1052       65498 :     if (link->getApproaching().size() > 0) {
    1053        4071 :         Approaching closest = getClosest(link);
    1054        4071 :         flankApproachingDist = closest.second.dist;
    1055             :     }
    1056             : #ifdef DEBUG_FIND_PROTECTION
    1057             :     if (gDebugFlag4) {
    1058             :         std::cout << SIMTIME << " findProtection for link=" << link->getDescription() << " flankApproachingDist=" << flankApproachingDist << "\n";
    1059             :     }
    1060             : #endif
    1061      171839 :     for (MSLink* l2 : link->getLaneBefore()->getLinkCont()) {
    1062      118972 :         if (l2->getLane() != link->getLane()) {
    1063             : #ifdef DEBUG_FIND_PROTECTION
    1064             :             if (gDebugFlag4) {
    1065             :                 std::cout << " protectionCandidate=" << l2->getDescription() << " l2Via=" << Named::getIDSecure(l2->getViaLane())
    1066             :                           << " occupied=" << (l2->getViaLane() != nullptr && !l2->getViaLane()->isEmpty()) << "\n";
    1067             :             }
    1068             : #endif
    1069       65873 :             if (l2->getViaLane() != nullptr && !l2->getViaLane()->isEmpty()) {
    1070             : #ifdef DEBUG_FIND_PROTECTION
    1071             :                 if (gDebugFlag4) {
    1072             :                     std::cout << "   protection from internal=" << l2->getViaLane()->getID() << "\n";
    1073             :                 }
    1074             : #endif
    1075             :                 return true;
    1076             :             }
    1077       64709 :             if (l2->getApproaching().size() > 0) {
    1078       11467 :                 Approaching closest2 = getClosest(l2);
    1079       11467 :                 if (closest2.second.dist < flankApproachingDist) {
    1080             : #ifdef DEBUG_FIND_PROTECTION
    1081             :                     if (gDebugFlag4) {
    1082             :                         std::cout << "   protection from veh=" << closest2.first->getID() << "\n";
    1083             :                     }
    1084             : #endif
    1085       11467 :                     return true;
    1086             :                 }
    1087             :             }
    1088             :         }
    1089             :     }
    1090       52867 :     if (link->getApproaching().size() == 0) {
    1091             :         return true;
    1092             :     } else {
    1093             :         // find protection further upstream
    1094        4071 :         DriveWay tmp(true);
    1095        4071 :         const MSLane* before = link->getLaneBefore();
    1096        4071 :         tmp.myFlank.push_back(before);
    1097             :         LaneVisitedMap visited;
    1098        8432 :         for (auto ili : before->getIncomingLanes()) {
    1099        4361 :             tmp.findFlankProtection(ili.viaLink, myMaxFlankLength, visited, ili.viaLink, tmp.myFlank);
    1100             :         }
    1101        4071 :         tmp.myConflictLanes = tmp.myFlank;
    1102        4071 :         tmp.myRoute = myRoute;
    1103        4071 :         tmp.myCoreSize = myCoreSize;
    1104             :         MSEdgeVector occupied;
    1105        4071 :         if (gDebugFlag4) std::cout << SIMTIME << " tmpDW flank=" << toString(tmp.myFlank)
    1106           0 :                                        << " protSwitch=" << describeLinks(tmp.myProtectingSwitches) << " cLinks=" << describeLinks(tmp.myConflictLinks) << "\n";
    1107        4071 :         return tmp.reserve(veh, occupied);
    1108        4071 :     }
    1109             : }
    1110             : 
    1111             : 
    1112             : bool
    1113        7979 : MSRailSignal::DriveWay::overlap(const DriveWay& other) const {
    1114       12522 :     for (int i = 0; i < myCoreSize; i++) {
    1115       53434 :         for (int j = 0; j < other.myCoreSize; j++) {
    1116       48891 :             const MSEdge* edge = myRoute[i];
    1117       48891 :             const MSEdge* edge2 = other.myRoute[j];
    1118             :             if (edge->getToJunction() == edge2->getToJunction()
    1119       48891 :                     || edge->getToJunction() == edge2->getFromJunction()) {
    1120             :                 // XXX might be rail_crossing with parallel tracks
    1121             :                 return true;
    1122             :             }
    1123             :         }
    1124             :     }
    1125             :     return false;
    1126             : }
    1127             : 
    1128             : bool
    1129          29 : MSRailSignal::DriveWay::flankConflict(const DriveWay& other) const {
    1130         140 :     for (const MSLane* lane : myForward) {
    1131         534 :         for (const MSLane* lane2 : other.myForward) {
    1132         420 :             if (lane == lane2) {
    1133             :                 return true;
    1134             :             }
    1135             :         }
    1136        1273 :         for (const MSLane* lane2 : other.myBidi) {
    1137        1162 :             if (lane == lane2) {
    1138             :                 return true;
    1139             :             }
    1140             :         }
    1141             :     }
    1142             :     return false;
    1143             : }
    1144             : 
    1145             : void
    1146        1462 : MSRailSignal::DriveWay::writeBlocks(OutputDevice& od) const {
    1147        1462 :     od.openTag("driveWay");
    1148        1462 :     od.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
    1149        1462 :     if (myCoreSize != (int)myRoute.size()) {
    1150         188 :         od.writeAttr("core", myCoreSize);
    1151             :     }
    1152        1462 :     od.openTag("forward");
    1153        1462 :     od.writeAttr(SUMO_ATTR_LANES, toString(myForward));
    1154        1462 :     od.closeTag();
    1155        1462 :     od.openTag("bidi");
    1156        2924 :     od.writeAttr(SUMO_ATTR_LANES, toString(myBidi));
    1157        1462 :     if (myBidiExtended.size() > 0) {
    1158          72 :         od.lf();
    1159          72 :         od << "                   ";
    1160         196 :         od.writeAttr("deadlockCheck", toString(myBidiExtended));
    1161             :     }
    1162        1462 :     od.closeTag();
    1163        1462 :     od.openTag("flank");
    1164        1462 :     od.writeAttr(SUMO_ATTR_LANES, toString(myFlank));
    1165        1462 :     od.closeTag();
    1166             : 
    1167        2924 :     od.openTag("protectingSwitches");
    1168             :     std::vector<std::string> links;
    1169        1883 :     for (MSLink* link : myProtectingSwitches) {
    1170         842 :         links.push_back(getJunctionLinkID(link));
    1171             :     }
    1172        2924 :     od.writeAttr("links", joinToString(links, " "));
    1173        1462 :     od.closeTag();
    1174             : 
    1175        2924 :     od.openTag("conflictLinks");
    1176             :     std::vector<std::string> signals;
    1177        2370 :     for (MSLink* link : myConflictLinks) {
    1178        1816 :         signals.push_back(getTLLinkID(link));
    1179             :     }
    1180        2924 :     od.writeAttr("signals", joinToString(signals, " "));
    1181        1462 :     od.closeTag();
    1182        1462 :     od.closeTag(); // driveWay
    1183        1462 : }
    1184             : 
    1185             : 
    1186             : void
    1187        2498 : MSRailSignal::DriveWay::buildRoute(MSLink* origin, double length,
    1188             :                                    MSRouteIterator next, MSRouteIterator end,
    1189             :                                    LaneVisitedMap& visited) {
    1190             :     bool seekForwardSignal = true;
    1191             :     bool seekBidiSwitch = true;
    1192             :     bool foundUnsafeSwitch = false;
    1193        2498 :     MSLane* toLane = origin->getViaLaneOrLane();
    1194             : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1195             :     gDebugFlag4 = DEBUG_HELPER(origin->getTLLogic());
    1196             :     if (gDebugFlag4) std::cout << "buildRoute origin=" << getTLLinkID(origin) << " vehRoute=" << toString(ConstMSEdgeVector(next, end))
    1197             :                                    << " visited=" << formatVisitedMap(visited) << "\n";
    1198             : #endif
    1199       13838 :     while ((seekForwardSignal || seekBidiSwitch)) {
    1200       13072 :         if (length > MAX_BLOCK_LENGTH) {
    1201           0 :             if (myNumWarnings < MAX_SIGNAL_WARNINGS) {
    1202           0 :                 WRITE_WARNING("Block after rail signal " + getClickableTLLinkID(origin) +
    1203             :                               " exceeds maximum length (stopped searching after edge '" + toLane->getEdge().getID() + "' (length=" + toString(length) + "m).");
    1204             :             }
    1205           0 :             myNumWarnings++;
    1206             :             // length exceeded
    1207        1732 :             return;
    1208             :         }
    1209             : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1210             :         if (gDebugFlag4) {
    1211             :             std::cout << "   toLane=" << toLane->getID() << " visited=" << formatVisitedMap(visited) << "\n";
    1212             :         }
    1213             : #endif
    1214             :         if (visited.count(toLane) != 0) {
    1215           0 :             WRITE_WARNINGF(TL("Found circular block after railSignal % (% edges, length %)"), getClickableTLLinkID(origin), toString(myRoute.size()), toString(length));
    1216             :             //std::cout << getClickableTLLinkID(origin) << " circularBlock1=" << toString(myRoute) << " visited=" << formatVisitedMap(visited) << "\n";
    1217           0 :             return;
    1218             :         }
    1219       13072 :         if (toLane->getEdge().isNormal()) {
    1220        8254 :             myRoute.push_back(&toLane->getEdge());
    1221        8254 :             if (next != end) {
    1222             :                 next++;
    1223             :             }
    1224             :         }
    1225       13072 :         appendMapIndex(visited, toLane);
    1226       13072 :         length += toLane->getLength();
    1227       13072 :         MSLane* bidi = toLane->getBidiLane();
    1228       13072 :         if (seekForwardSignal) {
    1229        7888 :             if (!foundUnsafeSwitch) {
    1230        7888 :                 myForward.push_back(toLane);
    1231             :             }
    1232        5184 :         } else if (bidi == nullptr) {
    1233             :             seekBidiSwitch = false;
    1234             : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1235             :             if (gDebugFlag4) {
    1236             :                 std::cout << "      noBidi, abort search for bidiSwitch\n";
    1237             :             }
    1238             : #endif
    1239             :         }
    1240       13072 :         if (bidi != nullptr) {
    1241        9710 :             if (foundUnsafeSwitch) {
    1242         459 :                 myBidiExtended.push_back(bidi);
    1243             :             } else {
    1244        9251 :                 myBidi.push_back(bidi);
    1245             :             }
    1246        9710 :             appendMapIndex(visited, bidi);
    1247        9710 :             if (!seekForwardSignal) {
    1248             :                 // look for switch that could protect from oncoming vehicles
    1249        9562 :                 for (const auto& ili : bidi->getIncomingLanes()) {
    1250        4949 :                     if (ili.viaLink->getDirection() == LinkDirection::TURN) {
    1251         240 :                         continue;
    1252             :                     }
    1253        9488 :                     for (const MSLink* const link : ili.lane->getLinkCont()) {
    1254        5062 :                         if (link->getDirection() == LinkDirection::TURN) {
    1255         101 :                             continue;
    1256             :                         }
    1257        4961 :                         if (link->getViaLaneOrLane() != bidi) {
    1258             :                             // this switch is special beause it still lies on the current route
    1259             :                             //myProtectingSwitches.push_back(ili.viaLink);
    1260         449 :                             const MSEdge* const bidiNext = bidi->getNextNormal();
    1261         449 :                             myCoreSize = (int)myRoute.size();
    1262         449 :                             if (MSRailSignalControl::getInstance().getUsedEdges().count(bidiNext) == 0) {
    1263             : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1264             :                                 if (gDebugFlag4) {
    1265             :                                     std::cout << "      abort: found protecting switch " << ili.viaLink->getDescription() << "\n";
    1266             :                                 }
    1267             : #endif
    1268             :                                 // if bidi is actually used by a train (rather than
    1269             :                                 // the other route) we must later adapt this driveway for additional checks (myBidiExtended)
    1270         283 :                                 myProtectedBidi = bidiNext;
    1271             :                                 std::set<const MSEdge*> visitedEdges;
    1272        5125 :                                 for (auto item : visited) {
    1273        4842 :                                     visitedEdges.insert(&item.first->getEdge());
    1274             :                                 }
    1275        2151 :                                 while (next != end && visitedEdges.count(*next) == 0) {
    1276             :                                     // the driveway is route specific but only but stop recording if it loops back on itself
    1277             :                                     visitedEdges.insert(*next);
    1278        1868 :                                     const MSEdge* nextBidi = (*next)->getBidiEdge();
    1279        1868 :                                     if (nextBidi != nullptr) {
    1280             :                                         visitedEdges.insert(nextBidi);
    1281             :                                     }
    1282        1868 :                                     myRoute.push_back(*next);
    1283             :                                     next++;
    1284             :                                 }
    1285             :                                 return;
    1286             :                             } else {
    1287             : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1288             :                                 if (gDebugFlag4) {
    1289             :                                     std::cout << "      found unsafe switch " << ili.viaLink->getDescription() << " (used=" << bidiNext->getID() << ")\n";
    1290             :                                 }
    1291             : #endif
    1292             :                                 // trains along our route beyond this switch
    1293             :                                 // might create deadlock
    1294             :                                 foundUnsafeSwitch = true;
    1295             :                                 // the switch itself must still be guarded to ensure safety
    1296         348 :                                 for (const auto& ili2 : bidi->getIncomingLanes()) {
    1297         182 :                                     if (ili2.viaLink->getDirection() != LinkDirection::TURN) {
    1298         174 :                                         myFlankSwitches.push_back(ili.viaLink);
    1299             :                                     }
    1300             :                                 }
    1301             :                             }
    1302             :                         }
    1303             :                     }
    1304             :                 }
    1305             :             }
    1306             :         }
    1307       12789 :         const std::vector<MSLink*>& links = toLane->getLinkCont();
    1308             :         const MSEdge* current = &toLane->getEdge();
    1309       12789 :         toLane = nullptr;
    1310       13870 :         for (const MSLink* const link : links) {
    1311       12013 :             if ((next != end && &link->getLane()->getEdge() == *next)
    1312       23917 :                     && isRailway(link->getViaLaneOrLane()->getPermissions())) {
    1313       11418 :                 toLane = link->getViaLaneOrLane();
    1314       11418 :                 if (link->getLane()->getBidiLane() != nullptr && &link->getLane()->getEdge() == current->getBidiEdge()) {
    1315             :                     // do not follow turn-arounds even if the route contains a reversal
    1316             : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1317             :                     if (gDebugFlag4) {
    1318             :                         std::cout << "      abort: turn-around\n";
    1319             :                     }
    1320             : #endif
    1321          78 :                     myFoundReversal = true;
    1322          78 :                     return;
    1323             :                 }
    1324       11340 :                 if (link->getTLLogic() != nullptr) {
    1325        2991 :                     if (link->getTLLogic() == origin->getTLLogic()) {
    1326           0 :                         WRITE_WARNINGF(TL("Found circular block at railSignal % (% edges, length %)"), getClickableTLLinkID(origin), toString(myRoute.size()), toString(length));
    1327             :                         //std::cout << getClickableTLLinkID(origin) << " circularBlock2=" << toString(myRoute) << "\n";
    1328           0 :                         return;
    1329             :                     }
    1330             :                     seekForwardSignal = false;
    1331        2991 :                     myFoundSignal = true;
    1332        2991 :                     seekBidiSwitch = bidi != nullptr;
    1333             : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1334             :                     if (gDebugFlag4) {
    1335             :                         std::cout << "      found forwardSignal " << link->getTLLogic()->getID() << " seekBidiSwitch=" << seekBidiSwitch << "\n";
    1336             :                     }
    1337             : #endif
    1338             :                 }
    1339             :                 break;
    1340             :             }
    1341             :         }
    1342       12711 :         if (toLane == nullptr) {
    1343        1371 :             if (next != end) {
    1344             :                 // no connection found, jump to next route edge
    1345             :                 toLane = (*next)->getLanes()[0];
    1346             : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1347             :                 if (gDebugFlag4) {
    1348             :                     std::cout << "      abort: turn-around or jump\n";
    1349             :                 }
    1350             : #endif
    1351          33 :                 myFoundReversal = true;
    1352          33 :                 return;
    1353             :             } else {
    1354             : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
    1355             :                 if (gDebugFlag4) {
    1356             :                     std::cout << "      abort: no next lane available\n";
    1357             :                 }
    1358             : #endif
    1359             :                 return;
    1360             :             }
    1361             :         }
    1362             :     }
    1363             : }
    1364             : 
    1365             : 
    1366             : void
    1367        9992 : MSRailSignal::DriveWay::checkFlanks(const MSLink* originLink, const std::vector<const MSLane*>& lanes, const LaneVisitedMap& visited, bool allFoes, std::vector<MSLink*>& flankSwitches) const {
    1368             : #ifdef DEBUG_CHECK_FLANKS
    1369             :     std::cout << " checkFlanks lanes=" << toString(lanes) << "\n  visited=" << formatVisitedMap(visited) << " allFoes=" << allFoes << "\n";
    1370             : #endif
    1371       15532 :     const MSLink* reverseOriginLink = originLink->getLane()->getBidiLane() != nullptr && originLink->getLaneBefore()->getBidiLane() != nullptr
    1372       15120 :                                       ? originLink->getLane()->getBidiLane()->getLinkTo(originLink->getLaneBefore()->getBidiLane())
    1373             :                                       : nullptr;
    1374             :     //std::cout << "   originLink=" << originLink->getDescription() << "\n";
    1375        5128 :     if (reverseOriginLink != nullptr) {
    1376        5128 :         reverseOriginLink = reverseOriginLink->getCorrespondingExitLink();
    1377             :         //std::cout << "   reverseOriginLink=" << reverseOriginLink->getDescription() << "\n";
    1378             :     }
    1379       28931 :     for (int i = 0; i < (int)lanes.size(); i++) {
    1380       18939 :         const MSLane* lane = lanes[i];
    1381       18939 :         const MSLane* prev = i > 0 ? lanes[i - 1] : nullptr;
    1382       18939 :         const MSLane* next = i + 1 < (int)lanes.size() ? lanes[i + 1] : nullptr;
    1383       18939 :         if (lane->isInternal()) {
    1384        6384 :             continue;
    1385             :         }
    1386       27835 :         for (auto ili : lane->getIncomingLanes()) {
    1387       18890 :             if (ili.viaLink == originLink
    1388       14012 :                     || ili.viaLink == reverseOriginLink
    1389       12730 :                     || ili.viaLink->getDirection() == LinkDirection::TURN
    1390       26950 :                     || ili.viaLink->getDirection() == LinkDirection::TURN_LEFTHAND) {
    1391        3610 :                 continue;
    1392             :             }
    1393       11670 :             if (ili.lane != prev && ili.lane != next) {
    1394             : #ifdef DEBUG_CHECK_FLANKS
    1395             :                 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";
    1396             : #endif
    1397        3042 :                 flankSwitches.push_back(ili.viaLink);
    1398        8628 :             } else if (allFoes) {
    1399             :                 // link is part of the driveway, find foes that cross the driveway without entering
    1400        4067 :                 checkCrossingFlanks(ili.viaLink, visited, flankSwitches);
    1401             :             }
    1402             :         }
    1403             :     }
    1404        9992 : }
    1405             : 
    1406             : 
    1407             : void
    1408        4067 : MSRailSignal::DriveWay::checkCrossingFlanks(MSLink* dwLink, const LaneVisitedMap& visited, std::vector<MSLink*>& flankSwitches) const {
    1409             : #ifdef DEBUG_CHECK_FLANKS
    1410             :     std::cout << "  checkCrossingFlanks  dwLink=" << dwLink->getDescription() << " visited=" << formatVisitedMap(visited) << "\n";
    1411             : #endif
    1412             :     const MSJunction* junction = dwLink->getJunction();
    1413        4067 :     if (junction == nullptr) {
    1414             :         return; // unregulated junction;
    1415             :     }
    1416        4061 :     const MSJunctionLogic* logic = junction->getLogic();
    1417        4061 :     if (logic == nullptr) {
    1418             :         return; // unregulated junction;
    1419             :     }
    1420       21040 :     for (const MSEdge* in : junction->getIncoming()) {
    1421       16983 :         if (in->isInternal()) {
    1422        8355 :             continue;
    1423             :         }
    1424       17256 :         for (MSLane* inLane : in->getLanes()) {
    1425        8628 :             if (isRailway(inLane->getPermissions()) && visited.count(inLane) == 0) {
    1426        4194 :                 for (MSLink* link : inLane->getLinkCont()) {
    1427        2220 :                     if (link->getIndex() >= 0 && logic->getFoesFor(dwLink->getIndex()).test(link->getIndex())
    1428        6084 :                             && visited.count(link->getLane()) == 0) {
    1429             : #ifdef DEBUG_CHECK_FLANKS
    1430             :                         std::cout << " add crossing flankSwitch junction=" << junction->getID() << " index=" << link->getIndex() << "\n";
    1431             : #endif
    1432          20 :                         if (link->getViaLane() == nullptr) {
    1433           8 :                             flankSwitches.push_back(link);
    1434             :                         } else {
    1435          12 :                             flankSwitches.push_back(link->getViaLane()->getLinkCont().front());
    1436             :                         }
    1437             :                     }
    1438             :                 }
    1439             :             }
    1440             :         }
    1441             :     }
    1442             : }
    1443             : 
    1444             : void
    1445       28567 : MSRailSignal::DriveWay::findFlankProtection(MSLink* link, double length, LaneVisitedMap& visited, MSLink* origLink, std::vector<const MSLane*>& flank) {
    1446             : #ifdef DEBUG_CHECK_FLANKS
    1447             :     std::cout << "  findFlankProtection link=" << link->getDescription() << " length=" << length << " origLink=" << origLink->getDescription() << "\n";
    1448             : #endif
    1449       28567 :     if (link->getTLLogic() != nullptr) {
    1450             :         // guarded by signal
    1451             : #ifdef DEBUG_CHECK_FLANKS
    1452             :         std::cout << "   flank guarded by " << link->getTLLogic()->getID() << "\n";
    1453             : #endif
    1454        5598 :         myConflictLinks.push_back(link);
    1455       22969 :     } else if (length > MAX_BLOCK_LENGTH) {
    1456             :         // length exceeded
    1457           0 :         if (myNumWarnings < MAX_SIGNAL_WARNINGS) {
    1458           0 :             WRITE_WARNING("Incoming block at junction '" + origLink->getJunction()->getID() + "', link " + toString(origLink->getIndex()) + " exceeds maximum length (stopped searching after lane '" + link->getLane()->getID() + "' (length=" + toString(length) + "m).");
    1459             :         }
    1460           0 :         myNumWarnings++;
    1461             :     } else {
    1462             :         // find normal lane before this link
    1463       22969 :         const MSLane* lane = link->getLaneBefore();
    1464             :         const bool isNew = visited.count(lane) == 0;
    1465         275 :         if (isNew || (visited[lane] > visited[origLink->getLane()] && std::find(myForward.begin(), myForward.end(), lane) == myForward.end())) {
    1466       22905 :             if (isNew) {
    1467       22694 :                 appendMapIndex(visited, lane);
    1468             :             }
    1469       22905 :             length += lane->getLength();
    1470       22905 :             if (lane->isInternal()) {
    1471       12625 :                 flank.push_back(lane);
    1472       12625 :                 findFlankProtection(lane->getIncomingLanes().front().viaLink, length, visited, origLink, flank);
    1473             :             } else {
    1474             :                 bool foundPSwitch = false;
    1475       22258 :                 for (MSLink* l2 : lane->getLinkCont()) {
    1476             : #ifdef DEBUG_CHECK_FLANKS
    1477             :                     std::cout << "   lane=" << lane->getID() << " visitedIndex=" << visited[lane] << " origIndex=" << visited[origLink->getLane()] << " cand=" << l2->getDescription() << "\n";
    1478             : #endif
    1479       11978 :                     if (l2->getDirection() != LinkDirection::TURN && l2->getLane() != link->getLane()) {
    1480             :                         foundPSwitch = true;
    1481             :                         // found potential protection
    1482             : #ifdef DEBUG_CHECK_FLANKS
    1483             :                         std::cout << "   protectingSwitch=" << l2->getDescription() << " for flank=" << link->getDescription() << "\n";
    1484             : #endif
    1485        1688 :                         myProtectingSwitches.push_back(link);
    1486        1688 :                         if (std::find(myBidi.begin(), myBidi.end(), origLink->getLane()) != myBidi.end()) {
    1487             : #ifdef DEBUG_CHECK_FLANKS
    1488             :                             std::cout << "     (is bidi-switch)\n";
    1489             : #endif
    1490         485 :                             myProtectingSwitchesBidi.push_back(link);
    1491             :                         }
    1492             :                     }
    1493             :                 }
    1494       10280 :                 if (!foundPSwitch) {
    1495        8592 :                     flank.push_back(lane);
    1496             :                     // continue search for protection upstream recursively
    1497       16965 :                     for (auto ili : lane->getIncomingLanes()) {
    1498        8373 :                         if (ili.viaLink->getDirection() != LinkDirection::TURN) {
    1499        8345 :                             findFlankProtection(ili.viaLink, length, visited, origLink, flank);
    1500             :                         }
    1501             :                     }
    1502             :                 }
    1503             :             }
    1504             :         } else {
    1505             : #ifdef DEBUG_CHECK_FLANKS
    1506             :             std::cout << "    laneBefore=" << lane->getID() << " already visited. index=" << visited[lane] << " origAfter=" << origLink->getLane()->getID() << " origIndex=" << visited[origLink->getLane()] << "\n";
    1507             : #endif
    1508             :         }
    1509             :     }
    1510       28567 :     myMaxFlankLength = MAX2(myMaxFlankLength, length);
    1511       28567 : }
    1512             : 
    1513             : void
    1514       22656 : MSRailSignal::storeTraCIVehicles(int linkIndex) {
    1515             :     myBlockingVehicles.clear();
    1516             :     myRivalVehicles.clear();
    1517             :     myPriorityVehicles.clear();
    1518             :     myConstraintInfo = "";
    1519       22656 :     myStoreVehicles = true;
    1520       22656 :     LinkInfo& li = myLinkInfos[linkIndex];
    1521       22656 :     if (li.myLink->getApproaching().size() > 0) {
    1522        2445 :         Approaching closest = getClosest(li.myLink);
    1523        2445 :         DriveWay& driveway = li.getDriveWay(closest.first);
    1524             :         MSEdgeVector occupied;
    1525             :         // call for side effects
    1526        2445 :         driveway.reserve(closest, occupied);
    1527        2445 :         constraintsAllow(closest.first);
    1528       20211 :     } else if (li.myDriveways.size() > 0) {
    1529       39192 :         li.myDriveways.front().conflictLaneOccupied();
    1530       19596 :         li.myDriveways.front().conflictLinkApproached();
    1531             :     }
    1532       22656 :     myStoreVehicles = false;
    1533       22656 : }
    1534             : 
    1535             : MSRailSignal::VehicleVector
    1536        7560 : MSRailSignal::getBlockingVehicles(int linkIndex) {
    1537        7560 :     storeTraCIVehicles(linkIndex);
    1538        7560 :     return myBlockingVehicles;
    1539             : }
    1540             : 
    1541             : MSRailSignal::VehicleVector
    1542        7548 : MSRailSignal::getRivalVehicles(int linkIndex) {
    1543        7548 :     storeTraCIVehicles(linkIndex);
    1544        7548 :     return myRivalVehicles;
    1545             : }
    1546             : 
    1547             : MSRailSignal::VehicleVector
    1548        7548 : MSRailSignal::getPriorityVehicles(int linkIndex) {
    1549        7548 :     storeTraCIVehicles(linkIndex);
    1550        7548 :     return myPriorityVehicles;
    1551             : }
    1552             : 
    1553             : std::string
    1554           0 : MSRailSignal::getConstraintInfo(int linkIndex) {
    1555           0 :     storeTraCIVehicles(linkIndex);
    1556           0 :     return myConstraintInfo;
    1557             : }
    1558             : 
    1559             : const MSRailSignal::DriveWay&
    1560          32 : MSRailSignal::retrieveDriveWay(int numericalID) const {
    1561          32 :     for (const LinkInfo& li : myLinkInfos) {
    1562          32 :         for (const DriveWay& dw : li.myDriveways) {
    1563          32 :             if (dw.myNumericalID == numericalID) {
    1564          32 :                 return dw;
    1565             :             }
    1566             :         }
    1567             :     }
    1568           0 :     throw ProcessError("Invalid driveway id " + toString(numericalID) + " at railSignal '" + getID() + "'");
    1569             : }
    1570             : 
    1571             : 
    1572             : void
    1573          59 : MSRailSignal::updateDriveway(int numericalID) {
    1574         115 :     for (LinkInfo& li : myLinkInfos) {
    1575         115 :         for (auto it = li.myDriveways.begin(); it != li.myDriveways.end(); it++) {
    1576             :             const DriveWay& dw = *it;
    1577          59 :             if (dw.myNumericalID == numericalID) {
    1578             : #ifdef DEBUG_DRIVEWAY_UPDATE
    1579             :                 std::cout << SIMTIME << " rail signal junction '" << getID() << "' requires update for driveway " << numericalID << "\n";
    1580             : #endif
    1581          59 :                 std::vector<const MSEdge*> route = dw.myRoute;
    1582          59 :                 li.myDriveways.erase(it);
    1583          59 :                 if (li.myDriveways.size() == 0) {
    1584             :                     // rebuild default driveway
    1585         118 :                     li.myDriveways.push_back(li.buildDriveWay(route.begin(), route.end()));
    1586             :                 }
    1587             :                 return;
    1588             :             }
    1589             :         }
    1590             :     }
    1591             : }
    1592             : 
    1593             : std::string
    1594           0 : MSRailSignal::getBlockingVehicleIDs() const {
    1595             :     MSRailSignal* rs = const_cast<MSRailSignal*>(this);
    1596           0 :     if (myLinkInfos.size() == 1) {
    1597           0 :         return toString(rs->getBlockingVehicles(0));
    1598             :     } else {
    1599             :         std::string result;
    1600           0 :         for (int i = 0; i < (int)myLinkInfos.size(); i++) {
    1601           0 :             result += toString(i) + ": " + toString(rs->getBlockingVehicles(i)) + ";";
    1602             :         }
    1603             :         return result;
    1604             :     }
    1605             : }
    1606             : std::string
    1607           0 : MSRailSignal::getRivalVehicleIDs() const {
    1608             :     MSRailSignal* rs = const_cast<MSRailSignal*>(this);
    1609           0 :     if (myLinkInfos.size() == 1) {
    1610           0 :         return toString(rs->getRivalVehicles(0));
    1611             :     } else {
    1612             :         std::string result;
    1613           0 :         for (int i = 0; i < (int)myLinkInfos.size(); i++) {
    1614           0 :             result += toString(i) + ": " + toString(rs->getRivalVehicles(i)) + ";";
    1615             :         }
    1616             :         return result;
    1617             :     }
    1618             : }
    1619             : std::string
    1620           0 : MSRailSignal::getPriorityVehicleIDs() const {
    1621             :     MSRailSignal* rs = const_cast<MSRailSignal*>(this);
    1622           0 :     if (myLinkInfos.size() == 1) {
    1623           0 :         return toString(rs->getPriorityVehicles(0));
    1624             :     } else {
    1625             :         std::string result;
    1626           0 :         for (int i = 0; i < (int)myLinkInfos.size(); i++) {
    1627           0 :             result += toString(i) + ": " + toString(rs->getPriorityVehicles(i)) + ";";
    1628             :         }
    1629             :         return result;
    1630             :     }
    1631             : }
    1632             : std::string
    1633           0 : MSRailSignal::getConstraintInfo() const {
    1634             :     MSRailSignal* rs = const_cast<MSRailSignal*>(this);
    1635           0 :     if (myLinkInfos.size() == 1) {
    1636           0 :         return rs->getConstraintInfo(0);
    1637             :     } else {
    1638             :         std::string result;
    1639           0 :         for (int i = 0; i < (int)myLinkInfos.size(); i++) {
    1640           0 :             result += toString(i) + ": " + rs->getConstraintInfo(i);
    1641             :         }
    1642             :         return result;
    1643             :     }
    1644             : }
    1645             : 
    1646             : void
    1647           5 : MSRailSignal::setParameter(const std::string& key, const std::string& value) {
    1648             :     // some pre-defined parameters can be updated at runtime
    1649           5 :     if (key == "moving-block") {
    1650           5 :         bool movingBlock = StringUtils::toBool(value);
    1651           5 :         if (movingBlock != myMovingBlock) {
    1652             :             // recompute driveways
    1653           5 :             myMovingBlock = movingBlock;
    1654          10 :             for (LinkInfo& li : myLinkInfos) {
    1655           5 :                 li.reset();
    1656             :             }
    1657           5 :             updateCurrentPhase();
    1658           5 :             setTrafficLightSignals(MSNet::getInstance()->getCurrentTimeStep());
    1659             :         }
    1660             :     }
    1661           5 :     Parameterised::setParameter(key, value);
    1662           5 : }
    1663             : 
    1664             : /****************************************************************************/

Generated by: LCOV version 1.14