1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2011-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    NBLoadedSUMOTLDef.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Michael Behrisch
13 /// @author  Jakob Erdmann
14 /// @date    Mar 2011
15 /// @version $Id$
16 ///
17 // A complete traffic light logic loaded from a sumo-net. (opted to reimplement
18 // since NBLoadedTLDef is quite vissim specific)
19 /****************************************************************************/
20 
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25 
26 #include <vector>
27 #include <set>
28 #include <cassert>
29 #include <iterator>
30 #include <utils/common/MsgHandler.h>
31 #include <utils/common/ToString.h>
32 #include <utils/options/OptionsCont.h>
33 #include "NBTrafficLightLogic.h"
34 #include "NBOwnTLDef.h"
35 #include "NBTrafficLightDefinition.h"
36 #include "NBLoadedSUMOTLDef.h"
37 #include "NBNetBuilder.h"
38 #include "NBOwnTLDef.h"
39 #include "NBNode.h"
40 
41 //#define DEBUG_RECONSTRUCTION
42 
43 // ===========================================================================
44 // method definitions
45 // ===========================================================================
46 
NBLoadedSUMOTLDef(const std::string & id,const std::string & programID,SUMOTime offset,TrafficLightType type)47 NBLoadedSUMOTLDef::NBLoadedSUMOTLDef(const std::string& id, const std::string& programID,
48                                      SUMOTime offset, TrafficLightType type) :
49     NBTrafficLightDefinition(id, programID, offset, type),
50     myTLLogic(nullptr),
51     myReconstructAddedConnections(false),
52     myReconstructRemovedConnections(false),
53     myPhasesLoaded(false) {
54     myTLLogic = new NBTrafficLightLogic(id, programID, 0, offset, type);
55 }
56 
57 
NBLoadedSUMOTLDef(NBTrafficLightDefinition * def,NBTrafficLightLogic * logic)58 NBLoadedSUMOTLDef::NBLoadedSUMOTLDef(NBTrafficLightDefinition* def, NBTrafficLightLogic* logic) :
59     // allow for adding a new program for the same def: take the offset and programID from the new logic
60     NBTrafficLightDefinition(def->getID(), logic->getProgramID(), logic->getOffset(), def->getType()),
61     myTLLogic(new NBTrafficLightLogic(logic)),
62     myOriginalNodes(def->getNodes().begin(), def->getNodes().end()),
63     myReconstructAddedConnections(false),
64     myReconstructRemovedConnections(false),
65     myPhasesLoaded(false) {
66     assert(def->getType() == logic->getType());
67     myControlledLinks = def->getControlledLinks();
68     myControlledNodes = def->getNodes();
69     NBLoadedSUMOTLDef* sumoDef = dynamic_cast<NBLoadedSUMOTLDef*>(def);
70     updateParameter(def->getParametersMap());
71     if (sumoDef != nullptr) {
72         myReconstructAddedConnections = sumoDef->myReconstructAddedConnections;
73         myReconstructRemovedConnections = sumoDef->myReconstructRemovedConnections;
74     }
75 }
76 
77 
~NBLoadedSUMOTLDef()78 NBLoadedSUMOTLDef::~NBLoadedSUMOTLDef() {
79     delete myTLLogic;
80 }
81 
82 
83 NBTrafficLightLogic*
myCompute(int brakingTimeSeconds)84 NBLoadedSUMOTLDef::myCompute(int brakingTimeSeconds) {
85     // @todo what to do with those parameters?
86     UNUSED_PARAMETER(brakingTimeSeconds);
87     reconstructLogic();
88     myTLLogic->closeBuilding(false);
89     patchIfCrossingsAdded();
90     myTLLogic->closeBuilding();
91     return new NBTrafficLightLogic(myTLLogic);
92 }
93 
94 
95 void
addConnection(NBEdge * from,NBEdge * to,int fromLane,int toLane,int linkIndex,bool reconstruct)96 NBLoadedSUMOTLDef::addConnection(NBEdge* from, NBEdge* to, int fromLane, int toLane, int linkIndex, bool reconstruct) {
97     assert(myTLLogic->getNumLinks() > 0); // logic should be loaded by now
98     if (linkIndex >= (int)myTLLogic->getNumLinks()) {
99         throw ProcessError("Invalid linkIndex " + toString(linkIndex) + " for traffic light '" + getID() +
100                            "' with " + toString(myTLLogic->getNumLinks()) + " links.");
101     }
102     NBConnection conn(from, fromLane, to, toLane, linkIndex);
103     // avoid duplicates
104     auto newEnd = remove_if(myControlledLinks.begin(), myControlledLinks.end(), connection_equal(conn));
105     // remove_if does not remove, only re-order
106     myControlledLinks.erase(newEnd, myControlledLinks.end());
107     myControlledLinks.push_back(conn);
108     addNode(from->getToNode());
109     addNode(to->getFromNode());
110     myOriginalNodes.insert(from->getToNode());
111     myOriginalNodes.insert(to->getFromNode());
112     // added connections are definitely controlled. make sure none are removed because they lie within the tl
113     // myControlledInnerEdges.insert(from->getID()); // @todo recheck: this appears to be obsolete
114     // set this information now so that it can be used while loading diffs
115     from->setControllingTLInformation(conn, getID());
116     myReconstructAddedConnections |= reconstruct;
117 }
118 
119 
120 void
setTLControllingInformation() const121 NBLoadedSUMOTLDef::setTLControllingInformation() const {
122     if (myReconstructAddedConnections) {
123         NBOwnTLDef dummy(DummyID, myControlledNodes, 0, getType());
124         dummy.setParticipantsInformation();
125         dummy.setTLControllingInformation();
126         for (std::vector<NBNode*>::const_iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
127             (*i)->removeTrafficLight(&dummy);
128         }
129     }
130     if (myReconstructRemovedConnections) {
131         return; // will be called again in reconstructLogic()
132     }
133     // if nodes have been removed our links may have been invalidated as well
134     // since no logic will be built anyway there is no reason to inform any edges
135     if (amInvalid()) {
136         return;
137     }
138     // set the information about the link's positions within the tl into the
139     //  edges the links are starting at, respectively
140     for (NBConnectionVector::const_iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
141         const NBConnection& c = *it;
142         if (c.getTLIndex() >= (int)myTLLogic->getNumLinks()) {
143             throw ProcessError("Invalid linkIndex " + toString(c.getTLIndex()) + " for traffic light '" + getID() +
144                                "' with " + toString(myTLLogic->getNumLinks()) + " links.");
145         }
146         NBEdge* edge = c.getFrom();
147         if (edge != nullptr && edge->getNumLanes() > c.getFromLane()) {
148             // logic may have yet to be reconstructed
149             edge->setControllingTLInformation(c, getID());
150         }
151     }
152 }
153 
154 
155 void
remapRemoved(NBEdge *,const EdgeVector &,const EdgeVector &)156 NBLoadedSUMOTLDef::remapRemoved(NBEdge*, const EdgeVector&, const EdgeVector&) {}
157 
158 
159 void
replaceRemoved(NBEdge * removed,int removedLane,NBEdge * by,int byLane)160 NBLoadedSUMOTLDef::replaceRemoved(NBEdge* removed, int removedLane, NBEdge* by, int byLane) {
161     for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); ++it) {
162         (*it).replaceFrom(removed, removedLane, by, byLane);
163         (*it).replaceTo(removed, removedLane, by, byLane);
164     }
165 }
166 
167 
168 void
addPhase(SUMOTime duration,const std::string & state,SUMOTime minDur,SUMOTime maxDur,const std::vector<int> & next,const std::string & name)169 NBLoadedSUMOTLDef::addPhase(SUMOTime duration, const std::string& state, SUMOTime minDur, SUMOTime maxDur, const std::vector<int>& next, const std::string& name) {
170     myTLLogic->addStep(duration, state, minDur, maxDur, next, name);
171 }
172 
173 
174 bool
amInvalid() const175 NBLoadedSUMOTLDef::amInvalid() const {
176     if (myControlledLinks.size() == 0) {
177         return true;
178     }
179     // make sure that myControlledNodes are the original nodes
180     if (myControlledNodes.size() != myOriginalNodes.size()) {
181         //std::cout << " myControlledNodes=" << myControlledNodes.size() << " myOriginalNodes=" << myOriginalNodes.size() << "\n";
182         return true;
183     }
184     if (myIncomingEdges.size() == 0) {
185         return true;
186     }
187     for (std::vector<NBNode*>::const_iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
188         if (myOriginalNodes.count(*i) != 1) {
189             //std::cout << " node " << (*i)->getID() << " missing from myOriginalNodes\n";
190             return true;
191         }
192     }
193     return false;
194 }
195 
196 
197 void
removeConnection(const NBConnection & conn,bool reconstruct)198 NBLoadedSUMOTLDef::removeConnection(const NBConnection& conn, bool reconstruct) {
199     NBConnectionVector::iterator it = myControlledLinks.begin();
200     // find the connection but ignore its TLIndex since it might have been
201     // invalidated by an earlier removal
202     for (; it != myControlledLinks.end(); ++it) {
203         if (it->getFrom() == conn.getFrom() &&
204                 it->getTo() == conn.getTo() &&
205                 it->getFromLane() == conn.getFromLane() &&
206                 it->getToLane() == conn.getToLane()) {
207             break;
208         }
209     }
210     if (it == myControlledLinks.end()) {
211         // a traffic light doesn't always controll all connections at a junction
212         // especially when using the option --tls.join
213         return;
214     }
215     myReconstructRemovedConnections |= reconstruct;
216 }
217 
218 
219 void
setOffset(SUMOTime offset)220 NBLoadedSUMOTLDef::setOffset(SUMOTime offset) {
221     myOffset = offset;
222     myTLLogic->setOffset(offset);
223 }
224 
225 
226 void
setType(TrafficLightType type)227 NBLoadedSUMOTLDef::setType(TrafficLightType type) {
228     myType = type;
229     myTLLogic->setType(type);
230 }
231 
232 
233 void
collectEdges()234 NBLoadedSUMOTLDef::collectEdges() {
235     if (myControlledLinks.size() == 0) {
236         NBTrafficLightDefinition::collectEdges();
237     }
238     myIncomingEdges.clear();
239     EdgeVector myOutgoing;
240     // collect the edges from the participating nodes
241     for (std::vector<NBNode*>::iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
242         const EdgeVector& incoming = (*i)->getIncomingEdges();
243         copy(incoming.begin(), incoming.end(), back_inserter(myIncomingEdges));
244         const EdgeVector& outgoing = (*i)->getOutgoingEdges();
245         copy(outgoing.begin(), outgoing.end(), back_inserter(myOutgoing));
246     }
247     // check which of the edges are completely within the junction
248     // and which are uncontrolled as well (we already know myControlledLinks)
249     for (EdgeVector::iterator j = myIncomingEdges.begin(); j != myIncomingEdges.end();) {
250         NBEdge* edge = *j;
251         // an edge lies within the logic if it is outgoing as well as incoming
252         EdgeVector::iterator k = std::find(myOutgoing.begin(), myOutgoing.end(), edge);
253         if (k != myOutgoing.end()) {
254             if (myControlledInnerEdges.count(edge->getID()) == 0) {
255                 bool controlled = false;
256                 for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
257                     if ((*it).getFrom() == edge) {
258                         controlled = true;
259                         break;
260                     }
261                 }
262                 if (controlled) {
263                     myControlledInnerEdges.insert(edge->getID());
264                 } else {
265                     myEdgesWithin.push_back(edge);
266                     (*j)->setInternal();
267                     ++j; //j = myIncomingEdges.erase(j);
268                     continue;
269                 }
270             }
271         }
272         ++j;
273     }
274 }
275 
276 
277 void
collectLinks()278 NBLoadedSUMOTLDef::collectLinks() {
279     if (myControlledLinks.size() == 0) {
280         // maybe we only loaded a different program for a default traffic light.
281         // Try to build links now.
282         myOriginalNodes.insert(myControlledNodes.begin(), myControlledNodes.end());
283         collectAllLinks();
284     }
285 }
286 
287 
288 /// @brief patches signal plans by modifying lane indices
289 void
shiftTLConnectionLaneIndex(NBEdge * edge,int offset,int threshold)290 NBLoadedSUMOTLDef::shiftTLConnectionLaneIndex(NBEdge* edge, int offset, int threshold) {
291     // avoid shifting twice if the edge is incoming and outgoing to a joined TLS
292     if (myShifted.count(edge) == 0) {
293         /// XXX what if an edge should really be shifted twice?
294         myShifted.insert(edge);
295         for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
296             (*it).shiftLaneIndex(edge, offset, threshold);
297         }
298     }
299 }
300 
301 void
patchIfCrossingsAdded()302 NBLoadedSUMOTLDef::patchIfCrossingsAdded() {
303     const int size = myTLLogic->getNumLinks();
304     int noLinksAll = 0;
305     for (NBConnectionVector::const_iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
306         const NBConnection& c = *it;
307         if (c.getTLIndex() != NBConnection::InvalidTlIndex) {
308             noLinksAll = MAX2(noLinksAll, (int)c.getTLIndex() + 1);
309         }
310     }
311     const int numNormalLinks = noLinksAll;
312     int oldCrossings = 0;
313     // collect crossings
314     bool customIndex = false;
315     std::vector<NBNode::Crossing*> crossings;
316     for (std::vector<NBNode*>::iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
317         const std::vector<NBNode::Crossing*>& c = (*i)->getCrossings();
318         // set tl indices for crossings
319         customIndex |= (*i)->setCrossingTLIndices(getID(), noLinksAll);
320         copy(c.begin(), c.end(), std::back_inserter(crossings));
321         noLinksAll += (int)c.size();
322         oldCrossings += (*i)->numCrossingsFromSumoNet();
323     }
324     if ((int)crossings.size() != oldCrossings) {
325         std::vector<NBTrafficLightLogic::PhaseDefinition> phases = myTLLogic->getPhases();
326         // do not rebuilt crossing states there are custom indices and the state string is long enough
327         if (phases.size() > 0 && (
328                     (int)(phases.front().state.size()) < noLinksAll ||
329                     ((int)(phases.front().state.size()) > noLinksAll && !customIndex))) {
330             // collect edges
331             EdgeVector fromEdges(size, (NBEdge*)nullptr);
332             EdgeVector toEdges(size, (NBEdge*)nullptr);
333             std::vector<int> fromLanes(size, 0);
334             collectEdgeVectors(fromEdges, toEdges, fromLanes);
335             const std::string crossingDefaultState(crossings.size(), 'r');
336 
337             // rebuild the logic (see NBOwnTLDef.cpp::myCompute)
338             NBTrafficLightLogic* newLogic = new NBTrafficLightLogic(getID(), getProgramID(), 0, myOffset, myType);
339             SUMOTime brakingTime = TIME2STEPS(computeBrakingTime(OptionsCont::getOptions().getFloat("tls.yellow.min-decel")));
340             //std::cout << "patchIfCrossingsAdded for " << getID() << " numPhases=" << phases.size() << "\n";
341             for (std::vector<NBTrafficLightLogic::PhaseDefinition>::const_iterator it = phases.begin(); it != phases.end(); it++) {
342                 const std::string state = it->state.substr(0, numNormalLinks) + crossingDefaultState;
343                 NBOwnTLDef::addPedestrianPhases(newLogic, it->duration, it->minDur, it->maxDur, state, crossings, fromEdges, toEdges);
344             }
345             NBOwnTLDef::addPedestrianScramble(newLogic, noLinksAll, TIME2STEPS(10), brakingTime, crossings, fromEdges, toEdges);
346 
347             delete myTLLogic;
348             myTLLogic = newLogic;
349         } else if (phases.size() == 0) {
350             WRITE_WARNING("Could not patch tlLogic '" + getID() + "' for changed crossings");
351         }
352     }
353 }
354 
355 
356 void
collectEdgeVectors(EdgeVector & fromEdges,EdgeVector & toEdges,std::vector<int> & fromLanes) const357 NBLoadedSUMOTLDef::collectEdgeVectors(EdgeVector& fromEdges, EdgeVector& toEdges, std::vector<int>& fromLanes) const {
358     assert(fromEdges.size() > 0);
359     assert(fromEdges.size() == toEdges.size());
360     const int size = (int)fromEdges.size();
361 
362     for (NBConnectionVector::const_iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
363         const NBConnection& c = *it;
364         if (c.getTLIndex() != NBConnection::InvalidTlIndex) {
365             if (c.getTLIndex() >= size) {
366                 throw ProcessError("Invalid linkIndex " + toString(c.getTLIndex()) + " for traffic light '" + getID() +
367                                    "' with " + toString(size) + " links.");
368             }
369             fromEdges[c.getTLIndex()] = c.getFrom();
370             toEdges[c.getTLIndex()] = c.getTo();
371             fromLanes[c.getTLIndex()] = c.getFromLane();
372         }
373     }
374 }
375 
376 
377 void
initNeedsContRelation() const378 NBLoadedSUMOTLDef::initNeedsContRelation() const {
379     if (!amInvalid() && !myNeedsContRelationReady) {
380         myNeedsContRelation.clear();
381         myRightOnRedConflicts.clear();
382         const bool controlledWithin = !OptionsCont::getOptions().getBool("tls.uncontrolled-within");
383         const std::vector<NBTrafficLightLogic::PhaseDefinition> phases = myTLLogic->getPhases();
384         for (std::vector<NBTrafficLightLogic::PhaseDefinition>::const_iterator it = phases.begin(); it != phases.end(); it++) {
385             const std::string state = (*it).state;
386             for (NBConnectionVector::const_iterator it1 = myControlledLinks.begin(); it1 != myControlledLinks.end(); it1++) {
387                 const NBConnection& c1 = *it1;
388                 const int i1 = c1.getTLIndex();
389                 if (i1 == NBConnection::InvalidTlIndex || (state[i1] != 'g' && state[i1] != 's') || c1.getFrom() == nullptr || c1.getTo() == nullptr) {
390                     continue;
391                 }
392                 for (NBConnectionVector::const_iterator it2 = myControlledLinks.begin(); it2 != myControlledLinks.end(); it2++) {
393                     const NBConnection& c2 = *it2;
394                     const int i2 = c2.getTLIndex();
395                     if (i2 != NBConnection::InvalidTlIndex
396                             && i2 != i1
397                             && (state[i2] == 'G' || state[i2] == 'g')
398                             && c2.getFrom() != nullptr && c2.getTo() != nullptr) {
399                         const bool rightTurnConflict = NBNode::rightTurnConflict(
400                                                            c1.getFrom(), c1.getTo(), c1.getFromLane(), c2.getFrom(), c2.getTo(), c2.getFromLane());
401                         const bool forbidden = forbids(c2.getFrom(), c2.getTo(), c1.getFrom(), c1.getTo(), true, controlledWithin);
402                         const bool isFoes = foes(c2.getFrom(), c2.getTo(), c1.getFrom(), c1.getTo()) && !c2.getFrom()->isTurningDirectionAt(c2.getTo());
403                         if (forbidden || rightTurnConflict) {
404                             myNeedsContRelation.insert(StreamPair(c1.getFrom(), c1.getTo(), c2.getFrom(), c2.getTo()));
405                         }
406                         if (isFoes) {
407                             myRightOnRedConflicts.insert(std::make_pair(i1, i2));
408                         }
409                         //std::cout << getID() << " i1=" << i1 << " i2=" << i2 << " rightTurnConflict=" << rightTurnConflict << " forbidden=" << forbidden << " isFoes=" << isFoes << "\n";
410                     }
411                 }
412             }
413         }
414     }
415     myNeedsContRelationReady = true;
416     myRightOnRedConflictsReady = true;
417 }
418 
419 
420 bool
rightOnRedConflict(int index,int foeIndex) const421 NBLoadedSUMOTLDef::rightOnRedConflict(int index, int foeIndex) const {
422     if (amInvalid()) {
423         return false;
424     }
425     if (!myRightOnRedConflictsReady) {
426         initNeedsContRelation();
427         assert(myRightOnRedConflictsReady);
428     }
429     return std::find(myRightOnRedConflicts.begin(), myRightOnRedConflicts.end(), std::make_pair(index, foeIndex)) != myRightOnRedConflicts.end();
430 }
431 
432 
433 void
registerModifications(bool addedConnections,bool removedConnections)434 NBLoadedSUMOTLDef::registerModifications(bool addedConnections, bool removedConnections) {
435     myReconstructAddedConnections |= addedConnections;
436     myReconstructRemovedConnections |= removedConnections;
437 }
438 
439 void
reconstructLogic()440 NBLoadedSUMOTLDef::reconstructLogic() {
441     const bool netedit = NBNetBuilder::runningNetedit();
442 #ifdef DEBUG_RECONSTRUCTION
443     bool debugPrintModified = myReconstructAddedConnections || myReconstructRemovedConnections;
444     std::cout << " reconstructLogic added=" << myReconstructAddedConnections << " removed=" << myReconstructRemovedConnections << " valid=" << hasValidIndices() << " oldLinks:\n";
445     for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); ++it) {
446         std::cout << "    " << *it << "\n";
447     }
448 #endif
449     if (myReconstructAddedConnections) {
450         myReconstructAddedConnections = false;
451         // do not rebuild the logic when running netedit and all links are already covered by the program
452         if (!myPhasesLoaded && !(netedit && hasValidIndices())) {
453             // rebuild the logic from scratch
454             // XXX if a connection with the same from- and to-edge already exisits, its states could be copied instead
455             NBOwnTLDef dummy(DummyID, myControlledNodes, 0, getType());
456             dummy.setParticipantsInformation();
457             dummy.setProgramID(getProgramID());
458             dummy.setTLControllingInformation();
459             NBTrafficLightLogic* newLogic = dummy.compute(OptionsCont::getOptions());
460             myIncomingEdges = dummy.getIncomingEdges();
461             myControlledLinks = dummy.getControlledLinks();
462             for (std::vector<NBNode*>::const_iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
463                 (*i)->removeTrafficLight(&dummy);
464             }
465             delete myTLLogic;
466             myTLLogic = newLogic;
467             if (newLogic != nullptr) {
468                 newLogic->setID(getID());
469                 newLogic->setType(getType());
470                 newLogic->setOffset(getOffset());
471                 setTLControllingInformation();
472                 // reset crossing custom indices
473                 for (NBNode* n : myControlledNodes) {
474                     for (NBNode::Crossing* c : n->getCrossings()) {
475                         c->customTLIndex = NBConnection::InvalidTlIndex;
476                     }
477                 }
478 
479             }
480         } else {
481             setTLControllingInformation();
482         }
483     }
484     if (myReconstructRemovedConnections) {
485         myReconstructRemovedConnections = false;
486         // for each connection, check whether it is still valid
487         for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end();) {
488             const NBConnection con = (*it);
489             if (// edge still exists
490                 std::find(myIncomingEdges.begin(), myIncomingEdges.end(), con.getFrom()) != myIncomingEdges.end()
491                 // connection still exists
492                 && con.getFrom()->hasConnectionTo(con.getTo(), con.getToLane(), con.getFromLane())
493                 // connection is still set to be controlled
494                 && con.getFrom()->mayBeTLSControlled(con.getFromLane(), con.getTo(), con.getToLane())) {
495                 it++;
496             } else {
497                 // remove connection
498                 const int removed = con.getTLIndex();
499                 it = myControlledLinks.erase(it);
500                 // no automatic modificaions when running netedit
501                 if (!myPhasesLoaded && !(netedit && hasValidIndices())) {
502                     // shift index off successive connections and remove entry from all phases if the tlIndex was only used by this connection
503                     bool exclusive = true;
504                     for (NBConnection& other : myControlledLinks) {
505                         if (other != con && other.getTLIndex() == removed) {
506                             exclusive = false;
507                             break;
508                         }
509                     }
510                     if (exclusive) {
511                         // shift indices above the removed index downward
512                         for (NBConnection& other : myControlledLinks) {
513                             if (other.getTLIndex() > removed) {
514                                 other.setTLIndex(other.getTLIndex() - 1);
515                             }
516                         }
517                         // shift crossing custom indices above the removed index downward
518                         for (NBNode* n : myControlledNodes) {
519                             for (NBNode::Crossing* c : n->getCrossings()) {
520                                 if (c->customTLIndex > removed) {
521                                     c->customTLIndex--;
522                                 }
523                             }
524                         }
525                         // rebuild the logic
526                         const std::vector<NBTrafficLightLogic::PhaseDefinition> phases = myTLLogic->getPhases();
527                         NBTrafficLightLogic* newLogic = new NBTrafficLightLogic(getID(), getProgramID(), 0, myOffset, myType);
528                         for (std::vector<NBTrafficLightLogic::PhaseDefinition>::const_iterator it = phases.begin(); it != phases.end(); it++) {
529                             std::string newState = it->state;
530                             newState.erase(newState.begin() + removed);
531                             newLogic->addStep(it->duration, newState);
532                         }
533                         delete myTLLogic;
534                         myTLLogic = newLogic;
535                     }
536                 }
537             }
538         }
539         setTLControllingInformation();
540     }
541 #ifdef DEBUG_RECONSTRUCTION
542     if (debugPrintModified) {
543         std::cout << " newLinks:\n";
544         for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); ++it) {
545             std::cout << "    " << *it << "\n";
546         }
547     }
548 #endif
549 }
550 
551 
552 int
getMaxIndex()553 NBLoadedSUMOTLDef::getMaxIndex() {
554     int maxIndex = -1;
555     for (const NBConnection& c : myControlledLinks) {
556         maxIndex = MAX2(maxIndex, c.getTLIndex());
557     }
558     for (NBNode* n : myControlledNodes) {
559         for (NBNode::Crossing* c : n->getCrossings()) {
560             maxIndex = MAX2(maxIndex, c->tlLinkIndex);
561             maxIndex = MAX2(maxIndex, c->tlLinkIndex2);
562         }
563     }
564     return maxIndex;
565 }
566 
567 
568 int
getMaxValidIndex()569 NBLoadedSUMOTLDef::getMaxValidIndex() {
570     return myTLLogic->getNumLinks() - 1;
571 }
572 
573 
574 bool
hasValidIndices() const575 NBLoadedSUMOTLDef::hasValidIndices() const {
576     for (const NBConnection& c : myControlledLinks) {
577         if (c.getTLIndex() == NBConnection::InvalidTlIndex) {
578             return false;
579         }
580     }
581     for (NBNode* n : myControlledNodes) {
582         for (NBNode::Crossing* c : n->getCrossings()) {
583             if (c->tlLinkIndex == NBConnection::InvalidTlIndex) {
584                 return false;
585             }
586         }
587     }
588     // method getMaxIndex() is const but cannot be declare as such due to inheritance
589     return const_cast<NBLoadedSUMOTLDef*>(this)->getMaxIndex() < myTLLogic->getNumLinks();
590 }
591 
592 
593 bool
cleanupStates()594 NBLoadedSUMOTLDef::cleanupStates() {
595     const int maxIndex = getMaxIndex();
596     if (maxIndex >= 0 && maxIndex + 1 < myTLLogic->getNumLinks()) {
597         myTLLogic->setStateLength(maxIndex + 1);
598         return true;
599     }
600     return false;
601 }
602 
603 void
joinLogic(NBTrafficLightDefinition * def)604 NBLoadedSUMOTLDef::joinLogic(NBTrafficLightDefinition* def) {
605     def->setParticipantsInformation();
606     def->compute(OptionsCont::getOptions());
607     const int maxIndex = MAX2(getMaxIndex(), def->getMaxIndex());
608     myTLLogic->setStateLength(maxIndex + 1);
609     myControlledLinks.insert(myControlledLinks.end(), def->getControlledLinks().begin(), def->getControlledLinks().end());
610     myOriginalNodes.insert(def->getNodes().begin(), def->getNodes().end());
611 }
612 
613 bool
usingSignalGroups() const614 NBLoadedSUMOTLDef::usingSignalGroups() const {
615     // count how often each index is used
616     std::map<int, int> indexUsage;
617     for (const NBConnection& c : myControlledLinks) {
618         indexUsage[c.getTLIndex()]++;
619     }
620     for (NBNode* n : myControlledNodes) {
621         for (NBNode::Crossing* c : n->getCrossings()) {
622             indexUsage[c->tlLinkIndex]++;
623             indexUsage[c->tlLinkIndex2]++;
624         }
625     }
626     for (auto it : indexUsage) {
627         if (it.first >= 0 && it.second > 1) {
628             return true;
629         }
630     }
631     return false;
632 }
633 
634 /****************************************************************************/
635 
636