LCOV - code coverage report
Current view: top level - src/netbuild - NBLoadedSUMOTLDef.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 87.5 % 441 386
Test Date: 2025-11-13 15:38:19 Functions: 87.8 % 41 36

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2011-2025 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    NBLoadedSUMOTLDef.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Michael Behrisch
      17              : /// @author  Jakob Erdmann
      18              : /// @date    Mar 2011
      19              : ///
      20              : // A complete traffic light logic loaded from a sumo-net. (opted to reimplement
      21              : // since NBLoadedTLDef is quite vissim specific)
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : 
      25              : #include <vector>
      26              : #include <set>
      27              : #include <cassert>
      28              : #include <iterator>
      29              : #include <utils/common/MsgHandler.h>
      30              : #include <utils/common/ToString.h>
      31              : #include <utils/options/OptionsCont.h>
      32              : #include "NBTrafficLightLogic.h"
      33              : #include "NBOwnTLDef.h"
      34              : #include "NBTrafficLightDefinition.h"
      35              : #include "NBLoadedSUMOTLDef.h"
      36              : #include "NBNetBuilder.h"
      37              : #include "NBOwnTLDef.h"
      38              : #include "NBNode.h"
      39              : 
      40              : //#define DEBUG_RECONSTRUCTION
      41              : 
      42              : // ===========================================================================
      43              : // method definitions
      44              : // ===========================================================================
      45              : 
      46          768 : NBLoadedSUMOTLDef::NBLoadedSUMOTLDef(const std::string& id, const std::string& programID,
      47          768 :                                      SUMOTime offset, TrafficLightType type) :
      48              :     NBTrafficLightDefinition(id, programID, offset, type),
      49          768 :     myTLLogic(nullptr),
      50          768 :     myReconstructAddedConnections(false),
      51          768 :     myReconstructRemovedConnections(false),
      52          768 :     myPhasesLoaded(false) {
      53          768 :     myTLLogic = new NBTrafficLightLogic(id, programID, 0, offset, type);
      54          768 : }
      55              : 
      56              : 
      57           13 : NBLoadedSUMOTLDef::NBLoadedSUMOTLDef(const NBTrafficLightDefinition& def, const NBTrafficLightLogic& logic) :
      58              :     // allow for adding a new program for the same def: take the offset and programID from the new logic
      59              :     NBTrafficLightDefinition(def.getID(), logic.getProgramID(), logic.getOffset(), def.getType()),
      60           13 :     myTLLogic(new NBTrafficLightLogic(logic)),
      61           13 :     myReconstructAddedConnections(false),
      62           13 :     myReconstructRemovedConnections(false),
      63           26 :     myPhasesLoaded(false) {
      64              :     assert(def.getType() == logic.getType());
      65           13 :     myControlledLinks = def.getControlledLinks();
      66           13 :     myControlledNodes = def.getNodes();
      67           13 :     const NBLoadedSUMOTLDef* sumoDef = dynamic_cast<const NBLoadedSUMOTLDef*>(&def);
      68           13 :     updateParameters(def.getParametersMap());
      69           13 :     if (sumoDef != nullptr) {
      70           11 :         myReconstructAddedConnections = sumoDef->myReconstructAddedConnections;
      71           11 :         myReconstructRemovedConnections = sumoDef->myReconstructRemovedConnections;
      72              :     }
      73           13 : }
      74              : 
      75              : 
      76         1562 : NBLoadedSUMOTLDef::~NBLoadedSUMOTLDef() {
      77          781 :     delete myTLLogic;
      78         1562 : }
      79              : 
      80              : 
      81              : NBTrafficLightLogic*
      82          840 : NBLoadedSUMOTLDef::myCompute(int brakingTimeSeconds) {
      83              :     // @todo what to do with those parameters?
      84              :     UNUSED_PARAMETER(brakingTimeSeconds);
      85          840 :     initExtraConflicts();
      86          840 :     reconstructLogic();
      87          840 :     myTLLogic->closeBuilding(false);
      88          840 :     patchIfCrossingsAdded();
      89          840 :     myTLLogic->closeBuilding();
      90          840 :     return new NBTrafficLightLogic(myTLLogic);
      91              : }
      92              : 
      93              : 
      94              : void
      95         7278 : NBLoadedSUMOTLDef::addConnection(NBEdge* from, NBEdge* to, int fromLane, int toLane, int linkIndex, int linkIndex2, bool reconstruct) {
      96              :     assert(myTLLogic->getNumLinks() > 0); // logic should be loaded by now
      97         7278 :     if (linkIndex >= myTLLogic->getNumLinks()) {
      98            0 :         throw ProcessError("Invalid linkIndex " + toString(linkIndex) + " in connection from edge '" + from->getID() +
      99            0 :                            "' to edge '" + to->getID() + "' for traffic light '" + getID() +
     100            0 :                            "' with " + toString(myTLLogic->getNumLinks()) + " links.");
     101              :     }
     102         7278 :     if (linkIndex2 >= myTLLogic->getNumLinks()) {
     103            0 :         throw ProcessError("Invalid linkIndex2 " + toString(linkIndex2) + " in connection from edge '" + from->getID() +
     104            0 :                            "' to edge '" + to->getID() + "' for traffic light '" + getID() +
     105            0 :                            "' with " + toString(myTLLogic->getNumLinks()) + " links.");
     106              :     }
     107         7278 :     NBConnection conn(from, fromLane, to, toLane, linkIndex, linkIndex2);
     108              :     // avoid duplicates
     109         7278 :     auto newEnd = remove_if(myControlledLinks.begin(), myControlledLinks.end(), connection_equal(conn));
     110              :     // remove_if does not remove, only re-order
     111         7278 :     myControlledLinks.erase(newEnd, myControlledLinks.end());
     112         7278 :     myControlledLinks.push_back(conn);
     113         7278 :     addNode(from->getToNode());
     114         7278 :     addNode(to->getFromNode());
     115              :     // added connections are definitely controlled. make sure none are removed because they lie within the tl
     116              :     // myControlledInnerEdges.insert(from->getID()); // @todo recheck: this appears to be obsolete
     117              :     // set this information now so that it can be used while loading diffs
     118         7278 :     from->setControllingTLInformation(conn, getID());
     119         7278 :     myReconstructAddedConnections |= reconstruct;
     120         7278 : }
     121              : 
     122              : void
     123           74 : NBLoadedSUMOTLDef::setID(const std::string& newID) {
     124              :     Named::setID(newID);
     125           74 :     myTLLogic->setID(newID);
     126           74 : }
     127              : 
     128              : void
     129            0 : NBLoadedSUMOTLDef::setProgramID(const std::string& programID) {
     130              :     NBTrafficLightDefinition::setProgramID(programID);
     131            0 :     myTLLogic->setProgramID(programID);
     132            0 : }
     133              : 
     134              : 
     135              : void
     136          784 : NBLoadedSUMOTLDef::setTLControllingInformation() const {
     137          784 :     if (myReconstructAddedConnections) {
     138            5 :         NBOwnTLDef dummy(DummyID, myControlledNodes, 0, getType());
     139            5 :         dummy.setParticipantsInformation();
     140            5 :         dummy.setTLControllingInformation();
     141           10 :         for (NBNode* const n : myControlledNodes) {
     142            5 :             n->removeTrafficLight(&dummy);
     143              :         }
     144            5 :     }
     145          784 :     if (myReconstructRemovedConnections) {
     146              :         return; // will be called again in reconstructLogic()
     147              :     }
     148              :     // if nodes have been removed our links may have been invalidated as well
     149              :     // since no logic will be built anyway there is no reason to inform any edges
     150          758 :     if (amInvalid()) {
     151              :         return;
     152              :     }
     153              :     // set the information about the link's positions within the tl into the
     154              :     //  edges the links are starting at, respectively
     155         8078 :     for (const NBConnection& c : myControlledLinks) {
     156         7328 :         if (c.getTLIndex() >= myTLLogic->getNumLinks()) {
     157            2 :             throw ProcessError("Invalid linkIndex " + toString(c.getTLIndex()) + " for traffic light '" + getID() +
     158            4 :                                "' with " + toString(myTLLogic->getNumLinks()) + " links.");
     159              :         }
     160         7327 :         NBEdge* edge = c.getFrom();
     161         7327 :         if (edge != nullptr && edge->getNumLanes() > c.getFromLane()) {
     162              :             // logic may have yet to be reconstructed
     163         7327 :             edge->setControllingTLInformation(c, getID());
     164              :         }
     165              :     }
     166              : }
     167              : 
     168              : 
     169              : void
     170            0 : NBLoadedSUMOTLDef::remapRemoved(NBEdge*, const EdgeVector&, const EdgeVector&) {}
     171              : 
     172              : 
     173              : void
     174           94 : NBLoadedSUMOTLDef::replaceRemoved(NBEdge* removed, int removedLane, NBEdge* by, int byLane, bool incoming) {
     175           94 :     if (by == nullptr) {
     176           45 :         myReconstructRemovedConnections = true;
     177              :     }
     178         1177 :     for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); ++it) {
     179         1083 :         if (incoming) {
     180          727 :             (*it).replaceFrom(removed, removedLane, by, byLane);
     181              :         } else {
     182          356 :             (*it).replaceTo(removed, removedLane, by, byLane);
     183              :         }
     184              :     }
     185           94 : }
     186              : 
     187              : 
     188              : void
     189         4757 : NBLoadedSUMOTLDef::addPhase(const SUMOTime duration, const std::string& state, const SUMOTime minDur, const SUMOTime maxDur,
     190              :                             const SUMOTime earliestEnd, const SUMOTime latestEnd, const SUMOTime vehExt, const SUMOTime yellow,
     191              :                             const SUMOTime red, const std::vector<int>& next, const std::string& name) {
     192         4757 :     myTLLogic->addStep(duration, state, minDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, name, next);
     193         4756 : }
     194              : 
     195              : 
     196              : bool
     197        52769 : NBLoadedSUMOTLDef::amInvalid() const {
     198        52769 :     if (myControlledLinks.size() == 0) {
     199              :         return true;
     200              :     }
     201        52764 :     if (myIncomingEdges.size() == 0) {
     202              :         return true;
     203              :     }
     204        52762 :     if (myReconstructRemovedConnections) {
     205              :         // check whether at least one connection is valid
     206           19 :         for (const NBConnection& con : myControlledLinks) {
     207           18 :             if (isValid(con)) {
     208              :                 return false;
     209              :             }
     210              :         }
     211              :         // all invalid
     212              :         return true;
     213              :     }
     214              :     return false;
     215              : }
     216              : 
     217              : 
     218              : void
     219          145 : NBLoadedSUMOTLDef::removeConnection(const NBConnection& conn, bool reconstruct) {
     220         1377 :     for (auto it = myControlledLinks.begin(); it != myControlledLinks.end();) {
     221         1625 :         if ((it->getFrom() == conn.getFrom() &&
     222          518 :                 it->getTo() == conn.getTo() &&
     223          214 :                 it->getFromLane() == conn.getFromLane() &&
     224           89 :                 it->getToLane() == conn.getToLane())
     225         1543 :                 || (it->getTLIndex() == conn.getTLIndex() &&
     226           18 :                     conn.getTLIndex() != conn.InvalidTlIndex &&
     227           14 :                     (it->getFrom() == nullptr || it->getTo() == nullptr))) {
     228           91 :             if (reconstruct) {
     229           60 :                 myReconstructRemovedConnections = true;
     230              :                 it++;
     231              :             } else {
     232           31 :                 it = myControlledLinks.erase(it);
     233              :             }
     234              :         } else {
     235              :             it++;
     236              :         }
     237              :     }
     238          145 : }
     239              : 
     240              : 
     241              : void
     242            6 : NBLoadedSUMOTLDef::setOffset(SUMOTime offset) {
     243            6 :     myOffset = offset;
     244            6 :     myTLLogic->setOffset(offset);
     245            6 : }
     246              : 
     247              : 
     248              : void
     249            7 : NBLoadedSUMOTLDef::setType(TrafficLightType type) {
     250            7 :     myType = type;
     251            7 :     myTLLogic->setType(type);
     252            7 : }
     253              : 
     254              : 
     255              : void
     256          761 : NBLoadedSUMOTLDef::collectEdges() {
     257          761 :     if (myControlledLinks.size() == 0) {
     258           15 :         NBTrafficLightDefinition::collectEdges();
     259              :     }
     260              :     myIncomingEdges.clear();
     261              :     EdgeVector myOutgoing;
     262              :     // collect the edges from the participating nodes
     263         1731 :     for (std::vector<NBNode*>::iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
     264          970 :         const EdgeVector& incoming = (*i)->getIncomingEdges();
     265          970 :         copy(incoming.begin(), incoming.end(), back_inserter(myIncomingEdges));
     266          970 :         const EdgeVector& outgoing = (*i)->getOutgoingEdges();
     267              :         copy(outgoing.begin(), outgoing.end(), back_inserter(myOutgoing));
     268              :     }
     269              :     // check which of the edges are completely within the junction
     270              :     // and which are uncontrolled as well (we already know myControlledLinks)
     271         3170 :     for (EdgeVector::iterator j = myIncomingEdges.begin(); j != myIncomingEdges.end();) {
     272         2409 :         NBEdge* edge = *j;
     273              :         edge->setInsideTLS(false); // reset
     274              :         // an edge lies within the logic if it is outgoing as well as incoming
     275         2409 :         EdgeVector::iterator k = std::find(myOutgoing.begin(), myOutgoing.end(), edge);
     276         2409 :         if (k != myOutgoing.end()) {
     277          186 :             if (myControlledInnerEdges.count(edge->getID()) == 0) {
     278              :                 bool controlled = false;
     279         1158 :                 for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
     280         1154 :                     if ((*it).getFrom() == edge) {
     281              :                         controlled = true;
     282              :                         break;
     283              :                     }
     284              :                 }
     285          186 :                 if (controlled) {
     286          182 :                     myControlledInnerEdges.insert(edge->getID());
     287              :                 } else {
     288            4 :                     myEdgesWithin.push_back(edge);
     289              :                     edge->setInsideTLS(true);
     290              :                     ++j; //j = myIncomingEdges.erase(j);
     291            4 :                     continue;
     292              :                 }
     293              :             }
     294              :         }
     295              :         ++j;
     296              :     }
     297          761 : }
     298              : 
     299              : 
     300              : void
     301          761 : NBLoadedSUMOTLDef::collectLinks() {
     302          761 :     if (myControlledLinks.size() == 0) {
     303              :         // maybe we only loaded a different program for a default traffic light.
     304              :         // Try to build links now.
     305           15 :         collectAllLinks(myControlledLinks);
     306              :     }
     307          761 : }
     308              : 
     309              : 
     310              : /// @brief patches signal plans by modifying lane indices
     311              : void
     312           16 : NBLoadedSUMOTLDef::shiftTLConnectionLaneIndex(NBEdge* edge, int offset, int threshold) {
     313              :     // avoid shifting twice if the edge is incoming and outgoing to a joined TLS
     314              :     if (myShifted.count(edge) == 0) {
     315              :         /// XXX what if an edge should really be shifted twice?
     316              :         myShifted.insert(edge);
     317          224 :         for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
     318          208 :             (*it).shiftLaneIndex(edge, offset, threshold);
     319              :         }
     320              :     }
     321           16 : }
     322              : 
     323              : void
     324          840 : NBLoadedSUMOTLDef::patchIfCrossingsAdded() {
     325          840 :     const int size = myTLLogic->getNumLinks();
     326              :     int noLinksAll = 0;
     327         9658 :     for (NBConnectionVector::const_iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
     328              :         const NBConnection& c = *it;
     329         8818 :         if (c.getTLIndex() != NBConnection::InvalidTlIndex) {
     330         8818 :             noLinksAll = MAX2(noLinksAll, (int)c.getTLIndex() + 1);
     331              :         }
     332              :     }
     333              :     const int numNormalLinks = noLinksAll;
     334              :     int oldCrossings = 0;
     335              :     // collect crossings
     336              :     bool customIndex = false;
     337              :     std::vector<NBNode::Crossing*> crossings;
     338         1911 :     for (std::vector<NBNode*>::iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
     339         1071 :         const std::vector<NBNode::Crossing*>& c = (*i)->getCrossings();
     340              :         // set tl indices for crossings
     341         1071 :         customIndex |= (*i)->setCrossingTLIndices(getID(), noLinksAll);
     342              :         copy(c.begin(), c.end(), std::back_inserter(crossings));
     343         1071 :         noLinksAll += (int)c.size();
     344         1071 :         oldCrossings += (*i)->numCrossingsFromSumoNet();
     345         1071 :     }
     346          840 :     if ((int)crossings.size() != oldCrossings) {
     347           15 :         std::vector<NBTrafficLightLogic::PhaseDefinition> phases = myTLLogic->getPhases();
     348              :         // do not rebuilt crossing states there are custom indices and the state string is long enough
     349           15 :         if (phases.size() > 0 && (
     350           15 :                     (int)(phases.front().state.size()) < noLinksAll ||
     351            4 :                     ((int)(phases.front().state.size()) > noLinksAll && !customIndex))) {
     352              :             // collect edges
     353            6 :             EdgeVector fromEdges(size, (NBEdge*)nullptr);
     354            6 :             EdgeVector toEdges(size, (NBEdge*)nullptr);
     355            6 :             std::vector<int> fromLanes(size, 0);
     356            6 :             collectEdgeVectors(fromEdges, toEdges, fromLanes);
     357              :             const std::string crossingDefaultState(crossings.size(), 'r');
     358              : 
     359              :             // rebuild the logic (see NBOwnTLDef.cpp::myCompute)
     360            6 :             NBTrafficLightLogic* newLogic = new NBTrafficLightLogic(getID(), getProgramID(), 0, myOffset, myType);
     361            6 :             SUMOTime brakingTime = TIME2STEPS(computeBrakingTime(OptionsCont::getOptions().getFloat("tls.yellow.min-decel")));
     362              :             //std::cout << "patchIfCrossingsAdded for " << getID() << " numPhases=" << phases.size() << "\n";
     363           50 :             for (const auto& phase : phases) {
     364           88 :                 const std::string state = phase.state.substr(0, numNormalLinks) + crossingDefaultState;
     365           88 :                 NBOwnTLDef::addPedestrianPhases(newLogic, phase.duration, phase.minDur, phase.maxDur, phase.earliestEnd, phase.latestEnd,
     366              :                                                 state, crossings, fromEdges, toEdges);
     367              :             }
     368            6 :             NBOwnTLDef::addPedestrianScramble(newLogic, noLinksAll, TIME2STEPS(10), brakingTime, crossings, fromEdges, toEdges);
     369              : 
     370            6 :             delete myTLLogic;
     371            6 :             myTLLogic = newLogic;
     372           15 :         } else if (phases.size() == 0) {
     373            0 :             WRITE_WARNINGF(TL("Could not patch tlLogic '%' for changed crossings"), getID());
     374              :         }
     375           15 :     }
     376          840 : }
     377              : 
     378              : 
     379              : void
     380            6 : NBLoadedSUMOTLDef::collectEdgeVectors(EdgeVector& fromEdges, EdgeVector& toEdges, std::vector<int>& fromLanes) const {
     381              :     assert(fromEdges.size() > 0);
     382              :     assert(fromEdges.size() == toEdges.size());
     383            6 :     const int size = (int)fromEdges.size();
     384              : 
     385           72 :     for (NBConnectionVector::const_iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
     386              :         const NBConnection& c = *it;
     387           66 :         if (c.getTLIndex() != NBConnection::InvalidTlIndex) {
     388           66 :             if (c.getTLIndex() >= size) {
     389            0 :                 throw ProcessError("Invalid linkIndex " + toString(c.getTLIndex()) + " for traffic light '" + getID() +
     390            0 :                                    "' with " + toString(size) + " links.");
     391              :             }
     392           66 :             fromEdges[c.getTLIndex()] = c.getFrom();
     393           66 :             toEdges[c.getTLIndex()] = c.getTo();
     394           66 :             fromLanes[c.getTLIndex()] = c.getFromLane();
     395              :         }
     396              :     }
     397            6 : }
     398              : 
     399              : 
     400              : void
     401          162 : NBLoadedSUMOTLDef::initNeedsContRelation() const {
     402          162 :     if (!amInvalid() && !myNeedsContRelationReady) {
     403              :         myNeedsContRelation.clear();
     404              :         myExtraConflicts.clear();
     405          162 :         if (myType == TrafficLightType::NEMA) {
     406            3 :             NBTrafficLightDefinition::initNeedsContRelation();
     407            3 :             NBTrafficLightDefinition::initExtraConflicts();
     408              :         } else {
     409          159 :             const bool controlledWithin = !OptionsCont::getOptions().getBool("tls.uncontrolled-within");
     410          159 :             const std::vector<NBTrafficLightLogic::PhaseDefinition> phases = myTLLogic->getPhases();
     411         1152 :             for (std::vector<NBTrafficLightLogic::PhaseDefinition>::const_iterator it = phases.begin(); it != phases.end(); it++) {
     412              :                 const std::string state = (*it).state;
     413        15489 :                 for (NBConnectionVector::const_iterator it1 = myControlledLinks.begin(); it1 != myControlledLinks.end(); it1++) {
     414              :                     const NBConnection& c1 = *it1;
     415              :                     const int i1 = c1.getTLIndex();
     416        14496 :                     if (i1 == NBConnection::InvalidTlIndex || (state[i1] != 'g' && state[i1] != 's') || c1.getFrom() == nullptr || c1.getTo() == nullptr) {
     417              :                         continue;
     418              :                     }
     419        25803 :                     for (NBConnectionVector::const_iterator it2 = myControlledLinks.begin(); it2 != myControlledLinks.end(); it2++) {
     420              :                         const NBConnection& c2 = *it2;
     421              :                         const int i2 = c2.getTLIndex();
     422              :                         if (i2 != NBConnection::InvalidTlIndex
     423        24412 :                                 && i2 != i1
     424        22853 :                                 && (state[i2] == 'G' || state[i2] == 'g')
     425        32689 :                                 && c2.getFrom() != nullptr && c2.getTo() != nullptr) {
     426        24831 :                             const bool rightTurnConflict = NBNode::rightTurnConflict(
     427        16554 :                                                                c1.getFrom(), c1.getTo(), c1.getFromLane(), c2.getFrom(), c2.getTo(), c2.getFromLane());
     428         8277 :                             const bool forbidden = forbids(c2.getFrom(), c2.getTo(), c1.getFrom(), c1.getTo(), true, controlledWithin);
     429         8277 :                             const bool isFoes = foes(c2.getFrom(), c2.getTo(), c1.getFrom(), c1.getTo()) && !c2.getFrom()->isTurningDirectionAt(c2.getTo());
     430         8277 :                             const bool hasContRel = (forbidden && state[i1] != 's') || rightTurnConflict;
     431              :                             if (hasContRel) {
     432         3518 :                                 myNeedsContRelation.insert(StreamPair(c1.getFrom(), c1.getTo(), c2.getFrom(), c2.getTo()));
     433              :                             }
     434         8277 :                             const bool indirectLeft = c1.getFrom()->getConnection(c1.getFromLane(), c1.getTo(), c1.getToLane()).indirectLeft;
     435         8277 :                             if (isFoes && (state[i1] == 's' || (!hasContRel && state[i2] == 'G' && !indirectLeft))) {
     436           32 :                                 myExtraConflicts.insert(std::make_pair(i1, i2));
     437              :                                 //std::cout << getID() << " prog=" << getProgramID() << " phase=" << (it - phases.begin()) << " extraConflict i1=" << i1 << " i2=" << i2
     438              :                                 //    << " c1=" << c1 << " c2=" << c2 << "\n";
     439              :                             }
     440              :                             //std::cout << getID() << " p=" << (it - phases.begin()) << " i1=" << i1 << " i2=" << i2 << " rightTurnConflict=" << rightTurnConflict << " forbidden=" << forbidden << " isFoes=" << isFoes << "\n";
     441              :                         }
     442              :                     }
     443              :                 }
     444              :             }
     445          159 :         }
     446              :     }
     447          162 :     myNeedsContRelationReady = true;
     448          162 :     myExtraConflictsReady = true;
     449          162 : }
     450              : 
     451              : 
     452              : bool
     453        51005 : NBLoadedSUMOTLDef::extraConflict(int index, int foeIndex) const {
     454        51005 :     if (amInvalid()) {
     455              :         return false;
     456              :     }
     457        51005 :     if (!myExtraConflictsReady) {
     458            0 :         initExtraConflicts();
     459              :         assert(myExtraConflictsReady);
     460              :     }
     461        51005 :     return std::find(myExtraConflicts.begin(), myExtraConflicts.end(), std::make_pair(index, foeIndex)) != myExtraConflicts.end();
     462              : }
     463              : 
     464              : 
     465              : void
     466           12 : NBLoadedSUMOTLDef::registerModifications(bool addedConnections, bool removedConnections) {
     467           12 :     myReconstructAddedConnections |= addedConnections;
     468           12 :     myReconstructRemovedConnections |= removedConnections;
     469           12 : }
     470              : 
     471              : bool
     472          194 : NBLoadedSUMOTLDef::isValid(const NBConnection& con) const {
     473              :     return (// edge still exists
     474          194 :                std::find(myIncomingEdges.begin(), myIncomingEdges.end(), con.getFrom()) != myIncomingEdges.end()
     475              :                // connection still exists
     476          188 :                && con.getFrom()->hasConnectionTo(con.getTo(), con.getToLane(), con.getFromLane())
     477              :                // connection is still set to be controlled
     478          365 :                && con.getFrom()->mayBeTLSControlled(con.getFromLane(), con.getTo(), con.getToLane()));
     479              : }
     480              : 
     481              : void
     482          840 : NBLoadedSUMOTLDef::reconstructLogic() {
     483          840 :     const bool netedit = NBNetBuilder::runningNetedit();
     484              : #ifdef DEBUG_RECONSTRUCTION
     485              :     bool debugPrintModified = myReconstructAddedConnections || myReconstructRemovedConnections;
     486              :     std::cout << getID() << " reconstructLogic added=" << myReconstructAddedConnections
     487              :               << " removed=" << myReconstructRemovedConnections
     488              :               << " valid=" << hasValidIndices()
     489              :               << " phasesLoaded=" << myPhasesLoaded
     490              :               << " oldLinks:\n";
     491              :     for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); ++it) {
     492              :         std::cout << "    " << *it << "\n";
     493              :     }
     494              : #endif
     495          840 :     if (myReconstructAddedConnections) {
     496            5 :         myReconstructAddedConnections = false;
     497              :         // do not rebuild the logic when running netedit and all links are already covered by the program
     498            5 :         if (!myPhasesLoaded && !(netedit && hasValidIndices())) {
     499              :             // rebuild the logic from scratch
     500              :             // XXX if a connection with the same from- and to-edge already exisits, its states could be copied instead
     501            3 :             NBOwnTLDef dummy(DummyID, myControlledNodes, 0, getType());
     502            3 :             dummy.setParticipantsInformation();
     503              :             dummy.setProgramID(getProgramID());
     504            3 :             dummy.setTLControllingInformation();
     505            3 :             NBTrafficLightLogic* newLogic = dummy.compute(OptionsCont::getOptions());
     506            3 :             myIncomingEdges = dummy.getIncomingEdges();
     507            3 :             myControlledLinks = dummy.getControlledLinks();
     508            6 :             for (std::vector<NBNode*>::const_iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
     509            3 :                 (*i)->removeTrafficLight(&dummy);
     510              :             }
     511            3 :             delete myTLLogic;
     512            3 :             myTLLogic = newLogic;
     513            3 :             if (newLogic != nullptr) {
     514            3 :                 newLogic->setID(getID());
     515              :                 newLogic->setType(getType());
     516              :                 newLogic->setOffset(getOffset());
     517            3 :                 setTLControllingInformation();
     518              :                 // reset crossing custom indices
     519            6 :                 for (NBNode* n : myControlledNodes) {
     520            3 :                     for (NBNode::Crossing* c : n->getCrossings()) {
     521            0 :                         c->customTLIndex = NBConnection::InvalidTlIndex;
     522            3 :                     }
     523              :                 }
     524              : 
     525              :             }
     526            3 :         } else {
     527            2 :             setTLControllingInformation();
     528              :         }
     529              :     }
     530          840 :     if (myReconstructRemovedConnections) {
     531           15 :         myReconstructRemovedConnections = false;
     532              :         // for each connection, check whether it is still valid
     533          191 :         for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end();) {
     534          176 :             const NBConnection con = (*it);
     535          176 :             if (isValid(con)) {
     536              :                 it++;
     537              :             } else {
     538              :                 // remove connection
     539              :                 const int removed = con.getTLIndex();
     540           20 :                 it = myControlledLinks.erase(it);
     541              :                 // no automatic modificaions when running netedit
     542           20 :                 if (!myPhasesLoaded && !(netedit && hasValidIndices())) {
     543              :                     // shift index off successive connections and remove entry from all phases if the tlIndex was only used by this connection
     544              :                     bool exclusive = true;
     545          169 :                     for (NBConnection& other : myControlledLinks) {
     546          159 :                         if (other != con && other.getTLIndex() == removed) {
     547              :                             exclusive = false;
     548              :                             break;
     549              :                         }
     550              :                     }
     551           20 :                     if (exclusive) {
     552              :                         // shift indices above the removed index downward
     553          132 :                         for (NBConnection& other : myControlledLinks) {
     554          122 :                             if (other.getTLIndex() > removed) {
     555           33 :                                 other.setTLIndex(other.getTLIndex() - 1);
     556              :                             }
     557              :                         }
     558              :                         // shift crossing custom indices above the removed index downward
     559           20 :                         for (NBNode* n : myControlledNodes) {
     560           10 :                             for (NBNode::Crossing* c : n->getCrossings()) {
     561            0 :                                 if (c->customTLIndex > removed) {
     562            0 :                                     c->customTLIndex--;
     563              :                                 }
     564           10 :                             }
     565              :                         }
     566              :                         // rebuild the logic
     567           10 :                         NBTrafficLightLogic* newLogic = new NBTrafficLightLogic(getID(), getProgramID(), 0, myOffset, myType);
     568           76 :                         for (const NBTrafficLightLogic::PhaseDefinition& phase : myTLLogic->getPhases()) {
     569              :                             std::string newState = phase.state;
     570              :                             newState.erase(newState.begin() + removed);
     571           66 :                             newLogic->addStep(phase.duration, newState);
     572              :                         }
     573           10 :                         delete myTLLogic;
     574           10 :                         myTLLogic = newLogic;
     575              :                     }
     576              :                 }
     577              :             }
     578          176 :         }
     579           15 :         setTLControllingInformation();
     580              :     }
     581              : #ifdef DEBUG_RECONSTRUCTION
     582              :     if (debugPrintModified) {
     583              :         std::cout << " newLinks:\n";
     584              :         for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); ++it) {
     585              :             std::cout << "    " << *it << "\n";
     586              :         }
     587              :     }
     588              : #endif
     589          840 : }
     590              : 
     591              : 
     592              : int
     593            4 : NBLoadedSUMOTLDef::getMaxIndex() {
     594              :     int maxIndex = -1;
     595           59 :     for (const NBConnection& c : myControlledLinks) {
     596              :         maxIndex = MAX2(maxIndex, c.getTLIndex());
     597              :         maxIndex = MAX2(maxIndex, c.getTLIndex2());
     598              :     }
     599            8 :     for (NBNode* n : myControlledNodes) {
     600            6 :         for (NBNode::Crossing* c : n->getCrossings()) {
     601            2 :             maxIndex = MAX2(maxIndex, c->tlLinkIndex);
     602            2 :             maxIndex = MAX2(maxIndex, c->tlLinkIndex2);
     603            4 :         }
     604              :     }
     605            4 :     return maxIndex;
     606              : }
     607              : 
     608              : 
     609              : int
     610            4 : NBLoadedSUMOTLDef::getMaxValidIndex() {
     611            4 :     return myTLLogic->getNumLinks() - 1;
     612              : }
     613              : 
     614              : 
     615              : bool
     616            0 : NBLoadedSUMOTLDef::hasValidIndices() const {
     617            0 :     for (const NBConnection& c : myControlledLinks) {
     618            0 :         if (c.getTLIndex() == NBConnection::InvalidTlIndex) {
     619              :             return false;
     620              :         }
     621              :     }
     622            0 :     for (NBNode* n : myControlledNodes) {
     623            0 :         for (NBNode::Crossing* c : n->getCrossings()) {
     624            0 :             if (c->tlLinkIndex == NBConnection::InvalidTlIndex) {
     625              :                 return false;
     626              :             }
     627            0 :         }
     628              :     }
     629              :     // method getMaxIndex() is const but cannot be declare as such due to inheritance
     630            0 :     return const_cast<NBLoadedSUMOTLDef*>(this)->getMaxIndex() < myTLLogic->getNumLinks();
     631              : }
     632              : 
     633              : 
     634              : std::string
     635          268 : NBLoadedSUMOTLDef::getStates(int index) {
     636              :     assert(index >= 0);
     637              :     assert(index <= getMaxValidIndex());
     638              :     std::string result;
     639         2082 :     for (auto& pd : myTLLogic->getPhases()) {
     640         1814 :         result += pd.state[index];
     641              :     }
     642          268 :     return result;
     643              : }
     644              : 
     645              : bool
     646         7095 : NBLoadedSUMOTLDef::isUsed(int index) const {
     647        49310 :     for (const NBConnection& c : myControlledLinks) {
     648        49173 :         if (c.getTLIndex() == index || c.getTLIndex2() == index) {
     649              :             return true;
     650              :         }
     651              :     }
     652          183 :     for (NBNode* n : myControlledNodes) {
     653          274 :         for (NBNode::Crossing* c : n->getCrossings()) {
     654          228 :             if (c->tlLinkIndex == index || c->tlLinkIndex2 == index) {
     655              :                 return true;
     656              :             }
     657          138 :         }
     658              :     }
     659              :     return false;
     660              : }
     661              : 
     662              : std::set<const NBEdge*>
     663           78 : NBLoadedSUMOTLDef::getEdgesUsingIndex(int index) const {
     664              :     std::set<const NBEdge*> result;
     665         1304 :     for (const NBConnection& c : myControlledLinks) {
     666         1226 :         if (c.getTLIndex() == index || c.getTLIndex2() == index) {
     667           74 :             result.insert(c.getFrom());
     668              :         }
     669              :     }
     670           78 :     return result;
     671              : }
     672              : 
     673              : 
     674              : void
     675           59 : NBLoadedSUMOTLDef::replaceIndex(int oldIndex, int newIndex) {
     676           59 :     if (oldIndex == newIndex) {
     677              :         return;
     678              :     }
     679          913 :     for (NBConnection& c : myControlledLinks) {
     680          858 :         if (c.getTLIndex() == oldIndex) {
     681              :             c.setTLIndex(newIndex);
     682              :         }
     683          858 :         if (c.getTLIndex2() == oldIndex) {
     684              :             c.setTLIndex2(newIndex);
     685              :         }
     686              :     }
     687          110 :     for (NBNode* n : myControlledNodes) {
     688           69 :         for (NBNode::Crossing* c : n->getCrossings()) {
     689           14 :             if (c->tlLinkIndex == oldIndex) {
     690            2 :                 c->tlLinkIndex = newIndex;
     691              :             }
     692           14 :             if (c->tlLinkIndex2 == oldIndex) {
     693            2 :                 c->tlLinkIndex2 = newIndex;
     694              :             }
     695           55 :         }
     696              :     }
     697              : }
     698              : 
     699              : void
     700            4 : NBLoadedSUMOTLDef::groupSignals() {
     701            4 :     const int maxIndex = getMaxIndex();
     702              :     std::vector<int> unusedIndices;
     703           63 :     for (int i = 0; i <= maxIndex; i++) {
     704           59 :         if (isUsed(i)) {
     705           30 :             std::set<const NBEdge*> edges = getEdgesUsingIndex(i);
     706              :             // compactify
     707           30 :             replaceIndex(i, i - (int)unusedIndices.size());
     708           30 :             if (edges.size() == 0) {
     709              :                 // do not group pedestrian crossing signals
     710              :                 continue;
     711              :             }
     712           26 :             std::string states = getStates(i);
     713          240 :             for (int j = i + 1; j <= maxIndex; j++) {
     714              :                 // only group signals from the same edges as is commonly done by
     715              :                 // traffic engineers
     716          262 :                 if (states == getStates(j) && edges == getEdgesUsingIndex(j)) {
     717           29 :                     replaceIndex(j, i - (int)unusedIndices.size());
     718              :                 }
     719              :             }
     720              :         } else {
     721           29 :             unusedIndices.push_back(i);
     722              :         }
     723              :     }
     724           33 :     for (int i = (int)unusedIndices.size() - 1; i >= 0; i--) {
     725           29 :         myTLLogic->deleteStateIndex(unusedIndices[i]);
     726              :     }
     727            4 :     cleanupStates();
     728              :     //std::cout << "oldMaxIndex=" << maxIndex << " newMaxIndex=" << getMaxIndex() << " unused=" << toString(unusedIndices) << "\n";
     729            4 :     setTLControllingInformation();
     730              :     // patch crossing indices
     731            8 :     for (NBNode* n : myControlledNodes) {
     732            6 :         for (NBNode::Crossing* c : n->getCrossings()) {
     733            6 :             for (int i = (int)unusedIndices.size() - 1; i >= 0; i--) {
     734            4 :                 if (c->customTLIndex > i) {
     735            4 :                     c->customTLIndex--;
     736              :                 }
     737            4 :                 if (c->customTLIndex2 > i) {
     738            4 :                     c->customTLIndex2--;
     739              :                 }
     740              :             }
     741            4 :         }
     742              :     }
     743            4 : }
     744              : 
     745              : void
     746            2 : NBLoadedSUMOTLDef::ungroupSignals() {
     747              :     NBConnectionVector defaultOrdering;
     748            2 :     collectAllLinks(defaultOrdering);
     749              :     std::vector<std::string> states; // organized per link rather than phase
     750              :     int index = 0;
     751           26 :     for (NBConnection& c : defaultOrdering) {
     752           24 :         NBConnection& c2 = *find_if(myControlledLinks.begin(), myControlledLinks.end(), connection_equal(c));
     753           24 :         states.push_back(getStates(c2.getTLIndex()));
     754           24 :         c2.setTLIndex(index++);
     755              :     }
     756            4 :     for (NBNode* n : myControlledNodes) {
     757            4 :         for (NBNode::Crossing* c : n->getCrossings()) {
     758            2 :             states.push_back(getStates(c->tlLinkIndex));
     759            2 :             c->customTLIndex = index++;
     760            2 :             if (c->tlLinkIndex2 != NBConnection::InvalidTlIndex) {
     761            2 :                 states.push_back(getStates(c->tlLinkIndex2));
     762            2 :                 c->customTLIndex2 = index++;
     763              :             }
     764            2 :         }
     765              :     }
     766            2 :     myTLLogic->setStateLength(index);
     767           30 :     for (int i = 0; i < (int)states.size(); i++) {
     768          228 :         for (int p = 0; p < (int)states[i].size(); p++) {
     769          200 :             myTLLogic->setPhaseState(p, i, (LinkState)states[i][p]);
     770              :         }
     771              :     }
     772            2 :     setTLControllingInformation();
     773            2 : }
     774              : 
     775              : 
     776              : void
     777            0 : NBLoadedSUMOTLDef::copyIndices(NBTrafficLightDefinition* def) {
     778              :     std::map<int, std::string> oldStates; // organized per link index rather than phase
     779              :     std::map<int, std::string> newStates; // organized per link index rather than phase
     780            0 :     for (NBConnection& c : def->getControlledLinks()) {
     781            0 :         NBConnection& c2 = *find_if(myControlledLinks.begin(), myControlledLinks.end(), connection_equal(c));
     782            0 :         const int oldIndex = c2.getTLIndex();
     783            0 :         const int newIndex = c.getTLIndex();
     784            0 :         std::string states = getStates(oldIndex);
     785            0 :         oldStates[oldIndex] = states;
     786            0 :         if (newStates.count(newIndex) != 0 && newStates[newIndex] != states) {
     787            0 :             WRITE_WARNING("Signal groups from program '" + def->getProgramID() + "' are incompatible with the states of program '" + getProgramID() + "' at tlLogic '" + getID()
     788              :                           + "'. Possibly unsafe program.");
     789              :         } else {
     790            0 :             newStates[newIndex] = states;
     791              :         }
     792            0 :         c2.setTLIndex(newIndex);
     793              :     }
     794            0 :     const int maxIndex = getMaxIndex();
     795            0 :     myTLLogic->setStateLength(maxIndex + 1);
     796            0 :     for (int i = 0; i < (int)newStates.size(); i++) {
     797            0 :         for (int p = 0; p < (int)newStates[i].size(); p++) {
     798            0 :             myTLLogic->setPhaseState(p, i, (LinkState)newStates[i][p]);
     799              :         }
     800              :     }
     801            0 :     setTLControllingInformation();
     802            0 : }
     803              : 
     804              : 
     805              : bool
     806            4 : NBLoadedSUMOTLDef::cleanupStates() {
     807            4 :     const int maxIndex = getMaxValidIndex();
     808              :     std::vector<int> unusedIndices;
     809           34 :     for (int i = 0; i <= maxIndex; i++) {
     810           30 :         if (isUsed(i)) {
     811           30 :             if (unusedIndices.size() > 0) {
     812            0 :                 replaceIndex(i, i - (int)unusedIndices.size());
     813              :             }
     814              :         } else {
     815            0 :             unusedIndices.push_back(i);
     816              :         }
     817              :     }
     818            4 :     for (int i = (int)unusedIndices.size() - 1; i >= 0; i--) {
     819            0 :         myTLLogic->deleteStateIndex(unusedIndices[i]);
     820              :     }
     821            4 :     if (unusedIndices.size() > 0) {
     822            0 :         myTLLogic->setStateLength(maxIndex + 1 - (int)unusedIndices.size());
     823            0 :         setTLControllingInformation();
     824              :         return true;
     825              :     } else {
     826              :         return false;
     827              :     }
     828            4 : }
     829              : 
     830              : 
     831              : void
     832            0 : NBLoadedSUMOTLDef::joinLogic(NBTrafficLightDefinition* def) {
     833            0 :     def->setParticipantsInformation();
     834            0 :     def->compute(OptionsCont::getOptions());
     835            0 :     const int maxIndex = MAX2(getMaxIndex(), def->getMaxIndex());
     836            0 :     myTLLogic->setStateLength(maxIndex + 1);
     837            0 :     myControlledLinks.insert(myControlledLinks.end(), def->getControlledLinks().begin(), def->getControlledLinks().end());
     838            0 : }
     839              : 
     840              : bool
     841            2 : NBLoadedSUMOTLDef::usingSignalGroups() const {
     842              :     // count how often each index is used
     843              :     std::map<int, int> indexUsage;
     844           26 :     for (const NBConnection& c : myControlledLinks) {
     845           24 :         indexUsage[c.getTLIndex()]++;
     846              :     }
     847            4 :     for (NBNode* n : myControlledNodes) {
     848            4 :         for (NBNode::Crossing* c : n->getCrossings()) {
     849            2 :             indexUsage[c->tlLinkIndex]++;
     850            2 :             indexUsage[c->tlLinkIndex2]++;
     851            2 :         }
     852              :     }
     853            2 :     for (auto it : indexUsage) {
     854            2 :         if (it.first >= 0 && it.second > 1) {
     855              :             return true;
     856              :         }
     857              :     }
     858              :     return false;
     859              : }
     860              : 
     861              : void
     862            1 : NBLoadedSUMOTLDef::guessMinMaxDuration() {
     863              :     bool hasMinMaxDur = false;
     864            9 :     for (auto phase : myTLLogic->getPhases()) {
     865            8 :         if (phase.maxDur != UNSPECIFIED_DURATION) {
     866              :             //std::cout << " phase=" << phase.state << " maxDur=" << phase.maxDur << "\n";
     867              :             hasMinMaxDur = true;
     868              :         }
     869            8 :     }
     870            1 :     if (!hasMinMaxDur) {
     871            1 :         const SUMOTime minMinDur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.min-dur"));
     872            1 :         const SUMOTime maxDur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.max-dur"));
     873              :         std::set<int> yellowIndices;
     874            9 :         for (auto phase : myTLLogic->getPhases()) {
     875          168 :             for (int i = 0; i < (int)phase.state.size(); i++) {
     876          160 :                 if (phase.state[i] == 'y' || phase.state[i] == 'Y') {
     877              :                     yellowIndices.insert(i);
     878              :                 }
     879              :             }
     880            8 :         }
     881            9 :         for (int ip = 0; ip < (int)myTLLogic->getPhases().size(); ip++) {
     882              :             bool needMinMaxDur = false;
     883            8 :             auto phase = myTLLogic->getPhases()[ip];
     884              :             std::set<int> greenIndices;
     885            8 :             if (phase.state.find_first_of("yY") != std::string::npos) {
     886              :                 continue;
     887              :             }
     888           84 :             for (int i = 0; i < (int)phase.state.size(); i++) {
     889           80 :                 if (yellowIndices.count(i) != 0 && phase.state[i] == 'G') {
     890              :                     needMinMaxDur = true;
     891              :                     greenIndices.insert(i);
     892              :                 }
     893              :             }
     894            4 :             if (needMinMaxDur) {
     895              :                 double maxSpeed = 0;
     896           84 :                 for (NBConnection& c : myControlledLinks) {
     897              :                     if (greenIndices.count(c.getTLIndex()) != 0) {
     898           20 :                         maxSpeed = MAX2(maxSpeed, c.getFrom()->getLaneSpeed(c.getFromLane()));
     899              :                     }
     900              :                 }
     901              :                 // 5s at 50km/h, 10s at 80km/h, rounded to full seconds
     902            4 :                 const double minDurBySpeed = maxSpeed * 3.6 / 6 - 3.3;
     903            4 :                 SUMOTime minDur = MAX2(minMinDur, TIME2STEPS(floor(minDurBySpeed + 0.5)));
     904            4 :                 myTLLogic->setPhaseMinDuration(ip, minDur);
     905            4 :                 myTLLogic->setPhaseMaxDuration(ip, maxDur);
     906              :             }
     907            8 :         }
     908              :     }
     909            1 : }
     910              : 
     911              : 
     912              : void
     913          728 : NBLoadedSUMOTLDef::finalChecks() const {
     914         7718 :     for (int i = 0; i < myTLLogic->getNumLinks(); i++) {
     915         7006 :         if (!isUsed(i)) {
     916           48 :             WRITE_WARNINGF(TL("Unused state in tlLogic '%', program '%' at tl-index %"), getID(), getProgramID(), i);
     917           16 :             break;
     918              :         }
     919              :     }
     920          728 : }
     921              : 
     922              : 
     923              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1