LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSRailSignalControl.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 99.2 % 133 132
Test Date: 2026-03-02 16:00:03 Functions: 100.0 % 16 16

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    MSRailSignalControl.cpp
      15              : /// @author  Jakob Erdmann
      16              : /// @date    Sept 2020
      17              : ///
      18              : // Centralized services for rail signal control (Singleton)
      19              : /****************************************************************************/
      20              : #include <config.h>
      21              : 
      22              : #include <cassert>
      23              : #include <utility>
      24              : #include <vector>
      25              : #include <bitset>
      26              : #include <microsim/MSNet.h>
      27              : #include <microsim/MSRoute.h>
      28              : #include <microsim/MSEdge.h>
      29              : #include <microsim/MSLane.h>
      30              : #include "MSRailSignal.h"
      31              : #include "MSRailSignalConstraint.h"
      32              : #include "MSDriveWay.h"
      33              : #include "MSRailSignalControl.h"
      34              : 
      35              : 
      36              : //#define DEBUG_BUILD_DEADLOCK_CHECK
      37              : 
      38              : // ===========================================================================
      39              : // static value definitions
      40              : // ===========================================================================
      41              : MSRailSignalControl* MSRailSignalControl::myInstance(nullptr);
      42              : SVCPermissions MSRailSignalControl::mySignalizedClasses(SVC_UNSPECIFIED);
      43              : SVCPermissions MSRailSignalControl::myMBClasses(SVC_UNSPECIFIED);
      44              : 
      45              : // ===========================================================================
      46              : // method definitions
      47              : // ===========================================================================
      48         1203 : MSRailSignalControl::MSRailSignalControl()
      49         1203 : {}
      50              : 
      51              : MSRailSignalControl&
      52     18653317 : MSRailSignalControl::getInstance() {
      53     18653317 :     if (myInstance == nullptr) {
      54         1203 :         myInstance = new MSRailSignalControl();
      55         1203 :         MSNet::getInstance()->addVehicleStateListener(myInstance);
      56              :     }
      57     18653317 :     return *myInstance;
      58              : }
      59              : 
      60              : void
      61        40907 : MSRailSignalControl::cleanup() {
      62        40907 :     delete myInstance;
      63        40907 :     myInstance = nullptr;
      64        40907 : }
      65              : 
      66              : void
      67          186 : MSRailSignalControl::clearState() {
      68          186 :     if (myInstance != nullptr) {
      69              :         myInstance->myDriveWayCompatibility.clear();
      70           10 :         myInstance->myDriveWaySucc.clear();
      71           10 :         myInstance->myDriveWayPred.clear();
      72           10 :         myInstance->myWrittenDeadlocks.clear();
      73           10 :         myInstance->myDeadlockChecks.clear();
      74              :         //myInstance->myActiveSignals.clear();
      75              :     }
      76          186 : }
      77              : 
      78              : 
      79         2406 : MSRailSignalControl::~MSRailSignalControl() {
      80         2406 : }
      81              : 
      82              : void
      83        16591 : MSRailSignalControl::vehicleStateChanged(const SUMOVehicle* const vehicle, MSNet::VehicleState to, const std::string& /*info*/) {
      84        16591 :     if (vehicle->isRail()) {
      85              :         std::string dummyMsg;
      86        18451 :         if ((to == MSNet::VehicleState::BUILT && (!vehicle->getParameter().wasSet(VEHPARS_FORCE_REROUTE) || vehicle->hasValidRoute(dummyMsg)))
      87        16315 :                 || to == MSNet::VehicleState::NEWROUTE) {
      88              :             // @note we could delay initialization until the departure time
      89         3573 :             if (vehicle->getEdge()->getFunction() != SumoXMLEdgeFunc::CONNECTOR && isRailwayOrShared(vehicle->getEdge()->getPermissions())) {
      90         3568 :                 MSRailSignal::initDriveWays(vehicle, to == MSNet::VehicleState::NEWROUTE);
      91              :             }
      92              :         }
      93              :     }
      94        16591 : }
      95              : 
      96              : 
      97              : void
      98         4697 : MSRailSignalControl::addSignal(MSRailSignal* signal) {
      99         4697 :     mySignals.push_back(signal);
     100        10671 :     for (const auto& links : signal->getLinks()) {
     101        11948 :         for (const MSLink* link : links) {
     102         5974 :             mySignalizedClasses |= link->getPermissions();
     103              :         }
     104              :     }
     105         4697 : }
     106              : 
     107              : 
     108              : void
     109       152137 : MSRailSignalControl::addWaitRelation(const SUMOVehicle* waits, const MSRailSignal* rs, const SUMOVehicle* reason, MSRailSignalConstraint* constraint) {
     110              :     //std::cout << time2string(SIMSTEP) << " addWaitRelation waits=" << waits->getID() << " foe=" << reason->getID() << "\n";
     111       152137 :     myWaitRelations[waits] = WaitRelation(rs, reason, constraint);
     112       152137 : }
     113              : 
     114              : 
     115              : bool
     116        13241 : MSRailSignalControl::haveDeadlock(const SUMOVehicle* veh) const {
     117              :     std::set<const SUMOVehicle*> seen;
     118              :     std::vector<WaitRelation> list;
     119        13241 :     const SUMOVehicle* cur = veh;
     120              :     std::vector<MSRailSignalConstraint*> constraints;
     121              :     std::vector<const SUMOVehicle*> constraintBlocked;
     122              :     std::vector<const MSRailSignal*> constraintSignals;
     123              :     //std::cout << time2string(SIMSTEP) << " haveDeadlock veh=" << veh->getID() << "\n";
     124              :     while (seen.count(cur) == 0) {
     125              :         auto it = myWaitRelations.find(cur);
     126        26104 :         if (it != myWaitRelations.end()) {
     127        12917 :             if (it->second.constraint != nullptr) {
     128           69 :                 constraints.push_back(it->second.constraint);
     129           69 :                 constraintBlocked.push_back(cur);
     130           69 :                 constraintSignals.push_back(it->second.railSignal);
     131              :             }
     132              :             seen.insert(cur);
     133        12917 :             list.push_back(it->second);
     134        12917 :             cur = it->second.foe;
     135              :         } else {
     136              :             return false;
     137              :         }
     138              :     }
     139           54 :     if (cur == veh) {
     140              :         const bool newDeadlock = myWrittenDeadlocks.count(seen) == 0;
     141              :         myWrittenDeadlocks.insert(seen);
     142           54 :         const OptionsCont& oc = OptionsCont::getOptions();
     143              :         MSRailSignalConstraint* resolved = nullptr;
     144              :         const SUMOVehicle* resolvedUnblocked = nullptr;
     145              :         const MSRailSignal* resolvedSignal = nullptr;
     146           90 :         if (!constraints.empty() && oc.getBool("time-to-teleport.remove-constraint")) {
     147           21 :             resolved = constraints.front();
     148           21 :             if (newDeadlock) {
     149              :                 std::vector<std::string> vehicles;
     150           54 :                 for (auto item : list) {
     151           36 :                     vehicles.push_back(item.foe->getID());
     152              :                 }
     153           36 :                 WRITE_WARNINGF("Deactivating constraint to resolve deadlock between vehicles % at time %.", toString(vehicles), time2string(SIMSTEP));
     154           18 :                 resolved->setActive(false);
     155           18 :                 resolvedUnblocked = constraintBlocked.front();
     156           18 :                 resolvedSignal = constraintSignals.front();
     157           18 :             }
     158              :         }
     159              : 
     160          108 :         if (oc.isSet("deadlock-output")) {
     161           54 :             if (newDeadlock) {
     162              :                 myWrittenDeadlocks.insert(seen);
     163              :                 std::vector<std::string> signals;
     164              :                 std::vector<std::string> vehicles;
     165              :                 std::vector<std::string> tripIDs;
     166          158 :                 for (auto item : list) {
     167          330 :                     signals.push_back(item.railSignal == nullptr ? "INSERTION" : item.railSignal->getID());
     168          114 :                     vehicles.push_back(item.foe->getID());
     169          342 :                     tripIDs.push_back(item.foe->getParameter().getParameter("tripId", item.foe->getID()));
     170              :                 }
     171           88 :                 OutputDevice& od = OutputDevice::getDeviceByOption("deadlock-output");
     172           44 :                 if (constraints.empty()) {
     173           11 :                     od.openTag(SUMO_TAG_DEADLOCK);
     174              :                 } else {
     175           66 :                     od.openTag("constraintDeadlock");
     176              :                 }
     177           44 :                 od.writeAttr(SUMO_ATTR_TIME, time2string(SIMSTEP));
     178           44 :                 od.writeAttr(SUMO_ATTR_SIGNALS, signals);
     179           88 :                 od.writeAttr("vehicles", vehicles);
     180           44 :                 if (!constraints.empty()) {
     181           66 :                     od.writeAttr("tripIds", tripIDs);
     182              :                 }
     183           44 :                 if (resolved != nullptr) {
     184           36 :                     od.openTag("resolvedConstraint");
     185           18 :                     od.writeAttr(SUMO_ATTR_ID, resolvedSignal->getID());
     186           54 :                     resolved->write(od, resolvedUnblocked->getParameter().getParameter("tripId", resolvedUnblocked->getID()));
     187           36 :                     od.closeTag();
     188              :                 }
     189           44 :                 od.closeTag();
     190              : 
     191           44 :             }
     192              :         }
     193           54 :         return resolved == nullptr;
     194              :     } else {
     195              :         // it's a deadlock but does not involve veh
     196              :         return false;
     197              :     }
     198        13241 : }
     199              : 
     200              : 
     201              : void
     202           15 : MSRailSignalControl::addDeadlockCheck(std::vector<const MSRailSignal*> signals) {
     203           15 :     if (MSDriveWay::haveDriveWays()) {
     204            0 :         WRITE_WARNING("Deadlocks should be loaded before any vehicles");
     205              :     }
     206           15 :     const int n = (int)signals.size();
     207           78 :     for (int i = 0; i < n; i++) {
     208              :         std::vector<const MSRailSignal*> others;
     209          342 :         for (int j = 0; j < n; j++) {
     210          279 :             others.push_back(signals[(i + j + 1) % n]);
     211              :         }
     212           63 :         myDeadlockChecks[signals[i]] = others;
     213           63 :     }
     214           15 : }
     215              : 
     216              : void
     217        33173 : MSRailSignalControl::addDrivewayFollower(const MSDriveWay* dw, const MSDriveWay* dw2) {
     218              :     //std::cout << " addDrivewayFollower " << dw->getID() << " " << dw2->getID() << "\n";
     219        33173 :     myDriveWaySucc[dw].insert(dw2);
     220        33173 :     myDriveWayPred[dw2].insert(dw);
     221        33173 : }
     222              : 
     223              : 
     224              : void
     225        33173 : MSRailSignalControl::addDWDeadlockChecks(const MSRailSignal* rs, MSDriveWay* dw) {
     226        33173 :     auto itDL = MSRailSignalControl::getInstance().getDeadlockChecks().find(rs);
     227        33173 :     if (itDL == MSRailSignalControl::getInstance().getDeadlockChecks().end()) {
     228        33053 :         return;
     229              :     }
     230          120 :     const std::vector<const MSRailSignal*>& others = itDL->second;
     231              :     // the driveway could be part of a deadlock. check whether it would be blocked by the next signal in the circle
     232              :     std::vector<const MSDriveWay*> deadlockFoes;
     233          120 :     findDeadlockFoes(dw, others, deadlockFoes);
     234          120 : }
     235              : 
     236              : 
     237              : void
     238          751 : MSRailSignalControl::findDeadlockFoes(const MSDriveWay* dw, const std::vector<const MSRailSignal*>& others, std::vector<const MSDriveWay*> deadlockFoes) {
     239              : #ifdef DEBUG_BUILD_DEADLOCK_CHECK
     240              :     //std::cout << " findDLfoes dw=" << dw->getID() << " dlFoes=" << toString(deadlockFoes) << "\n";
     241              : #endif
     242          751 :     int circleIndex = (int)deadlockFoes.size();
     243          751 :     if (circleIndex < (int)others.size()) {
     244          571 :         const MSRailSignal* other = others[circleIndex];
     245          571 :         deadlockFoes.push_back(dw);
     246         1062 :         for (const MSDriveWay* follower : myDriveWaySucc[dw]) {
     247         1991 :             for (MSDriveWay* foe : follower->getFoes()) {
     248         1500 :                 if (foe->getForward().back()->getEdge().getToJunction()->getID() == other->getID()) {
     249          631 :                     findDeadlockFoes(foe, others, deadlockFoes);
     250              :                 }
     251              :             }
     252              :         }
     253              :     } else {
     254              : #ifdef DEBUG_BUILD_DEADLOCK_CHECK
     255              :         std::cout << " add deadlock check foes=" << toString(deadlockFoes) << "\n";;
     256              : #endif
     257          860 :         for (const MSDriveWay* dldw : deadlockFoes) {
     258          680 :             if (dldw->isDepartDriveway()) {
     259          233 :                 const_cast<MSDriveWay*>(dldw)->addDWDeadlock(deadlockFoes);
     260              :             } else {
     261          861 :                 for (const MSDriveWay* pred : myDriveWayPred[dldw]) {
     262          414 :                     if (!pred->isDepartDriveway()) {
     263          113 :                         const MSRailSignal* predRS = dynamic_cast<const MSRailSignal*>(pred->getOrigin()->getTLLogic());
     264          113 :                         if (std::find(others.begin(), others.end(), predRS) != others.end()) {
     265              :                             // driveways that participate in the deadlock don't need to
     266              :                             // do checking since they cannot prevent the deadlock
     267              :                             // (unless they are departDriveways)
     268           61 :                             continue;
     269              :                         }
     270           52 :                         const_cast<MSDriveWay*>(pred)->addDWDeadlock(deadlockFoes);
     271              :                     }
     272              :                 }
     273              :             }
     274              :         }
     275              :     }
     276          751 : }
     277              : 
     278              : 
     279              : void
     280       641283 : MSRailSignalControl::notifyApproach(const MSLink* link) {
     281       641283 :     const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(link->getTLLogic());
     282              :     assert(rs != nullptr);
     283       641283 :     myActiveSignals.insert(const_cast<MSRailSignal*>(rs));
     284       641283 : }
     285              : 
     286              : 
     287              : void
     288      8854466 : MSRailSignalControl::updateSignals(SUMOTime t) {
     289              :     UNUSED_PARAMETER(t);
     290              :     // there are 4 states for a signal
     291              :     // 1. approached and green
     292              :     // 2. approached and red
     293              :     // 3. not approached and trains could pass
     294              :     // 4. not approached and trains coult not pass
     295              :     //
     296              :     // for understanding conflicts better in sumo-gui, we want to show (3) as green. This
     297              :     // means we have to keep updating signals in state (4) until they change to (3)
     298              : 
     299              :     //std::cout << SIMTIME << " activeSignals=" << myActiveSignals.size() << "\n";
     300     26933355 :     for (auto it = myActiveSignals.begin(); it != myActiveSignals.end();) {
     301     18078889 :         MSRailSignal* rs = *it;
     302              :         //std::cout << SIMTIME << " update " << rs->getID() << "\n";
     303     18078889 :         const bool keepActive = rs->updateCurrentPhase();
     304     18078889 :         if (rs->isActive()) {
     305     18078874 :             rs->setTrafficLightSignals(t);
     306              :         }
     307     18078889 :         if (!keepActive) {
     308              :             it = myActiveSignals.erase(it);
     309              :         } else {
     310              :             it++;
     311              :         }
     312              :     }
     313      8854466 : }
     314              : 
     315              : 
     316              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1