Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netbuild/NBLoadedSUMOTLDef.cpp
169665 views
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
NBLoadedSUMOTLDef::NBLoadedSUMOTLDef(const std::string& id, const std::string& programID,
47
SUMOTime offset, TrafficLightType type) :
48
NBTrafficLightDefinition(id, programID, offset, type),
49
myTLLogic(nullptr),
50
myReconstructAddedConnections(false),
51
myReconstructRemovedConnections(false),
52
myPhasesLoaded(false) {
53
myTLLogic = new NBTrafficLightLogic(id, programID, 0, offset, type);
54
}
55
56
57
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
myTLLogic(new NBTrafficLightLogic(logic)),
61
myReconstructAddedConnections(false),
62
myReconstructRemovedConnections(false),
63
myPhasesLoaded(false) {
64
assert(def.getType() == logic.getType());
65
myControlledLinks = def.getControlledLinks();
66
myControlledNodes = def.getNodes();
67
const NBLoadedSUMOTLDef* sumoDef = dynamic_cast<const NBLoadedSUMOTLDef*>(&def);
68
updateParameters(def.getParametersMap());
69
if (sumoDef != nullptr) {
70
myReconstructAddedConnections = sumoDef->myReconstructAddedConnections;
71
myReconstructRemovedConnections = sumoDef->myReconstructRemovedConnections;
72
}
73
}
74
75
76
NBLoadedSUMOTLDef::~NBLoadedSUMOTLDef() {
77
delete myTLLogic;
78
}
79
80
81
NBTrafficLightLogic*
82
NBLoadedSUMOTLDef::myCompute(int brakingTimeSeconds) {
83
// @todo what to do with those parameters?
84
UNUSED_PARAMETER(brakingTimeSeconds);
85
initExtraConflicts();
86
reconstructLogic();
87
myTLLogic->closeBuilding(false);
88
patchIfCrossingsAdded();
89
myTLLogic->closeBuilding();
90
return new NBTrafficLightLogic(myTLLogic);
91
}
92
93
94
void
95
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
if (linkIndex >= myTLLogic->getNumLinks()) {
98
throw ProcessError("Invalid linkIndex " + toString(linkIndex) + " in connection from edge '" + from->getID() +
99
"' to edge '" + to->getID() + "' for traffic light '" + getID() +
100
"' with " + toString(myTLLogic->getNumLinks()) + " links.");
101
}
102
if (linkIndex2 >= myTLLogic->getNumLinks()) {
103
throw ProcessError("Invalid linkIndex2 " + toString(linkIndex2) + " in connection from edge '" + from->getID() +
104
"' to edge '" + to->getID() + "' for traffic light '" + getID() +
105
"' with " + toString(myTLLogic->getNumLinks()) + " links.");
106
}
107
NBConnection conn(from, fromLane, to, toLane, linkIndex, linkIndex2);
108
// avoid duplicates
109
auto newEnd = remove_if(myControlledLinks.begin(), myControlledLinks.end(), connection_equal(conn));
110
// remove_if does not remove, only re-order
111
myControlledLinks.erase(newEnd, myControlledLinks.end());
112
myControlledLinks.push_back(conn);
113
addNode(from->getToNode());
114
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
from->setControllingTLInformation(conn, getID());
119
myReconstructAddedConnections |= reconstruct;
120
}
121
122
void
123
NBLoadedSUMOTLDef::setID(const std::string& newID) {
124
Named::setID(newID);
125
myTLLogic->setID(newID);
126
}
127
128
void
129
NBLoadedSUMOTLDef::setProgramID(const std::string& programID) {
130
NBTrafficLightDefinition::setProgramID(programID);
131
myTLLogic->setProgramID(programID);
132
}
133
134
135
void
136
NBLoadedSUMOTLDef::setTLControllingInformation() const {
137
if (myReconstructAddedConnections) {
138
NBOwnTLDef dummy(DummyID, myControlledNodes, 0, getType());
139
dummy.setParticipantsInformation();
140
dummy.setTLControllingInformation();
141
for (NBNode* const n : myControlledNodes) {
142
n->removeTrafficLight(&dummy);
143
}
144
}
145
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
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
for (const NBConnection& c : myControlledLinks) {
156
if (c.getTLIndex() >= myTLLogic->getNumLinks()) {
157
throw ProcessError("Invalid linkIndex " + toString(c.getTLIndex()) + " for traffic light '" + getID() +
158
"' with " + toString(myTLLogic->getNumLinks()) + " links.");
159
}
160
NBEdge* edge = c.getFrom();
161
if (edge != nullptr && edge->getNumLanes() > c.getFromLane()) {
162
// logic may have yet to be reconstructed
163
edge->setControllingTLInformation(c, getID());
164
}
165
}
166
}
167
168
169
void
170
NBLoadedSUMOTLDef::remapRemoved(NBEdge*, const EdgeVector&, const EdgeVector&) {}
171
172
173
void
174
NBLoadedSUMOTLDef::replaceRemoved(NBEdge* removed, int removedLane, NBEdge* by, int byLane, bool incoming) {
175
if (by == nullptr) {
176
myReconstructRemovedConnections = true;
177
}
178
for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); ++it) {
179
if (incoming) {
180
(*it).replaceFrom(removed, removedLane, by, byLane);
181
} else {
182
(*it).replaceTo(removed, removedLane, by, byLane);
183
}
184
}
185
}
186
187
188
void
189
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
myTLLogic->addStep(duration, state, minDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, name, next);
193
}
194
195
196
bool
197
NBLoadedSUMOTLDef::amInvalid() const {
198
if (myControlledLinks.size() == 0) {
199
return true;
200
}
201
if (myIncomingEdges.size() == 0) {
202
return true;
203
}
204
if (myReconstructRemovedConnections) {
205
// check whether at least one connection is valid
206
for (const NBConnection& con : myControlledLinks) {
207
if (isValid(con)) {
208
return false;
209
}
210
}
211
// all invalid
212
return true;
213
}
214
return false;
215
}
216
217
218
void
219
NBLoadedSUMOTLDef::removeConnection(const NBConnection& conn, bool reconstruct) {
220
for (auto it = myControlledLinks.begin(); it != myControlledLinks.end();) {
221
if ((it->getFrom() == conn.getFrom() &&
222
it->getTo() == conn.getTo() &&
223
it->getFromLane() == conn.getFromLane() &&
224
it->getToLane() == conn.getToLane())
225
|| (it->getTLIndex() == conn.getTLIndex() &&
226
conn.getTLIndex() != conn.InvalidTlIndex &&
227
(it->getFrom() == nullptr || it->getTo() == nullptr))) {
228
if (reconstruct) {
229
myReconstructRemovedConnections = true;
230
it++;
231
} else {
232
it = myControlledLinks.erase(it);
233
}
234
} else {
235
it++;
236
}
237
}
238
}
239
240
241
void
242
NBLoadedSUMOTLDef::setOffset(SUMOTime offset) {
243
myOffset = offset;
244
myTLLogic->setOffset(offset);
245
}
246
247
248
void
249
NBLoadedSUMOTLDef::setType(TrafficLightType type) {
250
myType = type;
251
myTLLogic->setType(type);
252
}
253
254
255
void
256
NBLoadedSUMOTLDef::collectEdges() {
257
if (myControlledLinks.size() == 0) {
258
NBTrafficLightDefinition::collectEdges();
259
}
260
myIncomingEdges.clear();
261
EdgeVector myOutgoing;
262
// collect the edges from the participating nodes
263
for (std::vector<NBNode*>::iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
264
const EdgeVector& incoming = (*i)->getIncomingEdges();
265
copy(incoming.begin(), incoming.end(), back_inserter(myIncomingEdges));
266
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
for (EdgeVector::iterator j = myIncomingEdges.begin(); j != myIncomingEdges.end();) {
272
NBEdge* edge = *j;
273
edge->setInsideTLS(false); // reset
274
// an edge lies within the logic if it is outgoing as well as incoming
275
EdgeVector::iterator k = std::find(myOutgoing.begin(), myOutgoing.end(), edge);
276
if (k != myOutgoing.end()) {
277
if (myControlledInnerEdges.count(edge->getID()) == 0) {
278
bool controlled = false;
279
for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
280
if ((*it).getFrom() == edge) {
281
controlled = true;
282
break;
283
}
284
}
285
if (controlled) {
286
myControlledInnerEdges.insert(edge->getID());
287
} else {
288
myEdgesWithin.push_back(edge);
289
edge->setInsideTLS(true);
290
++j; //j = myIncomingEdges.erase(j);
291
continue;
292
}
293
}
294
}
295
++j;
296
}
297
}
298
299
300
void
301
NBLoadedSUMOTLDef::collectLinks() {
302
if (myControlledLinks.size() == 0) {
303
// maybe we only loaded a different program for a default traffic light.
304
// Try to build links now.
305
collectAllLinks(myControlledLinks);
306
}
307
}
308
309
310
/// @brief patches signal plans by modifying lane indices
311
void
312
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
for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
318
(*it).shiftLaneIndex(edge, offset, threshold);
319
}
320
}
321
}
322
323
void
324
NBLoadedSUMOTLDef::patchIfCrossingsAdded() {
325
const int size = myTLLogic->getNumLinks();
326
int noLinksAll = 0;
327
for (NBConnectionVector::const_iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
328
const NBConnection& c = *it;
329
if (c.getTLIndex() != NBConnection::InvalidTlIndex) {
330
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
for (std::vector<NBNode*>::iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
339
const std::vector<NBNode::Crossing*>& c = (*i)->getCrossings();
340
// set tl indices for crossings
341
customIndex |= (*i)->setCrossingTLIndices(getID(), noLinksAll);
342
copy(c.begin(), c.end(), std::back_inserter(crossings));
343
noLinksAll += (int)c.size();
344
oldCrossings += (*i)->numCrossingsFromSumoNet();
345
}
346
if ((int)crossings.size() != oldCrossings) {
347
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
if (phases.size() > 0 && (
350
(int)(phases.front().state.size()) < noLinksAll ||
351
((int)(phases.front().state.size()) > noLinksAll && !customIndex))) {
352
// collect edges
353
EdgeVector fromEdges(size, (NBEdge*)nullptr);
354
EdgeVector toEdges(size, (NBEdge*)nullptr);
355
std::vector<int> fromLanes(size, 0);
356
collectEdgeVectors(fromEdges, toEdges, fromLanes);
357
const std::string crossingDefaultState(crossings.size(), 'r');
358
359
// rebuild the logic (see NBOwnTLDef.cpp::myCompute)
360
NBTrafficLightLogic* newLogic = new NBTrafficLightLogic(getID(), getProgramID(), 0, myOffset, myType);
361
SUMOTime brakingTime = TIME2STEPS(computeBrakingTime(OptionsCont::getOptions().getFloat("tls.yellow.min-decel")));
362
//std::cout << "patchIfCrossingsAdded for " << getID() << " numPhases=" << phases.size() << "\n";
363
for (const auto& phase : phases) {
364
const std::string state = phase.state.substr(0, numNormalLinks) + crossingDefaultState;
365
NBOwnTLDef::addPedestrianPhases(newLogic, phase.duration, phase.minDur, phase.maxDur, phase.earliestEnd, phase.latestEnd,
366
state, crossings, fromEdges, toEdges);
367
}
368
NBOwnTLDef::addPedestrianScramble(newLogic, noLinksAll, TIME2STEPS(10), brakingTime, crossings, fromEdges, toEdges);
369
370
delete myTLLogic;
371
myTLLogic = newLogic;
372
} else if (phases.size() == 0) {
373
WRITE_WARNINGF(TL("Could not patch tlLogic '%' for changed crossings"), getID());
374
}
375
}
376
}
377
378
379
void
380
NBLoadedSUMOTLDef::collectEdgeVectors(EdgeVector& fromEdges, EdgeVector& toEdges, std::vector<int>& fromLanes) const {
381
assert(fromEdges.size() > 0);
382
assert(fromEdges.size() == toEdges.size());
383
const int size = (int)fromEdges.size();
384
385
for (NBConnectionVector::const_iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
386
const NBConnection& c = *it;
387
if (c.getTLIndex() != NBConnection::InvalidTlIndex) {
388
if (c.getTLIndex() >= size) {
389
throw ProcessError("Invalid linkIndex " + toString(c.getTLIndex()) + " for traffic light '" + getID() +
390
"' with " + toString(size) + " links.");
391
}
392
fromEdges[c.getTLIndex()] = c.getFrom();
393
toEdges[c.getTLIndex()] = c.getTo();
394
fromLanes[c.getTLIndex()] = c.getFromLane();
395
}
396
}
397
}
398
399
400
void
401
NBLoadedSUMOTLDef::initNeedsContRelation() const {
402
if (!amInvalid() && !myNeedsContRelationReady) {
403
myNeedsContRelation.clear();
404
myExtraConflicts.clear();
405
if (myType == TrafficLightType::NEMA) {
406
NBTrafficLightDefinition::initNeedsContRelation();
407
NBTrafficLightDefinition::initExtraConflicts();
408
} else {
409
const bool controlledWithin = !OptionsCont::getOptions().getBool("tls.uncontrolled-within");
410
const std::vector<NBTrafficLightLogic::PhaseDefinition> phases = myTLLogic->getPhases();
411
for (std::vector<NBTrafficLightLogic::PhaseDefinition>::const_iterator it = phases.begin(); it != phases.end(); it++) {
412
const std::string state = (*it).state;
413
for (NBConnectionVector::const_iterator it1 = myControlledLinks.begin(); it1 != myControlledLinks.end(); it1++) {
414
const NBConnection& c1 = *it1;
415
const int i1 = c1.getTLIndex();
416
if (i1 == NBConnection::InvalidTlIndex || (state[i1] != 'g' && state[i1] != 's') || c1.getFrom() == nullptr || c1.getTo() == nullptr) {
417
continue;
418
}
419
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
&& i2 != i1
424
&& (state[i2] == 'G' || state[i2] == 'g')
425
&& c2.getFrom() != nullptr && c2.getTo() != nullptr) {
426
const bool rightTurnConflict = NBNode::rightTurnConflict(
427
c1.getFrom(), c1.getTo(), c1.getFromLane(), c2.getFrom(), c2.getTo(), c2.getFromLane());
428
const bool forbidden = forbids(c2.getFrom(), c2.getTo(), c1.getFrom(), c1.getTo(), true, controlledWithin);
429
const bool isFoes = foes(c2.getFrom(), c2.getTo(), c1.getFrom(), c1.getTo()) && !c2.getFrom()->isTurningDirectionAt(c2.getTo());
430
const bool hasContRel = (forbidden && state[i1] != 's') || rightTurnConflict;
431
if (hasContRel) {
432
myNeedsContRelation.insert(StreamPair(c1.getFrom(), c1.getTo(), c2.getFrom(), c2.getTo()));
433
}
434
const bool indirectLeft = c1.getFrom()->getConnection(c1.getFromLane(), c1.getTo(), c1.getToLane()).indirectLeft;
435
if (isFoes && (state[i1] == 's' || (!hasContRel && state[i2] == 'G' && !indirectLeft))) {
436
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
}
446
}
447
myNeedsContRelationReady = true;
448
myExtraConflictsReady = true;
449
}
450
451
452
bool
453
NBLoadedSUMOTLDef::extraConflict(int index, int foeIndex) const {
454
if (amInvalid()) {
455
return false;
456
}
457
if (!myExtraConflictsReady) {
458
initExtraConflicts();
459
assert(myExtraConflictsReady);
460
}
461
return std::find(myExtraConflicts.begin(), myExtraConflicts.end(), std::make_pair(index, foeIndex)) != myExtraConflicts.end();
462
}
463
464
465
void
466
NBLoadedSUMOTLDef::registerModifications(bool addedConnections, bool removedConnections) {
467
myReconstructAddedConnections |= addedConnections;
468
myReconstructRemovedConnections |= removedConnections;
469
}
470
471
bool
472
NBLoadedSUMOTLDef::isValid(const NBConnection& con) const {
473
return (// edge still exists
474
std::find(myIncomingEdges.begin(), myIncomingEdges.end(), con.getFrom()) != myIncomingEdges.end()
475
// connection still exists
476
&& con.getFrom()->hasConnectionTo(con.getTo(), con.getToLane(), con.getFromLane())
477
// connection is still set to be controlled
478
&& con.getFrom()->mayBeTLSControlled(con.getFromLane(), con.getTo(), con.getToLane()));
479
}
480
481
void
482
NBLoadedSUMOTLDef::reconstructLogic() {
483
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
if (myReconstructAddedConnections) {
496
myReconstructAddedConnections = false;
497
// do not rebuild the logic when running netedit and all links are already covered by the program
498
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
NBOwnTLDef dummy(DummyID, myControlledNodes, 0, getType());
502
dummy.setParticipantsInformation();
503
dummy.setProgramID(getProgramID());
504
dummy.setTLControllingInformation();
505
NBTrafficLightLogic* newLogic = dummy.compute(OptionsCont::getOptions());
506
myIncomingEdges = dummy.getIncomingEdges();
507
myControlledLinks = dummy.getControlledLinks();
508
for (std::vector<NBNode*>::const_iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
509
(*i)->removeTrafficLight(&dummy);
510
}
511
delete myTLLogic;
512
myTLLogic = newLogic;
513
if (newLogic != nullptr) {
514
newLogic->setID(getID());
515
newLogic->setType(getType());
516
newLogic->setOffset(getOffset());
517
setTLControllingInformation();
518
// reset crossing custom indices
519
for (NBNode* n : myControlledNodes) {
520
for (NBNode::Crossing* c : n->getCrossings()) {
521
c->customTLIndex = NBConnection::InvalidTlIndex;
522
}
523
}
524
525
}
526
} else {
527
setTLControllingInformation();
528
}
529
}
530
if (myReconstructRemovedConnections) {
531
myReconstructRemovedConnections = false;
532
// for each connection, check whether it is still valid
533
for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end();) {
534
const NBConnection con = (*it);
535
if (isValid(con)) {
536
it++;
537
} else {
538
// remove connection
539
const int removed = con.getTLIndex();
540
it = myControlledLinks.erase(it);
541
// no automatic modificaions when running netedit
542
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
for (NBConnection& other : myControlledLinks) {
546
if (other != con && other.getTLIndex() == removed) {
547
exclusive = false;
548
break;
549
}
550
}
551
if (exclusive) {
552
// shift indices above the removed index downward
553
for (NBConnection& other : myControlledLinks) {
554
if (other.getTLIndex() > removed) {
555
other.setTLIndex(other.getTLIndex() - 1);
556
}
557
}
558
// shift crossing custom indices above the removed index downward
559
for (NBNode* n : myControlledNodes) {
560
for (NBNode::Crossing* c : n->getCrossings()) {
561
if (c->customTLIndex > removed) {
562
c->customTLIndex--;
563
}
564
}
565
}
566
// rebuild the logic
567
NBTrafficLightLogic* newLogic = new NBTrafficLightLogic(getID(), getProgramID(), 0, myOffset, myType);
568
for (const NBTrafficLightLogic::PhaseDefinition& phase : myTLLogic->getPhases()) {
569
std::string newState = phase.state;
570
newState.erase(newState.begin() + removed);
571
newLogic->addStep(phase.duration, newState);
572
}
573
delete myTLLogic;
574
myTLLogic = newLogic;
575
}
576
}
577
}
578
}
579
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
}
590
591
592
int
593
NBLoadedSUMOTLDef::getMaxIndex() {
594
int maxIndex = -1;
595
for (const NBConnection& c : myControlledLinks) {
596
maxIndex = MAX2(maxIndex, c.getTLIndex());
597
maxIndex = MAX2(maxIndex, c.getTLIndex2());
598
}
599
for (NBNode* n : myControlledNodes) {
600
for (NBNode::Crossing* c : n->getCrossings()) {
601
maxIndex = MAX2(maxIndex, c->tlLinkIndex);
602
maxIndex = MAX2(maxIndex, c->tlLinkIndex2);
603
}
604
}
605
return maxIndex;
606
}
607
608
609
int
610
NBLoadedSUMOTLDef::getMaxValidIndex() {
611
return myTLLogic->getNumLinks() - 1;
612
}
613
614
615
bool
616
NBLoadedSUMOTLDef::hasValidIndices() const {
617
for (const NBConnection& c : myControlledLinks) {
618
if (c.getTLIndex() == NBConnection::InvalidTlIndex) {
619
return false;
620
}
621
}
622
for (NBNode* n : myControlledNodes) {
623
for (NBNode::Crossing* c : n->getCrossings()) {
624
if (c->tlLinkIndex == NBConnection::InvalidTlIndex) {
625
return false;
626
}
627
}
628
}
629
// method getMaxIndex() is const but cannot be declare as such due to inheritance
630
return const_cast<NBLoadedSUMOTLDef*>(this)->getMaxIndex() < myTLLogic->getNumLinks();
631
}
632
633
634
std::string
635
NBLoadedSUMOTLDef::getStates(int index) {
636
assert(index >= 0);
637
assert(index <= getMaxValidIndex());
638
std::string result;
639
for (auto& pd : myTLLogic->getPhases()) {
640
result += pd.state[index];
641
}
642
return result;
643
}
644
645
bool
646
NBLoadedSUMOTLDef::isUsed(int index) const {
647
for (const NBConnection& c : myControlledLinks) {
648
if (c.getTLIndex() == index || c.getTLIndex2() == index) {
649
return true;
650
}
651
}
652
for (NBNode* n : myControlledNodes) {
653
for (NBNode::Crossing* c : n->getCrossings()) {
654
if (c->tlLinkIndex == index || c->tlLinkIndex2 == index) {
655
return true;
656
}
657
}
658
}
659
return false;
660
}
661
662
std::set<const NBEdge*>
663
NBLoadedSUMOTLDef::getEdgesUsingIndex(int index) const {
664
std::set<const NBEdge*> result;
665
for (const NBConnection& c : myControlledLinks) {
666
if (c.getTLIndex() == index || c.getTLIndex2() == index) {
667
result.insert(c.getFrom());
668
}
669
}
670
return result;
671
}
672
673
674
void
675
NBLoadedSUMOTLDef::replaceIndex(int oldIndex, int newIndex) {
676
if (oldIndex == newIndex) {
677
return;
678
}
679
for (NBConnection& c : myControlledLinks) {
680
if (c.getTLIndex() == oldIndex) {
681
c.setTLIndex(newIndex);
682
}
683
if (c.getTLIndex2() == oldIndex) {
684
c.setTLIndex2(newIndex);
685
}
686
}
687
for (NBNode* n : myControlledNodes) {
688
for (NBNode::Crossing* c : n->getCrossings()) {
689
if (c->tlLinkIndex == oldIndex) {
690
c->tlLinkIndex = newIndex;
691
}
692
if (c->tlLinkIndex2 == oldIndex) {
693
c->tlLinkIndex2 = newIndex;
694
}
695
}
696
}
697
}
698
699
void
700
NBLoadedSUMOTLDef::groupSignals() {
701
const int maxIndex = getMaxIndex();
702
std::vector<int> unusedIndices;
703
for (int i = 0; i <= maxIndex; i++) {
704
if (isUsed(i)) {
705
std::set<const NBEdge*> edges = getEdgesUsingIndex(i);
706
// compactify
707
replaceIndex(i, i - (int)unusedIndices.size());
708
if (edges.size() == 0) {
709
// do not group pedestrian crossing signals
710
continue;
711
}
712
std::string states = getStates(i);
713
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
if (states == getStates(j) && edges == getEdgesUsingIndex(j)) {
717
replaceIndex(j, i - (int)unusedIndices.size());
718
}
719
}
720
} else {
721
unusedIndices.push_back(i);
722
}
723
}
724
for (int i = (int)unusedIndices.size() - 1; i >= 0; i--) {
725
myTLLogic->deleteStateIndex(unusedIndices[i]);
726
}
727
cleanupStates();
728
//std::cout << "oldMaxIndex=" << maxIndex << " newMaxIndex=" << getMaxIndex() << " unused=" << toString(unusedIndices) << "\n";
729
setTLControllingInformation();
730
// patch crossing indices
731
for (NBNode* n : myControlledNodes) {
732
for (NBNode::Crossing* c : n->getCrossings()) {
733
for (int i = (int)unusedIndices.size() - 1; i >= 0; i--) {
734
if (c->customTLIndex > i) {
735
c->customTLIndex--;
736
}
737
if (c->customTLIndex2 > i) {
738
c->customTLIndex2--;
739
}
740
}
741
}
742
}
743
}
744
745
void
746
NBLoadedSUMOTLDef::ungroupSignals() {
747
NBConnectionVector defaultOrdering;
748
collectAllLinks(defaultOrdering);
749
std::vector<std::string> states; // organized per link rather than phase
750
int index = 0;
751
for (NBConnection& c : defaultOrdering) {
752
NBConnection& c2 = *find_if(myControlledLinks.begin(), myControlledLinks.end(), connection_equal(c));
753
states.push_back(getStates(c2.getTLIndex()));
754
c2.setTLIndex(index++);
755
}
756
for (NBNode* n : myControlledNodes) {
757
for (NBNode::Crossing* c : n->getCrossings()) {
758
states.push_back(getStates(c->tlLinkIndex));
759
c->customTLIndex = index++;
760
if (c->tlLinkIndex2 != NBConnection::InvalidTlIndex) {
761
states.push_back(getStates(c->tlLinkIndex2));
762
c->customTLIndex2 = index++;
763
}
764
}
765
}
766
myTLLogic->setStateLength(index);
767
for (int i = 0; i < (int)states.size(); i++) {
768
for (int p = 0; p < (int)states[i].size(); p++) {
769
myTLLogic->setPhaseState(p, i, (LinkState)states[i][p]);
770
}
771
}
772
setTLControllingInformation();
773
}
774
775
776
void
777
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
for (NBConnection& c : def->getControlledLinks()) {
781
NBConnection& c2 = *find_if(myControlledLinks.begin(), myControlledLinks.end(), connection_equal(c));
782
const int oldIndex = c2.getTLIndex();
783
const int newIndex = c.getTLIndex();
784
std::string states = getStates(oldIndex);
785
oldStates[oldIndex] = states;
786
if (newStates.count(newIndex) != 0 && newStates[newIndex] != states) {
787
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
newStates[newIndex] = states;
791
}
792
c2.setTLIndex(newIndex);
793
}
794
const int maxIndex = getMaxIndex();
795
myTLLogic->setStateLength(maxIndex + 1);
796
for (int i = 0; i < (int)newStates.size(); i++) {
797
for (int p = 0; p < (int)newStates[i].size(); p++) {
798
myTLLogic->setPhaseState(p, i, (LinkState)newStates[i][p]);
799
}
800
}
801
setTLControllingInformation();
802
}
803
804
805
bool
806
NBLoadedSUMOTLDef::cleanupStates() {
807
const int maxIndex = getMaxValidIndex();
808
std::vector<int> unusedIndices;
809
for (int i = 0; i <= maxIndex; i++) {
810
if (isUsed(i)) {
811
if (unusedIndices.size() > 0) {
812
replaceIndex(i, i - (int)unusedIndices.size());
813
}
814
} else {
815
unusedIndices.push_back(i);
816
}
817
}
818
for (int i = (int)unusedIndices.size() - 1; i >= 0; i--) {
819
myTLLogic->deleteStateIndex(unusedIndices[i]);
820
}
821
if (unusedIndices.size() > 0) {
822
myTLLogic->setStateLength(maxIndex + 1 - (int)unusedIndices.size());
823
setTLControllingInformation();
824
return true;
825
} else {
826
return false;
827
}
828
}
829
830
831
void
832
NBLoadedSUMOTLDef::joinLogic(NBTrafficLightDefinition* def) {
833
def->setParticipantsInformation();
834
def->compute(OptionsCont::getOptions());
835
const int maxIndex = MAX2(getMaxIndex(), def->getMaxIndex());
836
myTLLogic->setStateLength(maxIndex + 1);
837
myControlledLinks.insert(myControlledLinks.end(), def->getControlledLinks().begin(), def->getControlledLinks().end());
838
}
839
840
bool
841
NBLoadedSUMOTLDef::usingSignalGroups() const {
842
// count how often each index is used
843
std::map<int, int> indexUsage;
844
for (const NBConnection& c : myControlledLinks) {
845
indexUsage[c.getTLIndex()]++;
846
}
847
for (NBNode* n : myControlledNodes) {
848
for (NBNode::Crossing* c : n->getCrossings()) {
849
indexUsage[c->tlLinkIndex]++;
850
indexUsage[c->tlLinkIndex2]++;
851
}
852
}
853
for (auto it : indexUsage) {
854
if (it.first >= 0 && it.second > 1) {
855
return true;
856
}
857
}
858
return false;
859
}
860
861
void
862
NBLoadedSUMOTLDef::guessMinMaxDuration() {
863
bool hasMinMaxDur = false;
864
for (auto phase : myTLLogic->getPhases()) {
865
if (phase.maxDur != UNSPECIFIED_DURATION) {
866
//std::cout << " phase=" << phase.state << " maxDur=" << phase.maxDur << "\n";
867
hasMinMaxDur = true;
868
}
869
}
870
if (!hasMinMaxDur) {
871
const SUMOTime minMinDur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.min-dur"));
872
const SUMOTime maxDur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.max-dur"));
873
std::set<int> yellowIndices;
874
for (auto phase : myTLLogic->getPhases()) {
875
for (int i = 0; i < (int)phase.state.size(); i++) {
876
if (phase.state[i] == 'y' || phase.state[i] == 'Y') {
877
yellowIndices.insert(i);
878
}
879
}
880
}
881
for (int ip = 0; ip < (int)myTLLogic->getPhases().size(); ip++) {
882
bool needMinMaxDur = false;
883
auto phase = myTLLogic->getPhases()[ip];
884
std::set<int> greenIndices;
885
if (phase.state.find_first_of("yY") != std::string::npos) {
886
continue;
887
}
888
for (int i = 0; i < (int)phase.state.size(); i++) {
889
if (yellowIndices.count(i) != 0 && phase.state[i] == 'G') {
890
needMinMaxDur = true;
891
greenIndices.insert(i);
892
}
893
}
894
if (needMinMaxDur) {
895
double maxSpeed = 0;
896
for (NBConnection& c : myControlledLinks) {
897
if (greenIndices.count(c.getTLIndex()) != 0) {
898
maxSpeed = MAX2(maxSpeed, c.getFrom()->getLaneSpeed(c.getFromLane()));
899
}
900
}
901
// 5s at 50km/h, 10s at 80km/h, rounded to full seconds
902
const double minDurBySpeed = maxSpeed * 3.6 / 6 - 3.3;
903
SUMOTime minDur = MAX2(minMinDur, TIME2STEPS(floor(minDurBySpeed + 0.5)));
904
myTLLogic->setPhaseMinDuration(ip, minDur);
905
myTLLogic->setPhaseMaxDuration(ip, maxDur);
906
}
907
}
908
}
909
}
910
911
912
void
913
NBLoadedSUMOTLDef::finalChecks() const {
914
for (int i = 0; i < myTLLogic->getNumLinks(); i++) {
915
if (!isUsed(i)) {
916
WRITE_WARNINGF(TL("Unused state in tlLogic '%', program '%' at tl-index %"), getID(), getProgramID(), i);
917
break;
918
}
919
}
920
}
921
922
923
/****************************************************************************/
924
925