Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netbuild/NBOwnTLDef.cpp
169665 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-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 NBOwnTLDef.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Sascha Krieg
18
/// @author Michael Behrisch
19
/// @date Tue, 29.05.2005
20
///
21
// A traffic light logics which must be computed (only nodes/edges are given)
22
/****************************************************************************/
23
#include <config.h>
24
25
#include <vector>
26
#include <cassert>
27
#include <iterator>
28
#include "NBTrafficLightDefinition.h"
29
#include "NBNode.h"
30
#include "NBOwnTLDef.h"
31
#include "NBTrafficLightLogic.h"
32
#include <utils/common/MsgHandler.h>
33
#include <utils/common/UtilExceptions.h>
34
#include <utils/common/ToString.h>
35
#include <utils/common/StringUtils.h>
36
#include <utils/options/OptionsCont.h>
37
#include <utils/options/Option.h>
38
39
#define HEIGH_WEIGHT 2
40
#define LOW_WEIGHT .5;
41
42
#define MIN_GREEN_TIME 5
43
44
//#define DEBUG_STREAM_ORDERING
45
//#define DEBUG_PHASES
46
//#define DEBUG_CONTRELATION
47
#define DEBUGID "C"
48
#define DEBUGCOND (getID() == DEBUGID)
49
#define DEBUGCOND2(obj) (obj->getID() == DEBUGID)
50
//#define DEBUGEDGE(edge) (edge->getID() == "23209153#1" || edge->getID() == "319583927#0")
51
//#define DEBUGCOND (true)
52
#define DEBUGEDGE(edge) (true)
53
54
// ===========================================================================
55
// static members
56
// ===========================================================================
57
const double NBOwnTLDef::MIN_SPEED_CROSSING_TIME(25 / 3.6);
58
59
60
// ===========================================================================
61
// member method definitions
62
// ===========================================================================
63
NBOwnTLDef::NBOwnTLDef(const std::string& id,
64
const std::vector<NBNode*>& junctions, SUMOTime offset,
65
TrafficLightType type) :
66
NBTrafficLightDefinition(id, junctions, DefaultProgramID, offset, type),
67
myHaveSinglePhase(false),
68
myLayout(TrafficLightLayout::DEFAULT) {
69
}
70
71
72
NBOwnTLDef::NBOwnTLDef(const std::string& id, NBNode* junction, SUMOTime offset,
73
TrafficLightType type) :
74
NBTrafficLightDefinition(id, junction, DefaultProgramID, offset, type),
75
myHaveSinglePhase(false),
76
myLayout(TrafficLightLayout::DEFAULT) {
77
}
78
79
80
NBOwnTLDef::NBOwnTLDef(const std::string& id, SUMOTime offset,
81
TrafficLightType type) :
82
NBTrafficLightDefinition(id, DefaultProgramID, offset, type),
83
myHaveSinglePhase(false),
84
myLayout(TrafficLightLayout::DEFAULT) {
85
}
86
87
88
NBOwnTLDef::~NBOwnTLDef() {}
89
90
91
int
92
NBOwnTLDef::getToPrio(const NBEdge* const e) {
93
return e->getJunctionPriority(e->getToNode());
94
}
95
96
97
double
98
NBOwnTLDef::getDirectionalWeight(LinkDirection dir) {
99
switch (dir) {
100
case LinkDirection::STRAIGHT:
101
case LinkDirection::PARTLEFT:
102
case LinkDirection::PARTRIGHT:
103
return HEIGH_WEIGHT;
104
case LinkDirection::LEFT:
105
case LinkDirection::RIGHT:
106
return LOW_WEIGHT;
107
default:
108
break;
109
}
110
return 0;
111
}
112
113
double
114
NBOwnTLDef::computeUnblockedWeightedStreamNumber(const NBEdge* const e1, const NBEdge* const e2) {
115
double val = 0;
116
for (int e1l = 0; e1l < e1->getNumLanes(); e1l++) {
117
std::vector<NBEdge::Connection> approached1 = e1->getConnectionsFromLane(e1l);
118
for (int e2l = 0; e2l < e2->getNumLanes(); e2l++) {
119
std::vector<NBEdge::Connection> approached2 = e2->getConnectionsFromLane(e2l);
120
for (std::vector<NBEdge::Connection>::iterator e1c = approached1.begin(); e1c != approached1.end(); ++e1c) {
121
if (e1->getTurnDestination() == (*e1c).toEdge) {
122
continue;
123
}
124
for (std::vector<NBEdge::Connection>::iterator e2c = approached2.begin(); e2c != approached2.end(); ++e2c) {
125
if (e2->getTurnDestination() == (*e2c).toEdge) {
126
continue;
127
}
128
const double sign = (forbids(e1, (*e1c).toEdge, e2, (*e2c).toEdge, true)
129
|| forbids(e2, (*e2c).toEdge, e1, (*e1c).toEdge, true)) ? -1 : 1;
130
double w1;
131
double w2;
132
const int prio1 = e1->getJunctionPriority(e1->getToNode());
133
const int prio2 = e2->getJunctionPriority(e2->getToNode());
134
if (prio1 == prio2) {
135
w1 = getDirectionalWeight(e1->getToNode()->getDirection(e1, (*e1c).toEdge));
136
w2 = getDirectionalWeight(e2->getToNode()->getDirection(e2, (*e2c).toEdge));
137
} else {
138
if (prio1 > prio2) {
139
w1 = HEIGH_WEIGHT;
140
w2 = LOW_WEIGHT;
141
} else {
142
w1 = LOW_WEIGHT;
143
w2 = HEIGH_WEIGHT;
144
}
145
if (sign == -1) {
146
// extra penalty if edges with different junction priority are in conflict
147
w1 *= 2;
148
w2 *= 2;
149
}
150
}
151
if (isRailway(e1->getPermissions()) != isRailway(e2->getPermissions())) {
152
w1 *= 0.1;
153
w2 *= 0.1;
154
}
155
if ((e1->getPermissions() & SVC_PASSENGER) == 0) {
156
w1 *= 0.1;
157
}
158
if ((e2->getPermissions() & SVC_PASSENGER) == 0) {
159
w2 *= 0.1;
160
}
161
val += sign * w1;
162
val += sign * w2;
163
#ifdef DEBUG_STREAM_ORDERING
164
if (DEBUGCOND && DEBUGEDGE(e2) && DEBUGEDGE(e1)) {
165
std::cout << " sign=" << sign << " w1=" << w1 << " w2=" << w2 << " val=" << val
166
<< " c1=" << (*e1c).getDescription(e1)
167
<< " c2=" << (*e2c).getDescription(e2)
168
<< "\n";
169
}
170
#endif
171
}
172
}
173
}
174
}
175
#ifdef DEBUG_STREAM_ORDERING
176
if (DEBUGCOND && DEBUGEDGE(e2) && DEBUGEDGE(e1)) {
177
std::cout << " computeUnblockedWeightedStreamNumber e1=" << e1->getID() << " e2=" << e2->getID() << " val=" << val << "\n";
178
}
179
#endif
180
return val;
181
}
182
183
184
std::pair<NBEdge*, NBEdge*>
185
NBOwnTLDef::getBestCombination(const EdgeVector& edges) {
186
std::pair<NBEdge*, NBEdge*> bestPair(static_cast<NBEdge*>(nullptr), static_cast<NBEdge*>(nullptr));
187
double bestValue = -std::numeric_limits<double>::max();
188
for (EdgeVector::const_iterator i = edges.begin(); i != edges.end(); ++i) {
189
for (EdgeVector::const_iterator j = i + 1; j != edges.end(); ++j) {
190
const double value = computeUnblockedWeightedStreamNumber(*i, *j);
191
if (value > bestValue) {
192
bestValue = value;
193
bestPair = std::pair<NBEdge*, NBEdge*>(*i, *j);
194
} else if (value == bestValue) {
195
const double ca = GeomHelper::getMinAngleDiff((*i)->getAngleAtNode((*i)->getToNode()), (*j)->getAngleAtNode((*j)->getToNode()));
196
const double oa = GeomHelper::getMinAngleDiff(bestPair.first->getAngleAtNode(bestPair.first->getToNode()), bestPair.second->getAngleAtNode(bestPair.second->getToNode()));
197
if (fabs(oa - ca) < NUMERICAL_EPS) { // break ties by id
198
if (bestPair.first->getID() < (*i)->getID()) {
199
bestPair = std::pair<NBEdge*, NBEdge*>(*i, *j);
200
}
201
} else if (oa < ca) {
202
bestPair = std::pair<NBEdge*, NBEdge*>(*i, *j);
203
}
204
}
205
}
206
}
207
if (bestValue <= 0) {
208
// do not group edges
209
if (bestPair.first->getPriority() < bestPair.second->getPriority()) {
210
std::swap(bestPair.first, bestPair.second);
211
}
212
bestPair.second = nullptr;
213
}
214
#ifdef DEBUG_STREAM_ORDERING
215
if (DEBUGCOND) {
216
std::cout << " getBestCombination bestValue=" << bestValue << " best=" << Named::getIDSecure(bestPair.first) << ", " << Named::getIDSecure(bestPair.second) << "\n";
217
}
218
#endif
219
return bestPair;
220
}
221
222
223
std::pair<NBEdge*, NBEdge*>
224
NBOwnTLDef::getBestPair(EdgeVector& incoming) {
225
if (incoming.size() == 1) {
226
// only one there - return the one
227
std::pair<NBEdge*, NBEdge*> ret(*incoming.begin(), static_cast<NBEdge*>(nullptr));
228
incoming.clear();
229
return ret;
230
}
231
// determine the best combination
232
// by priority, first
233
EdgeVector used;
234
std::sort(incoming.begin(), incoming.end(), edge_by_incoming_priority_sorter());
235
used.push_back(*incoming.begin()); // the first will definitely be used
236
// get the ones with the same priority
237
int prio = getToPrio(*used.begin());
238
for (EdgeVector::iterator i = incoming.begin() + 1; i != incoming.end() && prio == getToPrio(*i); ++i) {
239
used.push_back(*i);
240
}
241
// if there only lower priorised, use these, too
242
if (used.size() < 2) {
243
used = incoming;
244
}
245
std::pair<NBEdge*, NBEdge*> ret = getBestCombination(used);
246
#ifdef DEBUG_STREAM_ORDERING
247
if (DEBUGCOND) {
248
std::cout << "getBestPair tls=" << getID() << " incoming=" << toString(incoming) << " prio=" << prio << " used=" << toString(used) << " best=" << Named::getIDSecure(ret.first) << ", " << Named::getIDSecure(ret.second) << "\n";
249
}
250
#endif
251
252
incoming.erase(find(incoming.begin(), incoming.end(), ret.first));
253
if (ret.second != nullptr) {
254
incoming.erase(find(incoming.begin(), incoming.end(), ret.second));
255
}
256
return ret;
257
}
258
259
bool
260
NBOwnTLDef::hasStraightConnection(const NBEdge* fromEdge) {
261
for (const NBEdge::Connection& c : fromEdge->getConnections()) {
262
LinkDirection dir = fromEdge->getToNode()->getDirection(fromEdge, c.toEdge);
263
if (dir == LinkDirection::STRAIGHT) {
264
return true;
265
}
266
}
267
return false;
268
}
269
270
NBTrafficLightLogic*
271
NBOwnTLDef::myCompute(int brakingTimeSeconds) {
272
if (myControlledNodes.size() > 1) {
273
// call this first so that the following call to computeLogicAndConts resets linkIndices
274
initNeedsContRelation();
275
// reset insideTLS info
276
collectEdges();
277
}
278
return computeLogicAndConts(brakingTimeSeconds);
279
}
280
281
282
NBTrafficLightLogic*
283
NBOwnTLDef::computeLogicAndConts(int brakingTimeSeconds, bool onlyConts) {
284
if (myControlledNodes.size() == 1) {
285
// otherwise, use values from previous call to initNeedsContRelation
286
myNeedsContRelation.clear();
287
}
288
myExtraConflicts.clear();
289
const bool isNEMA = myType == TrafficLightType::NEMA;
290
const SUMOTime brakingTime = TIME2STEPS(brakingTimeSeconds);
291
const SUMOTime leftTurnTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.left-green.time"));
292
const SUMOTime minMinDur = (myType == TrafficLightType::STATIC) ? UNSPECIFIED_DURATION : TIME2STEPS(OptionsCont::getOptions().getInt("tls.min-dur"));
293
const SUMOTime maxDur = (myType == TrafficLightType::STATIC) ? UNSPECIFIED_DURATION : TIME2STEPS(OptionsCont::getOptions().getInt("tls.max-dur"));
294
const SUMOTime earliestEnd = UNSPECIFIED_DURATION;
295
const SUMOTime latestEnd = UNSPECIFIED_DURATION;
296
const SUMOTime greenTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.green.time"));
297
SUMOTime allRedTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.allred.time"));
298
const double minorLeftSpeedThreshold = OptionsCont::getOptions().getFloat("tls.minor-left.max-speed");
299
const bool noMixed = OptionsCont::getOptions().getBool("tls.no-mixed");
300
// left-turn phases do not work well for joined tls, so we build incoming instead
301
if (myLayout == TrafficLightLayout::DEFAULT) {
302
// @note this prevents updating after loading plain-xml into netedit computing tls and then changing the default layout
303
myLayout = SUMOXMLDefinitions::TrafficLightLayouts.get(OptionsCont::getOptions().getString("tls.layout"));
304
}
305
// corridorLike() resets crossing indices so should be called first
306
const bool groupOpposites = (myLayout == TrafficLightLayout::OPPOSITES && (myControlledNodes.size() <= 2 || corridorLike()));
307
308
// things collect for NEMA phase building
309
std::vector<std::pair<NBEdge*, NBEdge*> > chosenList;
310
std::vector<std::string> straightStates;
311
std::vector<std::string> leftStates;
312
313
// build complete lists first
314
const EdgeVector& incoming = getIncomingEdges();
315
EdgeVector fromEdges, toEdges;
316
std::vector<bool> isTurnaround;
317
std::vector<bool> hasTurnLane;
318
std::vector<int> fromLanes;
319
std::vector<int> toLanes;
320
std::vector<SUMOTime> crossingTime;
321
int totalNumLinks = 0;
322
for (NBEdge* const fromEdge : incoming) {
323
const int numLanes = fromEdge->getNumLanes();
324
const bool edgeHasStraight = hasStraightConnection(fromEdge);
325
for (int i2 = 0; i2 < numLanes; i2++) {
326
bool hasLeft = false;
327
bool hasPartLeft = false;
328
bool hasStraight = false;
329
bool hasRight = false;
330
bool hasTurnaround = false;
331
for (const NBEdge::Connection& approached : fromEdge->getConnectionsFromLane(i2)) {
332
if (!fromEdge->mayBeTLSControlled(i2, approached.toEdge, approached.toLane)) {
333
continue;
334
}
335
fromEdges.push_back(fromEdge);
336
fromLanes.push_back(i2);
337
toLanes.push_back(approached.toLane);
338
toEdges.push_back(approached.toEdge);
339
if (approached.vmax < NUMERICAL_EPS || (fromEdge->getPermissions() & SVC_PASSENGER) == 0
340
|| (approached.toEdge->getPermissions() & SVC_PASSENGER) == 0) {
341
crossingTime.push_back(0);
342
} else {
343
crossingTime.push_back(TIME2STEPS((approached.length + approached.viaLength) / MAX2(approached.vmax, MIN_SPEED_CROSSING_TIME)));
344
}
345
// std::cout << fromEdge->getID() << " " << approached.toEdge->getID() << " " << (fromEdge->getPermissions() & SVC_PASSENGER) << " " << approached.length << " " << approached.viaLength << " " << approached.vmax << " " << crossingTime.back() << std::endl;
346
if (approached.toEdge != nullptr) {
347
isTurnaround.push_back(fromEdge->isTurningDirectionAt(approached.toEdge));
348
} else {
349
isTurnaround.push_back(true);
350
}
351
LinkDirection dir = fromEdge->getToNode()->getDirection(fromEdge, approached.toEdge);
352
if (dir == LinkDirection::STRAIGHT) {
353
hasStraight = true;
354
} else if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
355
hasRight = true;
356
} else if (dir == LinkDirection::LEFT) {
357
hasLeft = true;
358
} else if (dir == LinkDirection::PARTLEFT) {
359
hasPartLeft = true;
360
} else if (dir == LinkDirection::TURN) {
361
hasTurnaround = true;
362
}
363
totalNumLinks++;
364
}
365
for (const NBEdge::Connection& approached : fromEdge->getConnectionsFromLane(i2)) {
366
if (!fromEdge->mayBeTLSControlled(i2, approached.toEdge, approached.toLane)) {
367
continue;
368
}
369
hasTurnLane.push_back(
370
(hasLeft && !hasPartLeft && !hasStraight && !hasRight)
371
|| (hasPartLeft && !hasLeft && !hasStraight && !hasRight)
372
|| (hasPartLeft && hasLeft && edgeHasStraight && !hasRight)
373
|| (!hasLeft && !hasPartLeft && !hasTurnaround && hasRight));
374
}
375
//std::cout << " from=" << fromEdge->getID() << "_" << i2 << " hasTurnLane=" << hasTurnLane.back() << " s=" << hasStraight << " l=" << hasLeft << " r=" << hasRight << " t=" << hasTurnaround << "\n";
376
}
377
}
378
// collect crossings
379
std::vector<NBNode::Crossing*> crossings;
380
for (NBNode* const node : myControlledNodes) {
381
const std::vector<NBNode::Crossing*>& c = node->getCrossings();
382
node->setCrossingTLIndices(getID(), totalNumLinks, onlyConts);
383
totalNumLinks = MAX2(totalNumLinks, maxCrossingIndex(node) + 1);
384
copy(c.begin(), c.end(), std::back_inserter(crossings));
385
}
386
387
NBTrafficLightLogic* logic = new NBTrafficLightLogic(getID(), getProgramID(), totalNumLinks, myOffset, myType);
388
EdgeVector toProc = getConnectedOuterEdges(incoming);
389
390
// build all phases
391
std::vector<int> greenPhases; // indices of green phases
392
std::vector<bool> hadGreenMajor(totalNumLinks, false);
393
while (toProc.size() > 0) {
394
bool groupTram = false;
395
bool groupOther = false;
396
std::pair<NBEdge*, NBEdge*> chosen;
397
std::set<const NBEdge*> chosenSet;
398
if (groupOpposites) {
399
if (incoming.size() == 2) {
400
// if there are only 2 incoming edges we need to decide whether they are a crossing or a "continuation"
401
// @node: this heuristic could be extended to also check the number of outgoing edges
402
double angle = fabs(NBHelpers::relAngle(incoming[0]->getAngleAtNode(incoming[0]->getToNode()), incoming[1]->getAngleAtNode(incoming[1]->getToNode())));
403
// angle would be 180 for straight opposing incoming edges
404
if (angle < 135) {
405
chosen = std::pair<NBEdge*, NBEdge*>(toProc[0], static_cast<NBEdge*>(nullptr));
406
toProc.erase(toProc.begin());
407
} else {
408
chosen = getBestPair(toProc);
409
}
410
} else {
411
chosen = getBestPair(toProc);
412
if (chosen.second == nullptr && chosen.first->getPermissions() == SVC_TRAM) {
413
groupTram = true;
414
for (auto it = toProc.begin(); it != toProc.end();) {
415
if ((*it)->getPermissions() == SVC_TRAM) {
416
it = toProc.erase(it);
417
} else {
418
it++;
419
}
420
}
421
}
422
}
423
} else {
424
NBEdge* chosenEdge = toProc[0];
425
chosen = std::pair<NBEdge*, NBEdge*>(chosenEdge, static_cast<NBEdge*>(nullptr));
426
toProc.erase(toProc.begin());
427
SVCPermissions perms = chosenEdge->getPermissions();
428
if (perms == SVC_TRAM) {
429
groupTram = true;
430
} else if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_DELIVERY)) == 0) {
431
if (OptionsCont::getOptions().getBool("tls.ignore-internal-junction-jam")) {
432
// otherwise, we can get a mutual conflict for minor green
433
// streams which would create deadlock
434
groupOther = true;
435
}
436
}
437
// group all edges with the same permissions into a single phase (later)
438
if (groupTram || groupOther) {
439
for (auto it = toProc.begin(); it != toProc.end();) {
440
if ((*it)->getPermissions() == perms) {
441
it = toProc.erase(it);
442
} else {
443
it++;
444
}
445
}
446
}
447
}
448
int pos = 0;
449
std::string state(totalNumLinks, 'r');
450
#ifdef DEBUG_PHASES
451
if (DEBUGCOND) {
452
std::cout << " computing " << getID() << " prog=" << getProgramID() << " cho1=" << Named::getIDSecure(chosen.first) << " cho2=" << Named::getIDSecure(chosen.second) << " toProc=" << toString(toProc) << " bentPrio=" << chosen.first->getToNode()->isBentPriority() << "\n";
453
}
454
#endif
455
chosenList.push_back(chosen);
456
chosenSet.insert(chosen.first);
457
if (chosen.second != nullptr) {
458
chosenSet.insert(chosen.second);
459
}
460
// find parallel bike edge for the chosen (passenger) edges
461
for (const NBEdge* e : chosenSet) {
462
if ((e->getPermissions() & SVC_PASSENGER) != 0) {
463
std::vector<NBEdge*> parallelBikeEdges;
464
for (NBEdge* cand : toProc) {
465
if ((cand->getPermissions() & ~SVC_PEDESTRIAN) == SVC_BICYCLE) {
466
double angle = fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), cand->getAngleAtNode(cand->getToNode())));
467
if (angle < 30) {
468
// roughly parallel
469
parallelBikeEdges.push_back(cand);
470
}
471
}
472
}
473
for (NBEdge* be : parallelBikeEdges) {
474
#ifdef DEBUG_PHASES
475
if (DEBUGCOND) {
476
std::cout << " chosen=" << e->getID() << " be=" << be->getID() << "\n";
477
}
478
#endif
479
chosenSet.insert(be);
480
toProc.erase(std::find(toProc.begin(), toProc.end(), be));
481
}
482
}
483
}
484
// plain straight movers
485
double maxSpeed = 0;
486
bool haveGreen = false;
487
for (const NBEdge* const fromEdge : incoming) {
488
const bool inChosen = chosenSet.count(fromEdge) != 0;
489
const int numLanes = fromEdge->getNumLanes();
490
for (int i2 = 0; i2 < numLanes; i2++) {
491
for (const NBEdge::Connection& approached : fromEdge->getConnectionsFromLane(i2)) {
492
if (!fromEdge->mayBeTLSControlled(i2, approached.toEdge, approached.toLane)) {
493
continue;
494
}
495
if (inChosen) {
496
state[pos] = 'G';
497
haveGreen = true;
498
maxSpeed = MAX2(maxSpeed, fromEdge->getSpeed());
499
} else {
500
state[pos] = 'r';
501
}
502
++pos;
503
}
504
}
505
}
506
if (!haveGreen) {
507
continue;
508
}
509
510
#ifdef DEBUG_PHASES
511
if (DEBUGCOND) {
512
std::cout << " state after plain straight movers " << state << "\n";
513
}
514
#endif
515
if (!isNEMA) {
516
// correct behaviour for those that are not in chosen, but may drive, though
517
state = allowCompatible(state, fromEdges, toEdges, fromLanes, toLanes);
518
#ifdef DEBUG_PHASES
519
if (DEBUGCOND) {
520
std::cout << " state after allowing compatible " << state << "\n";
521
}
522
#endif
523
if (groupTram) {
524
state = allowByVClass(state, fromEdges, toEdges, SVC_TRAM);
525
} else if (groupOther) {
526
state = allowByVClass(state, fromEdges, toEdges, SVC_PEDESTRIAN | SVC_BICYCLE | SVC_DELIVERY);
527
}
528
#ifdef DEBUG_PHASES
529
if (DEBUGCOND) {
530
std::cout << " state after grouping by vClass " << state << " (groupTram=" << groupTram << " groupOther=" << groupOther << ")\n";
531
}
532
#endif
533
if (groupOpposites || chosen.first->getToNode()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED) {
534
state = allowUnrelated(state, fromEdges, toEdges, isTurnaround, crossings);
535
}
536
#ifdef DEBUG_PHASES
537
if (DEBUGCOND) {
538
std::cout << " state after finding allowUnrelated " << state << "\n";
539
}
540
#endif
541
}
542
// correct behaviour for those that have to wait (mainly left-mover)
543
bool haveForbiddenLeftMover = false;
544
std::vector<bool> rightTurnConflicts(pos, false);
545
std::vector<bool> mergeConflicts(pos, false);
546
state = correctConflicting(state, fromEdges, toEdges, isTurnaround, fromLanes, toLanes, hadGreenMajor, haveForbiddenLeftMover, rightTurnConflicts, mergeConflicts);
547
for (int i1 = 0; i1 < pos; ++i1) {
548
if (state[i1] == 'G') {
549
hadGreenMajor[i1] = true;
550
}
551
}
552
#ifdef DEBUG_PHASES
553
if (DEBUGCOND) {
554
std::cout << " state after correcting left movers=" << state << "\n";
555
}
556
#endif
557
558
std::vector<bool> leftGreen(pos, false);
559
// check whether at least one left-turn lane exist
560
bool foundLeftTurnLane = false;
561
for (int i1 = 0; i1 < pos; ++i1) {
562
if (state[i1] == 'g' && !rightTurnConflicts[i1] && !mergeConflicts[i1] && hasTurnLane[i1]) {
563
foundLeftTurnLane = true;
564
}
565
}
566
const bool buildLeftGreenPhase = (haveForbiddenLeftMover && !myHaveSinglePhase && leftTurnTime > 0 && foundLeftTurnLane
567
&& groupOpposites && !groupTram && !groupOther);
568
569
// find indices for exclusive left green phase and apply option minor-left.max-speed
570
for (int i1 = 0; i1 < pos; ++i1) {
571
if (state[i1] == 'g' && !rightTurnConflicts[i1] && !mergeConflicts[i1]
572
// only activate turn-around together with a real left-turn
573
&& (!isTurnaround[i1] || (i1 > 0 && leftGreen[i1 - 1]))) {
574
leftGreen[i1] = true;
575
if (fromEdges[i1]->getSpeed() > minorLeftSpeedThreshold) {
576
if (buildLeftGreenPhase) {
577
state[i1] = 'r';
578
//std::cout << " disabling minorLeft " << i1 << " (speed=" << fromEdges[i1]->getSpeed() << " thresh=" << minorLeftSpeedThreshold << ")\n";
579
} else if (!isTurnaround[i1]) {
580
WRITE_WARNINGF(TL("Minor green from edge '%' to edge '%' exceeds %m/s. Maybe a left-turn lane is missing."),
581
fromEdges[i1]->getID(), toEdges[i1]->getID(), minorLeftSpeedThreshold);
582
}
583
}
584
}
585
}
586
587
#ifdef DEBUG_PHASES
588
if (DEBUGCOND) {
589
std::cout << getID() << " state=" << state << " buildLeft=" << buildLeftGreenPhase << " hFLM=" << haveForbiddenLeftMover << " turnLane=" << foundLeftTurnLane
590
<< " \nrtC=" << toString(rightTurnConflicts)
591
<< " \nmC=" << toString(mergeConflicts)
592
<< " \nhTL=" << toString(hasTurnLane)
593
<< " \nlGr=" << toString(leftGreen)
594
<< "\n";
595
}
596
#endif
597
straightStates.push_back(state);
598
599
const std::string vehicleState = state; // backup state before pedestrian modifications
600
greenPhases.push_back((int)logic->getPhases().size());
601
602
// 5s at 50km/h, 10s at 80km/h, rounded to full seconds
603
const double minDurBySpeed = maxSpeed * 3.6 / 6 - 3.3;
604
SUMOTime minDur = MAX2(minMinDur, TIME2STEPS(floor(minDurBySpeed + 0.5)));
605
if (chosen.first->getPermissions() == SVC_TRAM && (chosen.second == nullptr || chosen.second->getPermissions() == SVC_TRAM)) {
606
// shorter minDuration for tram phase (only if the phase is
607
// exclusively for tram)
608
bool tramExclusive = true;
609
for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
610
if (state[i1] == 'G') {
611
SVCPermissions linkPerm = (fromEdges[i1]->getPermissions() & toEdges[i1]->getPermissions());
612
if (linkPerm != SVC_TRAM) {
613
tramExclusive = false;
614
break;
615
}
616
}
617
}
618
if (tramExclusive) {
619
// one tram per actuated phase
620
minDur = TIME2STEPS(1);
621
}
622
}
623
624
state = addPedestrianPhases(logic, greenTime, minDur, maxDur, earliestEnd, latestEnd, state, crossings, fromEdges, toEdges);
625
// pedestrians have 'r' from here on
626
for (int i1 = pos; i1 < (int)state.size(); ++i1) {
627
state[i1] = 'r';
628
}
629
if (brakingTime > 0) {
630
SUMOTime maxCross = 0;
631
// build yellow (straight)
632
for (int i1 = 0; i1 < pos; ++i1) {
633
if (state[i1] != 'G' && state[i1] != 'g') {
634
continue;
635
}
636
if ((vehicleState[i1] >= 'a' && vehicleState[i1] <= 'z')
637
&& buildLeftGreenPhase
638
&& !rightTurnConflicts[i1]
639
&& !mergeConflicts[i1]
640
&& leftGreen[i1]) {
641
continue;
642
}
643
state[i1] = 'y';
644
maxCross = MAX2(maxCross, crossingTime[i1]);
645
}
646
// add step
647
logic->addStep(brakingTime, state);
648
// add optional all-red state
649
if (!buildLeftGreenPhase) {
650
if (myLayout == TrafficLightLayout::ALTERNATE_ONEWAY) {
651
allRedTime = computeEscapeTime(state, fromEdges, toEdges);
652
}
653
buildAllRedState(allRedTime + MAX2(0ll, maxCross - brakingTime - allRedTime), logic, state);
654
}
655
}
656
657
658
if (buildLeftGreenPhase) {
659
// build left green
660
for (int i1 = 0; i1 < pos; ++i1) {
661
if (state[i1] == 'Y' || state[i1] == 'y') {
662
state[i1] = 'r';
663
continue;
664
}
665
if (leftGreen[i1]) {
666
state[i1] = 'G';
667
}
668
}
669
leftStates.push_back(state);
670
state = allowCompatible(state, fromEdges, toEdges, fromLanes, toLanes);
671
state = correctConflicting(state, fromEdges, toEdges, isTurnaround, fromLanes, toLanes, hadGreenMajor, haveForbiddenLeftMover, rightTurnConflicts, mergeConflicts);
672
bool buildMixedGreenPhase = false;
673
std::vector<bool> mixedGreen(pos, false);
674
const std::string oldState = state;
675
if (noMixed) {
676
state = correctMixed(state, fromEdges, fromLanes, buildMixedGreenPhase, mixedGreen);
677
}
678
if (state != oldState) {
679
for (int i1 = 0; i1 < pos; ++i1) {
680
if (mixedGreen[i1]) {
681
// patch previous yellow and allred phase
682
int yellowIndex = (int)logic->getPhases().size() - 1;
683
if (allRedTime > 0) {
684
logic->setPhaseState(yellowIndex--, i1, LINKSTATE_TL_RED);
685
}
686
if (brakingTime > 0) {
687
logic->setPhaseState(yellowIndex, i1, LINKSTATE_TL_YELLOW_MINOR);
688
}
689
}
690
}
691
state = allowCompatible(state, fromEdges, toEdges, fromLanes, toLanes);
692
}
693
694
// add step
695
logic->addStep(leftTurnTime, state, minDur, maxDur, earliestEnd, latestEnd);
696
697
// build left yellow
698
if (brakingTime > 0) {
699
SUMOTime maxCross = 0;
700
for (int i1 = 0; i1 < pos; ++i1) {
701
if (state[i1] != 'G' && state[i1] != 'g') {
702
continue;
703
}
704
state[i1] = 'y';
705
maxCross = MAX2(maxCross, crossingTime[i1]);
706
}
707
// add step
708
logic->addStep(brakingTime, state);
709
// add optional all-red state
710
buildAllRedState(allRedTime + MAX2(0ll, maxCross - brakingTime - allRedTime), logic, state);
711
}
712
713
if (buildMixedGreenPhase) {
714
// build mixed green
715
// @todo if there is no left green phase we might want to build two
716
// mixed-green phases but then we should consider avoid a common
717
// opposite phase for this direction
718
719
for (int i1 = 0; i1 < pos; ++i1) {
720
if (state[i1] == 'Y' || state[i1] == 'y') {
721
state[i1] = 'r';
722
continue;
723
}
724
if (mixedGreen[i1]) {
725
state[i1] = 'G';
726
}
727
}
728
state = allowCompatible(state, fromEdges, toEdges, fromLanes, toLanes);
729
state = correctConflicting(state, fromEdges, toEdges, isTurnaround, fromLanes, toLanes, hadGreenMajor, haveForbiddenLeftMover, rightTurnConflicts, mergeConflicts);
730
731
// add step
732
logic->addStep(leftTurnTime, state, minDur, maxDur, earliestEnd, latestEnd);
733
734
// build mixed yellow
735
if (brakingTime > 0) {
736
SUMOTime maxCross = 0;
737
for (int i1 = 0; i1 < pos; ++i1) {
738
if (state[i1] != 'G' && state[i1] != 'g') {
739
continue;
740
}
741
state[i1] = 'y';
742
maxCross = MAX2(maxCross, crossingTime[i1]);
743
}
744
// add step
745
logic->addStep(brakingTime, state);
746
// add optional all-red state
747
buildAllRedState(allRedTime + MAX2(0ll, maxCross - brakingTime - allRedTime), logic, state);
748
}
749
}
750
751
} else if (isNEMA) {
752
std::string& s = straightStates.back();
753
std::string leftState = s;
754
for (int ii = 0; ii < pos; ++ii) {
755
if (s[ii] != 'r') {
756
NBEdge* fromEdge = fromEdges[ii];
757
NBEdge* toEdge = toEdges[ii];
758
LinkDirection dir = fromEdge->getToNode()->getDirection(fromEdge, toEdge);
759
if (hasTurnLane[ii] && (dir == LinkDirection::LEFT || dir == LinkDirection::TURN)) {
760
s[ii] = 'r';
761
leftState[ii] = 'G';
762
} else {
763
leftState[ii] = 'r';
764
}
765
}
766
}
767
leftStates.push_back(leftState);
768
}
769
// fix edges within joined traffic lights that did not get the green light yet
770
if (myEdgesWithin.size() > 0 && !isNEMA && toProc.size() == 0 && !onlyConts) {
771
addGreenWithin(logic, fromEdges, toProc);
772
}
773
}
774
// fix pedestrian crossings that did not get the green light yet
775
if (crossings.size() > 0) {
776
addPedestrianScramble(logic, totalNumLinks, TIME2STEPS(10), brakingTime, crossings, fromEdges, toEdges);
777
}
778
// add optional red phase if there were no foes
779
if (logic->getPhases().size() == 2 && brakingTime > 0
780
&& OptionsCont::getOptions().getInt("tls.red.time") > 0) {
781
const SUMOTime redTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.red.time"));
782
logic->addStep(redTime, std::string(totalNumLinks, 'r'));
783
}
784
785
if (myLayout == TrafficLightLayout::ALTERNATE_ONEWAY) {
786
// exiting the oneway section should always be possible
787
deactivateInsideEdges(logic, fromEdges);
788
}
789
if (isNEMA) {
790
NBTrafficLightLogic* nemaLogic = buildNemaPhases(fromEdges, toEdges, crossings, chosenList, straightStates, leftStates);
791
if (nemaLogic == nullptr) {
792
WRITE_WARNINGF(TL("Generating NEMA phases is not supported for traffic light '%' with % incoming edges. Using tlType 'actuated' as fallback"), getID(), incoming.size());
793
logic->setType(TrafficLightType::ACTUATED);
794
setType(TrafficLightType::ACTUATED);
795
} else {
796
delete logic;
797
logic = nemaLogic;
798
}
799
}
800
801
SUMOTime totalDuration = logic->getDuration();
802
803
if ((OptionsCont::getOptions().isDefault("tls.green.time") || !OptionsCont::getOptions().isDefault("tls.cycle.time")) && !isNEMA) {
804
const SUMOTime cycleTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.cycle.time"));
805
// adapt to cycle time by changing the duration of the green phases
806
SUMOTime minGreenDuration = SUMOTime_MAX;
807
for (std::vector<int>::const_iterator it = greenPhases.begin(); it != greenPhases.end(); ++it) {
808
const SUMOTime dur = logic->getPhases()[*it].duration;
809
minGreenDuration = MIN2(minGreenDuration, dur);
810
}
811
const int patchSeconds = (int)(STEPS2TIME(cycleTime - totalDuration) / (double)greenPhases.size());
812
const int patchSecondsRest = (int)(STEPS2TIME(cycleTime - totalDuration)) - patchSeconds * (int)greenPhases.size();
813
//std::cout << "cT=" << cycleTime << " td=" << totalDuration << " pS=" << patchSeconds << " pSR=" << patchSecondsRest << "\n";
814
if (STEPS2TIME(minGreenDuration) + patchSeconds < MIN_GREEN_TIME
815
|| STEPS2TIME(minGreenDuration) + patchSeconds + patchSecondsRest < MIN_GREEN_TIME
816
|| greenPhases.size() == 0) {
817
if (getID() != DummyID) {
818
WRITE_WARNINGF(TL("The traffic light '%' cannot be adapted to a cycle time of %."), getID(), time2string(cycleTime));
819
}
820
// @todo use a multiple of cycleTime ?
821
} else {
822
for (std::vector<int>::const_iterator it = greenPhases.begin(); it != greenPhases.end(); ++it) {
823
logic->setPhaseDuration(*it, logic->getPhases()[*it].duration + TIME2STEPS(patchSeconds));
824
}
825
if (greenPhases.size() > 0) {
826
logic->setPhaseDuration(greenPhases.front(), logic->getPhases()[greenPhases.front()].duration + TIME2STEPS(patchSecondsRest));
827
}
828
totalDuration = logic->getDuration();
829
}
830
}
831
832
// check for coherent signal sequence and remove yellow if preceded and followed by green
833
const std::vector<NBTrafficLightLogic::PhaseDefinition>& allPhases = logic->getPhases();
834
const int phaseCount = (int)allPhases.size();
835
const int stateSize = (int)logic->getNumLinks();
836
for (int i = 0; i < phaseCount; ++i) {
837
std::string currState = allPhases[i].state;
838
const int prevIndex = (i == 0) ? phaseCount - 1 : i - 1;
839
const std::string prevState = allPhases[prevIndex].state;
840
const std::string nextState = allPhases[(i + 1) % phaseCount].state;
841
bool updatedState = false;
842
for (int i1 = 0; i1 < stateSize; ++i1) {
843
if (currState[i1] == 'y' && (nextState[i1] == prevState[i1] || nextState[i1] == 'G') && (prevState[i1] == 'g' || prevState[i1] == 'G')) {
844
logic->setPhaseState(i, i1, (LinkState)prevState[i1]);
845
updatedState = true;
846
}
847
}
848
UNUSED_PARAMETER(updatedState); // disable warning
849
#ifdef DEBUG_PHASES
850
if (DEBUGCOND) {
851
if (updatedState) {
852
std::cout << getID() << " state of phase index " << i << " was patched due to yellow in between green\n";
853
}
854
855
}
856
#endif
857
}
858
859
860
myExtraConflictsReady = true;
861
// this computation only makes sense for single nodes
862
if (myControlledNodes.size() == 1) {
863
myNeedsContRelationReady = true;
864
}
865
if (totalDuration > 0) {
866
if (totalDuration > 3 * (greenTime + 2 * brakingTime + leftTurnTime) && !isNEMA && getID() != DummyID) {
867
WRITE_WARNINGF(TL("The traffic light '%' has a high cycle time of %."), getID(), time2string(totalDuration));
868
}
869
logic->closeBuilding();
870
return logic;
871
} else {
872
delete logic;
873
return nullptr;
874
}
875
}
876
877
878
bool
879
NBOwnTLDef::hasCrossing(const NBEdge* from, const NBEdge* to, const std::vector<NBNode::Crossing*>& crossings) {
880
assert(to != 0);
881
for (auto c : crossings) {
882
const NBNode::Crossing& cross = *c;
883
// only check connections at this crossings node
884
if (to->getFromNode() == cross.node) {
885
for (EdgeVector::const_iterator it_e = cross.edges.begin(); it_e != cross.edges.end(); ++it_e) {
886
const NBEdge* edge = *it_e;
887
if (edge == from || edge == to) {
888
return true;
889
}
890
}
891
}
892
}
893
return false;
894
}
895
896
897
std::string
898
NBOwnTLDef::addPedestrianPhases(NBTrafficLightLogic* logic, const SUMOTime greenTime, const SUMOTime minDur, const SUMOTime maxDur,
899
const SUMOTime earliestEnd, const SUMOTime latestEnd,
900
std::string state, const std::vector<NBNode::Crossing*>& crossings, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
901
// compute based on length of the crossing if not set by the user
902
const SUMOTime pedClearingTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.crossing-clearance.time"));
903
// compute if not set by user: must be able to reach the middle of the second "Richtungsfahrbahn"
904
const SUMOTime minPedTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.crossing-min.time"));
905
const std::string orig = state;
906
state = patchStateForCrossings(state, crossings, fromEdges, toEdges);
907
if (orig == state) {
908
// add step
909
logic->addStep(greenTime, state, minDur, maxDur, earliestEnd, latestEnd);
910
} else {
911
const SUMOTime pedTime = greenTime - pedClearingTime;
912
if (pedTime >= minPedTime) {
913
// ensure clearing time for pedestrians
914
const bool isSimpleActuatedCrossing = logic->getType() == TrafficLightType::ACTUATED
915
&& minDur == UNSPECIFIED_DURATION && logic->getPhases().size() == 2;
916
if (isSimpleActuatedCrossing) {
917
// permit green phase to extend when there are no pedestrians
918
logic->setPhaseNext(0, {0, 1});
919
}
920
logic->addStep(pedTime, state, minDur, maxDur, earliestEnd, latestEnd);
921
#ifdef DEBUG_PHASES
922
if (DEBUGCOND2(logic)) {
923
std::cout << " intermidate state for addPedestrianPhases " << state << "\n";
924
}
925
#endif
926
for (auto cross : crossings) {
927
if (cross->tlLinkIndex >= (int)fromEdges.size() || fromEdges[cross->tlLinkIndex] == nullptr) {
928
state[cross->tlLinkIndex] = 'r';
929
}
930
if (cross->tlLinkIndex2 >= 0 && (cross->tlLinkIndex2 >= (int)fromEdges.size() || fromEdges[cross->tlLinkIndex2] == nullptr)) {
931
state[cross->tlLinkIndex2] = 'r';
932
}
933
}
934
logic->addStep(pedClearingTime, state);
935
} else {
936
state = orig;
937
// not safe for pedestrians.
938
logic->addStep(greenTime, state, minDur, maxDur, earliestEnd, latestEnd);
939
}
940
}
941
#ifdef DEBUG_PHASES
942
if (DEBUGCOND2(logic)) {
943
std::cout << " state after addPedestrianPhases " << state << "\n";
944
}
945
#endif
946
return state;
947
}
948
949
950
std::string
951
NBOwnTLDef::patchStateForCrossings(const std::string& state, const std::vector<NBNode::Crossing*>& crossings, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
952
std::string result = state;
953
for (const NBNode::Crossing* cross : crossings) {
954
bool isForbidden = false;
955
for (int i2 = 0; i2 < (int)fromEdges.size() && !isForbidden; ++i2) {
956
// only check connections at this crossings node
957
if (fromEdges[i2] != 0 && toEdges[i2] != 0 && fromEdges[i2]->getToNode() == cross->node) {
958
for (EdgeVector::const_iterator it = cross->edges.begin(); it != cross->edges.end(); ++it) {
959
const NBEdge* edge = *it;
960
const LinkDirection i2dir = cross->node->getDirection(fromEdges[i2], toEdges[i2]);
961
if (state[i2] != 'r' && state[i2] != 's' && (edge == fromEdges[i2] ||
962
(edge == toEdges[i2] && (i2dir == LinkDirection::STRAIGHT || i2dir == LinkDirection::PARTLEFT || i2dir == LinkDirection::PARTRIGHT)))) {
963
isForbidden = true;
964
break;
965
}
966
}
967
}
968
}
969
const int i1 = cross->tlLinkIndex;
970
assert(i1 >= 0 && i1 < (int)result.size());
971
const char newState = isForbidden ? 'r' : 'G';
972
if (i1 < (int)toEdges.size() && toEdges[i1] != nullptr && (result[i1] != newState || !isForbidden)) {
973
if (cross->tlID != DummyID) {
974
WRITE_WARNINGF(TL("Custom crossing linkIndex % conflicts with vehicular connections at tlLogic '%'"), i1, cross->tlID);
975
}
976
} else {
977
result[i1] = newState;
978
}
979
if (cross->tlLinkIndex2 >= 0) {
980
const int i2 = cross->tlLinkIndex2;
981
if (i2 < (int)toEdges.size() && toEdges[i2] != nullptr && (result[i2] != newState || !isForbidden)) {
982
if (cross->tlID != DummyID) {
983
WRITE_WARNINGF(TL("Custom crossing linkIndex2 % conflicts with vehicular connections at tlLogic '%'"), i2, cross->tlID);
984
}
985
} else {
986
result[i2] = newState;
987
}
988
}
989
}
990
991
// correct behaviour for roads that are in conflict with a pedestrian crossing
992
for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
993
if (result[i1] == 'G') {
994
for (const NBNode::Crossing* cross : crossings) {
995
const int i2 = cross->tlLinkIndex;
996
const int i3 = cross->tlLinkIndex2;
997
if (fromEdges[i1] != 0 && toEdges[i1] != 0 && fromEdges[i1]->getToNode() == cross->node) {
998
if ((result[i2] == 'G' || (i3 >= 0 && result[i3] == 'G'))
999
&& cross->node->mustBrakeForCrossing(fromEdges[i1], toEdges[i1], *cross)) {
1000
result[i1] = 'g';
1001
break;
1002
}
1003
}
1004
}
1005
}
1006
}
1007
return result;
1008
}
1009
1010
1011
std::string
1012
NBOwnTLDef::patchNEMAStateForCrossings(const std::string& state,
1013
const std::vector<NBNode::Crossing*>& crossings,
1014
const EdgeVector& fromEdges,
1015
const EdgeVector& toEdges,
1016
const NBEdge* greenEdge, NBEdge* otherChosen) {
1017
std::string result = state;
1018
const int pos = (int)(state.size() - crossings.size()); // number of controlled vehicle links
1019
const EdgeVector& all = greenEdge->getToNode()->getEdges();
1020
EdgeVector::const_iterator start = std::find(all.begin(), all.end(), greenEdge);
1021
1022
// permit crossings over edges between the current green edge and it's straight continuation
1023
const NBEdge* endEdge = nullptr;
1024
for (int i = 0; i < (int)state.size(); i++) {
1025
if (state[i] == 'G' && fromEdges[i] == greenEdge
1026
&& greenEdge->getToNode()->getDirection(greenEdge, toEdges[i]) == LinkDirection::STRAIGHT) {
1027
// straight edge found
1028
endEdge = toEdges[i];
1029
break;
1030
}
1031
}
1032
if (endEdge == nullptr) {
1033
endEdge = otherChosen;
1034
}
1035
if (endEdge == nullptr) {
1036
// try to find the reverse edge of the green edge
1037
auto itCW = start;
1038
NBContHelper::nextCW(all, itCW);
1039
if ((*itCW)->getFromNode() == greenEdge->getToNode()) {
1040
endEdge = *itCW;
1041
}
1042
}
1043
if (endEdge == nullptr) {
1044
// at least prevent an infinite loop
1045
endEdge = greenEdge;
1046
}
1047
//std::cout << " patchNEMAStateForCrossings green=" << greenEdge->getID() << " other=" << Named::getIDSecure(otherChosen) << " end=" << Named::getIDSecure(end) << " all=" << toString(all) << "\n";
1048
1049
EdgeVector::const_iterator end = std::find(all.begin(), all.end(), endEdge);
1050
if (end == all.end()) {
1051
// at least prevent an infinite loop
1052
end = start;
1053
}
1054
auto it = start;
1055
NBContHelper::nextCCW(all, it);
1056
for (; it != end; NBContHelper::nextCCW(all, it)) {
1057
for (int ic = 0; ic < (int)crossings.size(); ++ic) {
1058
const int i1 = pos + ic;
1059
const NBNode::Crossing& cross = *crossings[ic];
1060
for (const NBEdge* crossed : cross.edges) {
1061
//std::cout << " cand=" << (*it)->getID() << " crossed=" << crossed->getID() << "\n";
1062
if (crossed == *it) {
1063
result[i1] = 'G';
1064
break;
1065
}
1066
}
1067
}
1068
}
1069
// correct behaviour for roads that are in conflict with a pedestrian crossing
1070
for (int i1 = 0; i1 < pos; ++i1) {
1071
if (result[i1] == 'G') {
1072
for (int ic = 0; ic < (int)crossings.size(); ++ic) {
1073
const NBNode::Crossing& crossing = *crossings[ic];
1074
const int i2 = pos + ic;
1075
if (result[i2] == 'G' && crossing.node->mustBrakeForCrossing(fromEdges[i1], toEdges[i1], crossing)) {
1076
result[i1] = 'g';
1077
break;
1078
}
1079
}
1080
}
1081
}
1082
return result;
1083
}
1084
1085
1086
void
1087
NBOwnTLDef::collectLinks() {
1088
myControlledLinks.clear();
1089
collectAllLinks(myControlledLinks);
1090
}
1091
1092
1093
void
1094
NBOwnTLDef::setTLControllingInformation() const {
1095
// set the information about the link's positions within the tl into the
1096
// edges the links are starting at, respectively
1097
for (NBConnectionVector::const_iterator j = myControlledLinks.begin(); j != myControlledLinks.end(); ++j) {
1098
const NBConnection& conn = *j;
1099
NBEdge* edge = conn.getFrom();
1100
edge->setControllingTLInformation(conn, getID());
1101
}
1102
}
1103
1104
1105
void
1106
NBOwnTLDef::remapRemoved(NBEdge* /*removed*/, const EdgeVector& /*incoming*/,
1107
const EdgeVector& /*outgoing*/) {}
1108
1109
1110
void
1111
NBOwnTLDef::replaceRemoved(NBEdge* /*removed*/, int /*removedLane*/,
1112
NBEdge* /*by*/, int /*byLane*/, bool /*incoming*/) {}
1113
1114
1115
void
1116
NBOwnTLDef::initNeedsContRelation() const {
1117
if (!myNeedsContRelationReady) {
1118
if (myControlledNodes.size() > 0) {
1119
// we use a dummy node just to maintain const-correctness
1120
myNeedsContRelation.clear();
1121
for (NBNode* n : myControlledNodes) {
1122
NBOwnTLDef dummy(DummyID, n, 0, TrafficLightType::STATIC);
1123
dummy.setParticipantsInformation();
1124
NBTrafficLightLogic* tllDummy = dummy.computeLogicAndConts(0, true);
1125
delete tllDummy;
1126
myNeedsContRelation.insert(dummy.myNeedsContRelation.begin(), dummy.myNeedsContRelation.end());
1127
n->removeTrafficLight(&dummy);
1128
}
1129
#ifdef DEBUG_CONTRELATION
1130
if (DEBUGCOND) {
1131
std::cout << " contRelations at " << getID() << " prog=" << getProgramID() << ":\n";
1132
for (const StreamPair& s : myNeedsContRelation) {
1133
std::cout << " " << s.from1->getID() << "->" << s.to1->getID() << " foe " << s.from2->getID() << "->" << s.to2->getID() << "\n";
1134
}
1135
}
1136
#endif
1137
1138
}
1139
myNeedsContRelationReady = true;
1140
}
1141
}
1142
1143
1144
EdgeVector
1145
NBOwnTLDef::getConnectedOuterEdges(const EdgeVector& incoming) {
1146
EdgeVector result = incoming;
1147
for (EdgeVector::iterator it = result.begin(); it != result.end();) {
1148
if ((*it)->getConnections().size() == 0 || (*it)->isInsideTLS()) {
1149
it = result.erase(it);
1150
} else {
1151
++it;
1152
}
1153
}
1154
return result;
1155
}
1156
1157
1158
std::string
1159
NBOwnTLDef::allowCompatible(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
1160
const std::vector<int>& fromLanes, const std::vector<int>& toLanes) {
1161
state = allowSingleEdge(state, fromEdges);
1162
#ifdef DEBUG_PHASES
1163
if (DEBUGCOND) {
1164
std::cout << " state after allowSingle " << state << "\n";
1165
}
1166
#endif
1167
if (myControlledNodes.size() > 1) {
1168
state = allowFollowers(state, fromEdges, toEdges);
1169
#ifdef DEBUG_PHASES
1170
if (DEBUGCOND) {
1171
std::cout << " state after allowFollowers " << state << "\n";
1172
}
1173
#endif
1174
state = allowPredecessors(state, fromEdges, toEdges, fromLanes, toLanes);
1175
#ifdef DEBUG_PHASES
1176
if (DEBUGCOND) {
1177
std::cout << " state after allowPredecessors " << state << "\n";
1178
}
1179
#endif
1180
}
1181
return state;
1182
}
1183
1184
1185
std::string
1186
NBOwnTLDef::allowSingleEdge(std::string state, const EdgeVector& fromEdges) {
1187
// if only one edge has green, ensure sure that all connections from that edge are green
1188
const int size = (int)fromEdges.size();
1189
NBEdge* greenEdge = nullptr;
1190
for (int i1 = 0; i1 < size; ++i1) {
1191
if (state[i1] == 'G') {
1192
if (greenEdge == nullptr) {
1193
greenEdge = fromEdges[i1];
1194
} else if (greenEdge != fromEdges[i1]) {
1195
return state;
1196
}
1197
}
1198
}
1199
if (greenEdge != nullptr) {
1200
for (int i1 = 0; i1 < size; ++i1) {
1201
if (fromEdges[i1] == greenEdge) {
1202
state[i1] = 'G';
1203
}
1204
}
1205
}
1206
return state;
1207
}
1208
1209
1210
std::string
1211
NBOwnTLDef::allowFollowers(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
1212
// check continuation within joined traffic lights
1213
bool check = true;
1214
while (check) {
1215
check = false;
1216
for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1217
if (state[i1] == 'G') {
1218
continue;
1219
}
1220
if (forbidden(state, i1, fromEdges, toEdges, true)) {
1221
continue;
1222
}
1223
bool followsChosen = false;
1224
for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1225
if (state[i2] == 'G' && fromEdges[i1] == toEdges[i2]) {
1226
followsChosen = true;
1227
break;
1228
}
1229
}
1230
if (followsChosen) {
1231
state[i1] = 'G';
1232
check = true;
1233
}
1234
}
1235
}
1236
return state;
1237
}
1238
1239
1240
std::string
1241
NBOwnTLDef::allowPredecessors(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
1242
const std::vector<int>& fromLanes, const std::vector<int>& toLanes) {
1243
// also allow predecessors of chosen edges if the lanes match and there is no conflict
1244
// (must be done after the followers are done because followers are less specific)
1245
bool check = true;
1246
while (check) {
1247
check = false;
1248
for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1249
if (state[i1] == 'G') {
1250
continue;
1251
}
1252
if (forbidden(state, i1, fromEdges, toEdges, false)) {
1253
continue;
1254
}
1255
bool preceedsChosen = false;
1256
for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1257
if (state[i2] == 'G' && fromEdges[i2] == toEdges[i1]
1258
&& fromLanes[i2] == toLanes[i1]) {
1259
preceedsChosen = true;
1260
break;
1261
}
1262
}
1263
if (preceedsChosen) {
1264
state[i1] = 'G';
1265
check = true;
1266
}
1267
}
1268
}
1269
return state;
1270
}
1271
1272
1273
std::string
1274
NBOwnTLDef::allowUnrelated(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
1275
const std::vector<bool>& isTurnaround,
1276
const std::vector<NBNode::Crossing*>& crossings) {
1277
for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1278
if (state[i1] == 'G') {
1279
continue;
1280
}
1281
bool isForbidden = false;
1282
for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1283
if (state[i2] == 'G' && !isTurnaround[i2] &&
1284
(forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true) || forbids(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2], true))) {
1285
isForbidden = true;
1286
break;
1287
}
1288
}
1289
if (!isForbidden && !hasCrossing(fromEdges[i1], toEdges[i1], crossings)) {
1290
state[i1] = 'G';
1291
}
1292
}
1293
return state;
1294
}
1295
1296
1297
std::string
1298
NBOwnTLDef::allowByVClass(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges, SVCPermissions perm) {
1299
for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1300
SVCPermissions linkPerm = (fromEdges[i1]->getPermissions() & toEdges[i1]->getPermissions());
1301
if ((linkPerm & ~perm) == 0) {
1302
state[i1] = 'G';
1303
}
1304
}
1305
return state;
1306
}
1307
1308
1309
bool
1310
NBOwnTLDef::forbidden(const std::string& state, int index, const EdgeVector& fromEdges, const EdgeVector& toEdges, bool allowCont) {
1311
for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1312
if (state[i2] == 'G' && foes(fromEdges[i2], toEdges[i2], fromEdges[index], toEdges[index])) {
1313
if (!allowCont || (
1314
!needsCont(fromEdges[i2], toEdges[i2], fromEdges[index], toEdges[index]) &&
1315
!needsCont(fromEdges[index], toEdges[index], fromEdges[i2], toEdges[i2]))) {
1316
return true;
1317
}
1318
}
1319
}
1320
return false;
1321
}
1322
1323
1324
std::string
1325
NBOwnTLDef::correctConflicting(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
1326
const std::vector<bool>& isTurnaround,
1327
const std::vector<int>& fromLanes,
1328
const std::vector<int>& toLanes,
1329
const std::vector<bool>& hadGreenMajor,
1330
bool& haveForbiddenLeftMover,
1331
std::vector<bool>& rightTurnConflicts,
1332
std::vector<bool>& mergeConflicts) {
1333
const bool controlledWithin = !OptionsCont::getOptions().getBool("tls.uncontrolled-within");
1334
for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1335
if (state[i1] == 'G') {
1336
for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1337
if ((state[i2] == 'G' || state[i2] == 'g')) {
1338
if (NBNode::rightTurnConflict(
1339
fromEdges[i1], toEdges[i1], fromLanes[i1], fromEdges[i2], toEdges[i2], fromLanes[i2])) {
1340
rightTurnConflicts[i1] = true;
1341
}
1342
if (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true, controlledWithin) || rightTurnConflicts[i1]) {
1343
state[i1] = 'g';
1344
if (myControlledNodes.size() == 1) {
1345
myNeedsContRelation.insert(StreamPair(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2]));
1346
#ifdef DEBUG_CONTRELATION
1347
if (DEBUGCOND) {
1348
std::cout << getID() << " p=" << getProgramID() << " contRel: " << fromEdges[i1]->getID() << "->" << toEdges[i1]->getID()
1349
<< " foe " << fromEdges[i2]->getID() << "->" << toEdges[i2]->getID() << "\n";
1350
}
1351
#endif
1352
}
1353
if (!isTurnaround[i1] && !hadGreenMajor[i1] && !rightTurnConflicts[i1]) {
1354
haveForbiddenLeftMover = true;
1355
}
1356
} else if (fromEdges[i1] == fromEdges[i2]
1357
&& fromLanes[i1] != fromLanes[i2]
1358
&& toEdges[i1] == toEdges[i2]
1359
&& toLanes[i1] == toLanes[i2]
1360
&& fromEdges[i1]->getToNode()->mergeConflictYields(fromEdges[i1], fromLanes[i1], fromLanes[i2], toEdges[i1], toLanes[i1])) {
1361
mergeConflicts[i1] = true;
1362
state[i1] = 'g';
1363
}
1364
}
1365
}
1366
}
1367
if (state[i1] == 'r') {
1368
if (fromEdges[i1]->getToNode()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED &&
1369
fromEdges[i1]->getToNode()->getDirection(fromEdges[i1], toEdges[i1]) == LinkDirection::RIGHT) {
1370
state[i1] = 's';
1371
// do not allow right-on-red when in conflict with exclusive left-turn phase
1372
for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1373
if (state[i2] == 'G' && !isTurnaround[i2] &&
1374
(forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true) ||
1375
forbids(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2], true))) {
1376
const LinkDirection foeDir = fromEdges[i2]->getToNode()->getDirection(fromEdges[i2], toEdges[i2]);
1377
if (foeDir == LinkDirection::LEFT || foeDir == LinkDirection::PARTLEFT) {
1378
state[i1] = 'r';
1379
break;
1380
}
1381
}
1382
}
1383
if (state[i1] == 's') {
1384
// handle right-on-red conflicts
1385
for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1386
if (state[i2] == 'G' && !isTurnaround[i2] &&
1387
(forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true) ||
1388
forbids(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2], true))) {
1389
myExtraConflicts.insert(std::make_pair(i1, i2));
1390
}
1391
}
1392
}
1393
}
1394
}
1395
}
1396
return state;
1397
}
1398
1399
1400
std::string
1401
NBOwnTLDef::correctMixed(std::string state, const EdgeVector& fromEdges,
1402
const std::vector<int>& fromLanes,
1403
bool& buildMixedGreenPhase, std::vector<bool>& mixedGreen) {
1404
for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1405
if ((state[i1] == 'G' || state[i1] == 'g')) {
1406
for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1407
if (i1 != i2 && fromEdges[i1] == fromEdges[i2] && fromLanes[i1] == fromLanes[i2]
1408
&& state[i2] != 'G' && state[i2] != 'g') {
1409
state[i1] = state[i2];
1410
//std::cout << " mixedGreen i1=" << i1 << " i2=" << i2 << "\n";
1411
mixedGreen[i1] = true;
1412
if (fromEdges[i1]->getNumLanesThatAllow(SVC_PASSENGER) > 1) {
1413
buildMixedGreenPhase = true;
1414
}
1415
}
1416
}
1417
}
1418
}
1419
return state;
1420
}
1421
1422
1423
void
1424
NBOwnTLDef::addGreenWithin(NBTrafficLightLogic* logic, const EdgeVector& fromEdges, EdgeVector& toProc) {
1425
std::vector<bool> foundGreen(fromEdges.size(), false);
1426
for (const auto& phase : logic->getPhases()) {
1427
const std::string state = phase.state;
1428
for (int j = 0; j < (int)fromEdges.size(); j++) {
1429
LinkState ls = (LinkState)state[j];
1430
if (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR) {
1431
foundGreen[j] = true;
1432
}
1433
}
1434
}
1435
for (int j = 0; j < (int)foundGreen.size(); j++) {
1436
if (!foundGreen[j]) {
1437
NBEdge* e = fromEdges[j];
1438
if (std::find(toProc.begin(), toProc.end(), e) == toProc.end()) {
1439
toProc.push_back(e);
1440
}
1441
}
1442
}
1443
}
1444
1445
1446
void
1447
NBOwnTLDef::addPedestrianScramble(NBTrafficLightLogic* logic, int totalNumLinks, SUMOTime /* greenTime */, SUMOTime brakingTime,
1448
const std::vector<NBNode::Crossing*>& crossings, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
1449
// check both indices for each crossing (they may have green in different phases)
1450
std::vector<bool> foundGreen(crossings.size() * 2, false);
1451
const std::vector<NBTrafficLightLogic::PhaseDefinition>& phases = logic->getPhases();
1452
for (int i = 0; i < (int)phases.size(); i++) {
1453
const std::string state = phases[i].state;
1454
int j = 0;
1455
for (auto cross : crossings) {
1456
LinkState ls = (LinkState)state[cross->tlLinkIndex];
1457
LinkState ls2 = cross->tlLinkIndex2 >= 0 ? (LinkState)state[cross->tlLinkIndex2] : ls;
1458
if (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR) {
1459
foundGreen[j] = true;
1460
}
1461
if (ls2 == LINKSTATE_TL_GREEN_MAJOR || ls2 == LINKSTATE_TL_GREEN_MINOR) {
1462
foundGreen[j + crossings.size()] = true;
1463
}
1464
j++;
1465
}
1466
}
1467
#ifdef DEBUG_PHASES
1468
if (DEBUGCOND2(logic)) {
1469
std::cout << " foundCrossingGreen=" << toString(foundGreen) << "\n";
1470
}
1471
#endif
1472
for (int j = 0; j < (int)foundGreen.size(); j++) {
1473
if (!foundGreen[j]) {
1474
// add a phase where all pedestrians may walk, (preceded by a yellow phase and followed by a clearing phase)
1475
if (phases.size() > 0) {
1476
bool needYellowPhase = false;
1477
std::string state = phases.back().state;
1478
for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1479
if (state[i1] == 'G' || state[i1] == 'g') {
1480
state[i1] = 'y';
1481
needYellowPhase = true;
1482
}
1483
}
1484
// add yellow step
1485
if (needYellowPhase && brakingTime > 0) {
1486
logic->addStep(brakingTime, state);
1487
}
1488
}
1489
const SUMOTime pedClearingTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.crossing-clearance.time"));
1490
const SUMOTime scrambleTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.scramble.time"));
1491
addPedestrianPhases(logic, scrambleTime + pedClearingTime, UNSPECIFIED_DURATION,
1492
UNSPECIFIED_DURATION, UNSPECIFIED_DURATION, UNSPECIFIED_DURATION, std::string(totalNumLinks, 'r'), crossings, fromEdges, toEdges);
1493
break;
1494
}
1495
}
1496
}
1497
1498
1499
void
1500
NBOwnTLDef::buildAllRedState(SUMOTime allRedTime, NBTrafficLightLogic* logic, const std::string& state) {
1501
if (allRedTime > 0) {
1502
// build all-red phase
1503
std::string allRedState = state;
1504
for (int i = 0; i < (int)state.size(); i++) {
1505
if (allRedState[i] == 'Y' || allRedState[i] == 'y') {
1506
allRedState[i] = 'r';
1507
}
1508
}
1509
logic->addStep(TIME2STEPS(ceil(STEPS2TIME(allRedTime))), allRedState);
1510
}
1511
}
1512
1513
1514
int
1515
NBOwnTLDef::maxCrossingIndex(const NBNode* node) const {
1516
int result = 0;
1517
for (auto crossing : node->getCrossings()) {
1518
result = MAX2(result, crossing->tlLinkIndex);
1519
result = MAX2(result, crossing->tlLinkIndex2);
1520
}
1521
return result;
1522
}
1523
1524
void
1525
NBOwnTLDef::fixSuperfluousYellow(NBTrafficLightLogic* logic) const {
1526
// assume that yellow states last at most one phase
1527
const int n = logic->getNumLinks();
1528
const int p = (int)logic->getPhases().size();
1529
for (int i1 = 0; i1 < n; ++i1) {
1530
LinkState prev = (LinkState)logic->getPhases().back().state[i1];
1531
for (int i2 = 0; i2 < p; ++i2) {
1532
LinkState cur = (LinkState)logic->getPhases()[i2].state[i1];
1533
LinkState next = (LinkState)logic->getPhases()[(i2 + 1) % p].state[i1];
1534
if (cur == LINKSTATE_TL_YELLOW_MINOR
1535
&& (prev == LINKSTATE_TL_GREEN_MAJOR || prev == LINKSTATE_TL_YELLOW_MINOR)
1536
&& next == LINKSTATE_TL_GREEN_MAJOR) {
1537
logic->setPhaseState(i2, i1, prev);
1538
}
1539
prev = cur;
1540
}
1541
}
1542
}
1543
1544
1545
void
1546
NBOwnTLDef::deactivateAlwaysGreen(NBTrafficLightLogic* logic) const {
1547
const int n = logic->getNumLinks();
1548
std::vector<bool> alwaysGreen(n, true);
1549
for (int i1 = 0; i1 < n; ++i1) {
1550
for (const auto& phase : logic->getPhases()) {
1551
if (phase.state[i1] != 'G') {
1552
alwaysGreen[i1] = false;
1553
break;
1554
}
1555
}
1556
}
1557
const int p = (int)logic->getPhases().size();
1558
for (int i1 = 0; i1 < n; ++i1) {
1559
if (alwaysGreen[i1]) {
1560
for (int i2 = 0; i2 < p; ++i2) {
1561
logic->setPhaseState(i2, i1, LINKSTATE_TL_OFF_NOSIGNAL);
1562
}
1563
}
1564
}
1565
}
1566
1567
1568
void
1569
NBOwnTLDef::deactivateInsideEdges(NBTrafficLightLogic* logic, const EdgeVector& fromEdges) const {
1570
const int n = (int)fromEdges.size();
1571
const int p = (int)logic->getPhases().size();
1572
for (int i1 = 0; i1 < n; ++i1) {
1573
if (fromEdges[i1]->isInsideTLS()) {
1574
for (int i2 = 0; i2 < p; ++i2) {
1575
logic->setPhaseState(i2, i1, LINKSTATE_TL_OFF_NOSIGNAL);
1576
}
1577
}
1578
}
1579
}
1580
1581
1582
SUMOTime
1583
NBOwnTLDef::computeEscapeTime(const std::string& state, const EdgeVector& fromEdges, const EdgeVector& toEdges) const {
1584
const int n = (int)fromEdges.size();
1585
double maxTime = 0;
1586
for (int i1 = 0; i1 < n; ++i1) {
1587
if (state[i1] == 'y' && !fromEdges[i1]->isInsideTLS()) {
1588
for (int i2 = 0; i2 < n; ++i2) {
1589
if (fromEdges[i2]->isInsideTLS()) {
1590
double gapSpeed = (toEdges[i1]->getSpeed() + fromEdges[i2]->getSpeed()) / 2;
1591
double time = fromEdges[i1]->getGeometry().back().distanceTo2D(fromEdges[i2]->getGeometry().back()) / gapSpeed;
1592
maxTime = MAX2(maxTime, time);
1593
}
1594
}
1595
}
1596
}
1597
// give some slack
1598
return TIME2STEPS(floor(maxTime * 1.2 + 5));
1599
}
1600
1601
1602
int
1603
NBOwnTLDef::getMaxIndex() {
1604
setParticipantsInformation();
1605
NBTrafficLightLogic* logic = compute(OptionsCont::getOptions());
1606
if (logic != nullptr) {
1607
return logic->getNumLinks() - 1;
1608
} else {
1609
return -1;
1610
}
1611
}
1612
1613
1614
bool
1615
NBOwnTLDef::corridorLike() const {
1616
if (getID() == DummyID) {
1617
// avoid infinite recursion
1618
return true;
1619
}
1620
// setParticipantsInformation resets myAmInTLS so we need to make a copy
1621
std::vector<bool> edgeInsideTLS;
1622
for (const NBEdge* e : myIncomingEdges) {
1623
edgeInsideTLS.push_back(e->isInsideTLS());
1624
}
1625
assert(myControlledNodes.size() >= 2);
1626
NBOwnTLDef dummy(DummyID, myControlledNodes, 0, TrafficLightType::STATIC);
1627
dummy.setParticipantsInformation();
1628
NBTrafficLightLogic* tllDummy = dummy.computeLogicAndConts(0, true);
1629
int greenPhases = 0;
1630
for (const auto& phase : tllDummy->getPhases()) {
1631
if (phase.state.find_first_of("gG") != std::string::npos) {
1632
greenPhases++;
1633
}
1634
}
1635
delete tllDummy;
1636
for (const auto& controlledNode : myControlledNodes) {
1637
controlledNode->removeTrafficLight(&dummy);
1638
}
1639
int i = 0;
1640
for (NBEdge* e : myIncomingEdges) {
1641
e->setInsideTLS(edgeInsideTLS[i]);
1642
i++;
1643
}
1644
return greenPhases <= 2;
1645
}
1646
1647
1648
NBTrafficLightLogic*
1649
NBOwnTLDef::buildNemaPhases(
1650
const EdgeVector& fromEdges,
1651
const EdgeVector& toEdges,
1652
const std::vector<NBNode::Crossing*>& crossings,
1653
const std::vector<std::pair<NBEdge*, NBEdge*> >& chosenList,
1654
const std::vector<std::string>& straightStates,
1655
const std::vector<std::string>& leftStates) {
1656
if (chosenList.size() != 2) {
1657
return nullptr;
1658
}
1659
const SUMOTime dur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.cycle.time"));
1660
const SUMOTime vehExt = TIME2STEPS(OptionsCont::getOptions().getInt("tls.nema.vehExt"));
1661
const SUMOTime yellow = TIME2STEPS(OptionsCont::getOptions().getInt("tls.nema.yellow"));
1662
const SUMOTime red = TIME2STEPS(OptionsCont::getOptions().getInt("tls.nema.red"));
1663
const SUMOTime minMinDur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.min-dur"));
1664
const SUMOTime maxDur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.max-dur"));
1665
const SUMOTime earliestEnd = UNSPECIFIED_DURATION;
1666
const SUMOTime latestEnd = UNSPECIFIED_DURATION;
1667
1668
const int totalNumLinks = (int)straightStates[0].size();
1669
NBTrafficLightLogic* logic = new NBTrafficLightLogic(getID(), getProgramID(), totalNumLinks, myOffset, myType);
1670
std::vector<int> ring1({1, 2, 3, 4});
1671
std::vector<int> ring2({5, 6, 7, 8});
1672
std::vector<int> barrier1({4, 8});
1673
std::vector<int> barrier2({2, 6});
1674
int phaseNameLeft = 1;
1675
for (int i = 0; i < (int)chosenList.size(); i++) {
1676
NBEdge* e1 = chosenList[i].first;
1677
assert(e1 != nullptr);
1678
NBEdge* e2 = chosenList[i].second;
1679
if (i < (int)leftStates.size()) {
1680
std::string left1 = filterState(leftStates[i], fromEdges, e1);
1681
if (left1 != "") {
1682
logic->addStep(dur, left1, minMinDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, toString(phaseNameLeft));
1683
}
1684
}
1685
if (e2 != nullptr) {
1686
std::string straight2 = filterState(straightStates[i], fromEdges, e2);
1687
straight2 = patchNEMAStateForCrossings(straight2, crossings, fromEdges, toEdges, e2, e1);
1688
1689
logic->addStep(dur, straight2, minMinDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, toString(phaseNameLeft + 1));
1690
if (i < (int)leftStates.size()) {
1691
std::string left2 = filterState(leftStates[i], fromEdges, e2);
1692
if (left2 != "") {
1693
logic->addStep(dur, left2, minMinDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, toString(phaseNameLeft + 4));
1694
}
1695
}
1696
1697
}
1698
std::string straight1 = filterState(straightStates[i], fromEdges, e1);
1699
if (straight1 == "") {
1700
delete logic;
1701
return nullptr;
1702
}
1703
straight1 = patchNEMAStateForCrossings(straight1, crossings, fromEdges, toEdges, e1, e2);
1704
logic->addStep(dur, straight1, minMinDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, toString(phaseNameLeft + 5));
1705
phaseNameLeft += 2;
1706
}
1707
std::map<int, int> names; // nema phase name -> sumo phase index
1708
for (int i = 0; i < (int)logic->getPhases().size(); i++) {
1709
names[StringUtils::toInt(logic->getPhases()[i].name)] = i;
1710
}
1711
1712
filterMissingNames(ring1, names, false);
1713
filterMissingNames(ring2, names, false);
1714
filterMissingNames(barrier1, names, true, 8);
1715
filterMissingNames(barrier2, names, true, 6);
1716
if (ring1[0] == 0 && ring1[1] == 0) {
1717
ring1[1] = 6;
1718
}
1719
if (ring1[2] == 0 && ring1[3] == 0) {
1720
ring1[3] = 8;
1721
}
1722
fixDurationSum(logic, names, ring1[0], ring1[1], ring2[0], ring2[1]);
1723
fixDurationSum(logic, names, ring1[2], ring1[3], ring2[2], ring2[3]);
1724
1725
logic->setParameter("ring1", joinToString(ring1, ","));
1726
logic->setParameter("ring2", joinToString(ring2, ","));
1727
logic->setParameter("barrierPhases", joinToString(barrier1, ","));
1728
logic->setParameter("barrier2Phases", joinToString(barrier2, ","));
1729
return logic;
1730
}
1731
1732
1733
std::string
1734
NBOwnTLDef::filterState(std::string state, const EdgeVector& fromEdges, const NBEdge* e) {
1735
bool haveGreen = false;
1736
for (int j = 0; j < (int)fromEdges.size(); j++) {
1737
if (fromEdges[j] != e) {
1738
state[j] = 'r';
1739
} else if (state[j] != 'r') {
1740
haveGreen = true;
1741
}
1742
}
1743
if (haveGreen) {
1744
return state;
1745
} else {
1746
return "";
1747
}
1748
}
1749
1750
void
1751
NBOwnTLDef::filterMissingNames(std::vector<int>& vec, const std::map<int, int>& names, bool isBarrier, int barrierDefault) {
1752
for (int i = 0; i < (int)vec.size(); i++) {
1753
if (names.count(vec[i]) == 0) {
1754
if (isBarrier) {
1755
if (names.count(vec[i] - 1) > 0) {
1756
vec[i] = vec[i] - 1;
1757
} else {
1758
vec[i] = barrierDefault;
1759
}
1760
} else {
1761
vec[i] = 0;
1762
}
1763
}
1764
}
1765
}
1766
1767
void
1768
NBOwnTLDef::fixDurationSum(NBTrafficLightLogic* logic, const std::map<int, int>& names, int ring1a, int ring1b, int ring2a, int ring2b) {
1769
std::set<int> ring1existing;
1770
std::set<int> ring2existing;
1771
if (names.count(ring1a) != 0) {
1772
ring1existing.insert(ring1a);
1773
}
1774
if (names.count(ring1b) != 0) {
1775
ring1existing.insert(ring1b);
1776
}
1777
if (names.count(ring2a) != 0) {
1778
ring2existing.insert(ring2a);
1779
}
1780
if (names.count(ring2b) != 0) {
1781
ring2existing.insert(ring2b);
1782
}
1783
if (ring1existing.size() > 0 && ring2existing.size() > 0 &&
1784
ring1existing.size() != ring2existing.size()) {
1785
int pI; // sumo phase index
1786
if (ring1existing.size() < ring2existing.size()) {
1787
pI = names.find(*ring1existing.begin())->second;
1788
} else {
1789
pI = names.find(*ring2existing.begin())->second;
1790
}
1791
const auto& p = logic->getPhases()[pI];
1792
SUMOTime newMaxDur = 2 * p.maxDur + p.yellow + p.red;
1793
logic->setPhaseMaxDuration(pI, newMaxDur);
1794
}
1795
}
1796
1797
/****************************************************************************/
1798
1799