Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
NBOwnTLDef.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
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>
29#include "NBNode.h"
30#include "NBOwnTLDef.h"
31#include "NBTrafficLightLogic.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// ===========================================================================
57const double NBOwnTLDef::MIN_SPEED_CROSSING_TIME(25 / 3.6);
58
59
60// ===========================================================================
61// member method definitions
62// ===========================================================================
63NBOwnTLDef::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
72NBOwnTLDef::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
80NBOwnTLDef::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
89
90
91int
93 return e->getJunctionPriority(e->getToNode());
94}
95
96
97double
99 switch (dir) {
103 return HEIGH_WEIGHT;
106 return LOW_WEIGHT;
107 default:
108 break;
109 }
110 return 0;
111}
112
113double
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
184std::pair<NBEdge*, NBEdge*>
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
223std::pair<NBEdge*, NBEdge*>
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
259bool
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
271NBOwnTLDef::myCompute(int brakingTimeSeconds) {
272 if (myControlledNodes.size() > 1) {
273 // call this first so that the following call to computeLogicAndConts resets linkIndices
275 // reset insideTLS info
276 collectEdges();
277 }
278 return computeLogicAndConts(brakingTimeSeconds);
279}
280
281
283NBOwnTLDef::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"));
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
302 // @note this prevents updating after loading plain-xml into netedit computing tls and then changing the default 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) {
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
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());
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
861 // this computation only makes sense for single nodes
862 if (myControlledNodes.size() == 1) {
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
878bool
879NBOwnTLDef::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
897std::string
898NBOwnTLDef::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
950std::string
951NBOwnTLDef::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 assert(fromEdges.size() <= result.size());
993 for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
994 if (result[i1] == 'G') {
995 for (const NBNode::Crossing* cross : crossings) {
996 const int i2 = cross->tlLinkIndex;
997 const int i3 = cross->tlLinkIndex2;
998 if (fromEdges[i1] != 0 && toEdges[i1] != 0 && fromEdges[i1]->getToNode() == cross->node) {
999 if ((result[i2] == 'G' || (i3 >= 0 && result[i3] == 'G'))
1000 && cross->node->mustBrakeForCrossing(fromEdges[i1], toEdges[i1], *cross)) {
1001 result[i1] = 'g';
1002 break;
1003 }
1004 }
1005 }
1006 }
1007 }
1008 return result;
1009}
1010
1011
1012std::string
1014 const std::vector<NBNode::Crossing*>& crossings,
1015 const EdgeVector& fromEdges,
1016 const EdgeVector& toEdges,
1017 const NBEdge* greenEdge, NBEdge* otherChosen) {
1018 std::string result = state;
1019 const int pos = (int)(state.size() - crossings.size()); // number of controlled vehicle links
1020 const EdgeVector& all = greenEdge->getToNode()->getEdges();
1021 EdgeVector::const_iterator start = std::find(all.begin(), all.end(), greenEdge);
1022
1023 // permit crossings over edges between the current green edge and it's straight continuation
1024 const NBEdge* endEdge = nullptr;
1025 for (int i = 0; i < (int)state.size(); i++) {
1026 if (state[i] == 'G' && fromEdges[i] == greenEdge
1027 && greenEdge->getToNode()->getDirection(greenEdge, toEdges[i]) == LinkDirection::STRAIGHT) {
1028 // straight edge found
1029 endEdge = toEdges[i];
1030 break;
1031 }
1032 }
1033 if (endEdge == nullptr) {
1034 endEdge = otherChosen;
1035 }
1036 if (endEdge == nullptr) {
1037 // try to find the reverse edge of the green edge
1038 auto itCW = start;
1039 NBContHelper::nextCW(all, itCW);
1040 if ((*itCW)->getFromNode() == greenEdge->getToNode()) {
1041 endEdge = *itCW;
1042 }
1043 }
1044 if (endEdge == nullptr) {
1045 // at least prevent an infinite loop
1046 endEdge = greenEdge;
1047 }
1048 //std::cout << " patchNEMAStateForCrossings green=" << greenEdge->getID() << " other=" << Named::getIDSecure(otherChosen) << " end=" << Named::getIDSecure(end) << " all=" << toString(all) << "\n";
1049
1050 EdgeVector::const_iterator end = std::find(all.begin(), all.end(), endEdge);
1051 if (end == all.end()) {
1052 // at least prevent an infinite loop
1053 end = start;
1054 }
1055 auto it = start;
1056 NBContHelper::nextCCW(all, it);
1057 for (; it != end; NBContHelper::nextCCW(all, it)) {
1058 for (int ic = 0; ic < (int)crossings.size(); ++ic) {
1059 const int i1 = pos + ic;
1060 const NBNode::Crossing& cross = *crossings[ic];
1061 for (const NBEdge* crossed : cross.edges) {
1062 //std::cout << " cand=" << (*it)->getID() << " crossed=" << crossed->getID() << "\n";
1063 if (crossed == *it) {
1064 result[i1] = 'G';
1065 break;
1066 }
1067 }
1068 }
1069 }
1070 // correct behaviour for roads that are in conflict with a pedestrian crossing
1071 for (int i1 = 0; i1 < pos; ++i1) {
1072 if (result[i1] == 'G') {
1073 for (int ic = 0; ic < (int)crossings.size(); ++ic) {
1074 const NBNode::Crossing& crossing = *crossings[ic];
1075 const int i2 = pos + ic;
1076 if (result[i2] == 'G' && crossing.node->mustBrakeForCrossing(fromEdges[i1], toEdges[i1], crossing)) {
1077 result[i1] = 'g';
1078 break;
1079 }
1080 }
1081 }
1082 }
1083 return result;
1084}
1085
1086
1087void
1092
1093
1094void
1096 // set the information about the link's positions within the tl into the
1097 // edges the links are starting at, respectively
1098 for (NBConnectionVector::const_iterator j = myControlledLinks.begin(); j != myControlledLinks.end(); ++j) {
1099 const NBConnection& conn = *j;
1100 NBEdge* edge = conn.getFrom();
1101 edge->setControllingTLInformation(conn, getID());
1102 }
1103}
1104
1105
1106void
1107NBOwnTLDef::remapRemoved(NBEdge* /*removed*/, const EdgeVector& /*incoming*/,
1108 const EdgeVector& /*outgoing*/) {}
1109
1110
1111void
1112NBOwnTLDef::replaceRemoved(NBEdge* /*removed*/, int /*removedLane*/,
1113 NBEdge* /*by*/, int /*byLane*/, bool /*incoming*/) {}
1114
1115
1116void
1119 if (myControlledNodes.size() > 0) {
1120 // we use a dummy node just to maintain const-correctness
1121 myNeedsContRelation.clear();
1122 for (NBNode* n : myControlledNodes) {
1125 NBTrafficLightLogic* tllDummy = dummy.computeLogicAndConts(0, true);
1126 delete tllDummy;
1127 myNeedsContRelation.insert(dummy.myNeedsContRelation.begin(), dummy.myNeedsContRelation.end());
1128 n->removeTrafficLight(&dummy);
1129 }
1130#ifdef DEBUG_CONTRELATION
1131 if (DEBUGCOND) {
1132 std::cout << " contRelations at " << getID() << " prog=" << getProgramID() << ":\n";
1133 for (const StreamPair& s : myNeedsContRelation) {
1134 std::cout << " " << s.from1->getID() << "->" << s.to1->getID() << " foe " << s.from2->getID() << "->" << s.to2->getID() << "\n";
1135 }
1136 }
1137#endif
1138
1139 }
1141 }
1142}
1143
1144
1147 EdgeVector result = incoming;
1148 for (EdgeVector::iterator it = result.begin(); it != result.end();) {
1149 if ((*it)->getConnections().size() == 0 || (*it)->isInsideTLS()) {
1150 it = result.erase(it);
1151 } else {
1152 ++it;
1153 }
1154 }
1155 return result;
1156}
1157
1158
1159std::string
1160NBOwnTLDef::allowCompatible(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
1161 const std::vector<int>& fromLanes, const std::vector<int>& toLanes) {
1162 state = allowSingleEdge(state, fromEdges);
1163#ifdef DEBUG_PHASES
1164 if (DEBUGCOND) {
1165 std::cout << " state after allowSingle " << state << "\n";
1166 }
1167#endif
1168 if (myControlledNodes.size() > 1) {
1169 state = allowFollowers(state, fromEdges, toEdges);
1170#ifdef DEBUG_PHASES
1171 if (DEBUGCOND) {
1172 std::cout << " state after allowFollowers " << state << "\n";
1173 }
1174#endif
1175 state = allowPredecessors(state, fromEdges, toEdges, fromLanes, toLanes);
1176#ifdef DEBUG_PHASES
1177 if (DEBUGCOND) {
1178 std::cout << " state after allowPredecessors " << state << "\n";
1179 }
1180#endif
1181 }
1182 return state;
1183}
1184
1185
1186std::string
1187NBOwnTLDef::allowSingleEdge(std::string state, const EdgeVector& fromEdges) {
1188 // if only one edge has green, ensure sure that all connections from that edge are green
1189 const int size = (int)fromEdges.size();
1190 NBEdge* greenEdge = nullptr;
1191 for (int i1 = 0; i1 < size; ++i1) {
1192 if (state[i1] == 'G') {
1193 if (greenEdge == nullptr) {
1194 greenEdge = fromEdges[i1];
1195 } else if (greenEdge != fromEdges[i1]) {
1196 return state;
1197 }
1198 }
1199 }
1200 if (greenEdge != nullptr) {
1201 for (int i1 = 0; i1 < size; ++i1) {
1202 if (fromEdges[i1] == greenEdge) {
1203 state[i1] = 'G';
1204 }
1205 }
1206 }
1207 return state;
1208}
1209
1210
1211std::string
1212NBOwnTLDef::allowFollowers(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
1213 // check continuation within joined traffic lights
1214 bool check = true;
1215 while (check) {
1216 check = false;
1217 for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1218 if (state[i1] == 'G') {
1219 continue;
1220 }
1221 if (forbidden(state, i1, fromEdges, toEdges, true)) {
1222 continue;
1223 }
1224 bool followsChosen = false;
1225 for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1226 if (state[i2] == 'G' && fromEdges[i1] == toEdges[i2]) {
1227 followsChosen = true;
1228 break;
1229 }
1230 }
1231 if (followsChosen) {
1232 state[i1] = 'G';
1233 check = true;
1234 }
1235 }
1236 }
1237 return state;
1238}
1239
1240
1241std::string
1242NBOwnTLDef::allowPredecessors(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
1243 const std::vector<int>& fromLanes, const std::vector<int>& toLanes) {
1244 // also allow predecessors of chosen edges if the lanes match and there is no conflict
1245 // (must be done after the followers are done because followers are less specific)
1246 bool check = true;
1247 while (check) {
1248 check = false;
1249 for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1250 if (state[i1] == 'G') {
1251 continue;
1252 }
1253 if (forbidden(state, i1, fromEdges, toEdges, false)) {
1254 continue;
1255 }
1256 bool preceedsChosen = false;
1257 for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1258 if (state[i2] == 'G' && fromEdges[i2] == toEdges[i1]
1259 && fromLanes[i2] == toLanes[i1]) {
1260 preceedsChosen = true;
1261 break;
1262 }
1263 }
1264 if (preceedsChosen) {
1265 state[i1] = 'G';
1266 check = true;
1267 }
1268 }
1269 }
1270 return state;
1271}
1272
1273
1274std::string
1275NBOwnTLDef::allowUnrelated(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
1276 const std::vector<bool>& isTurnaround,
1277 const std::vector<NBNode::Crossing*>& crossings) {
1278 for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1279 if (state[i1] == 'G') {
1280 continue;
1281 }
1282 bool isForbidden = false;
1283 for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1284 if (state[i2] == 'G' && !isTurnaround[i2] &&
1285 (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true) || forbids(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2], true))) {
1286 isForbidden = true;
1287 break;
1288 }
1289 }
1290 if (!isForbidden && !hasCrossing(fromEdges[i1], toEdges[i1], crossings)) {
1291 state[i1] = 'G';
1292 }
1293 }
1294 return state;
1295}
1296
1297
1298std::string
1299NBOwnTLDef::allowByVClass(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges, SVCPermissions perm) {
1300 for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1301 SVCPermissions linkPerm = (fromEdges[i1]->getPermissions() & toEdges[i1]->getPermissions());
1302 if ((linkPerm & ~perm) == 0) {
1303 state[i1] = 'G';
1304 }
1305 }
1306 return state;
1307}
1308
1309
1310bool
1311NBOwnTLDef::forbidden(const std::string& state, int index, const EdgeVector& fromEdges, const EdgeVector& toEdges, bool allowCont) {
1312 for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1313 if (state[i2] == 'G' && foes(fromEdges[i2], toEdges[i2], fromEdges[index], toEdges[index])) {
1314 if (!allowCont || (
1315 !needsCont(fromEdges[i2], toEdges[i2], fromEdges[index], toEdges[index]) &&
1316 !needsCont(fromEdges[index], toEdges[index], fromEdges[i2], toEdges[i2]))) {
1317 return true;
1318 }
1319 }
1320 }
1321 return false;
1322}
1323
1324
1325std::string
1326NBOwnTLDef::correctConflicting(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
1327 const std::vector<bool>& isTurnaround,
1328 const std::vector<int>& fromLanes,
1329 const std::vector<int>& toLanes,
1330 const std::vector<bool>& hadGreenMajor,
1331 bool& haveForbiddenLeftMover,
1332 std::vector<bool>& rightTurnConflicts,
1333 std::vector<bool>& mergeConflicts) {
1334 const bool controlledWithin = !OptionsCont::getOptions().getBool("tls.uncontrolled-within");
1335 for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1336 if (state[i1] == 'G') {
1337 for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1338 if ((state[i2] == 'G' || state[i2] == 'g')) {
1340 fromEdges[i1], toEdges[i1], fromLanes[i1], fromEdges[i2], toEdges[i2], fromLanes[i2])) {
1341 rightTurnConflicts[i1] = true;
1342 }
1343 if (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true, controlledWithin) || rightTurnConflicts[i1]) {
1344 state[i1] = 'g';
1345 if (myControlledNodes.size() == 1) {
1346 myNeedsContRelation.insert(StreamPair(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2]));
1347#ifdef DEBUG_CONTRELATION
1348 if (DEBUGCOND) {
1349 std::cout << getID() << " p=" << getProgramID() << " contRel: " << fromEdges[i1]->getID() << "->" << toEdges[i1]->getID()
1350 << " foe " << fromEdges[i2]->getID() << "->" << toEdges[i2]->getID() << "\n";
1351 }
1352#endif
1353 }
1354 if (!isTurnaround[i1] && !hadGreenMajor[i1] && !rightTurnConflicts[i1]) {
1355 haveForbiddenLeftMover = true;
1356 }
1357 } else if (fromEdges[i1] == fromEdges[i2]
1358 && fromLanes[i1] != fromLanes[i2]
1359 && toEdges[i1] == toEdges[i2]
1360 && toLanes[i1] == toLanes[i2]
1361 && fromEdges[i1]->getToNode()->mergeConflictYields(fromEdges[i1], fromLanes[i1], fromLanes[i2], toEdges[i1], toLanes[i1])) {
1362 mergeConflicts[i1] = true;
1363 state[i1] = 'g';
1364 }
1365 }
1366 }
1367 }
1368 if (state[i1] == 'r') {
1369 if (fromEdges[i1]->getToNode()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED &&
1370 fromEdges[i1]->getToNode()->getDirection(fromEdges[i1], toEdges[i1]) == LinkDirection::RIGHT) {
1371 state[i1] = 's';
1372 // do not allow right-on-red when in conflict with exclusive left-turn phase
1373 for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1374 if (state[i2] == 'G' && !isTurnaround[i2] &&
1375 (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true) ||
1376 forbids(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2], true))) {
1377 const LinkDirection foeDir = fromEdges[i2]->getToNode()->getDirection(fromEdges[i2], toEdges[i2]);
1378 if (foeDir == LinkDirection::LEFT || foeDir == LinkDirection::PARTLEFT) {
1379 state[i1] = 'r';
1380 break;
1381 }
1382 }
1383 }
1384 if (state[i1] == 's') {
1385 // handle right-on-red conflicts
1386 for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1387 if (state[i2] == 'G' && !isTurnaround[i2] &&
1388 (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true) ||
1389 forbids(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2], true))) {
1390 myExtraConflicts.insert(std::make_pair(i1, i2));
1391 }
1392 }
1393 }
1394 }
1395 }
1396 }
1397 return state;
1398}
1399
1400
1401std::string
1402NBOwnTLDef::correctMixed(std::string state, const EdgeVector& fromEdges,
1403 const std::vector<int>& fromLanes,
1404 bool& buildMixedGreenPhase, std::vector<bool>& mixedGreen) {
1405 for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1406 if ((state[i1] == 'G' || state[i1] == 'g')) {
1407 for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1408 if (i1 != i2 && fromEdges[i1] == fromEdges[i2] && fromLanes[i1] == fromLanes[i2]
1409 && state[i2] != 'G' && state[i2] != 'g') {
1410 state[i1] = state[i2];
1411 //std::cout << " mixedGreen i1=" << i1 << " i2=" << i2 << "\n";
1412 mixedGreen[i1] = true;
1413 if (fromEdges[i1]->getNumLanesThatAllow(SVC_PASSENGER) > 1) {
1414 buildMixedGreenPhase = true;
1415 }
1416 }
1417 }
1418 }
1419 }
1420 return state;
1421}
1422
1423
1424void
1426 std::vector<bool> foundGreen(fromEdges.size(), false);
1427 for (const auto& phase : logic->getPhases()) {
1428 const std::string state = phase.state;
1429 for (int j = 0; j < (int)fromEdges.size(); j++) {
1430 LinkState ls = (LinkState)state[j];
1432 foundGreen[j] = true;
1433 }
1434 }
1435 }
1436 for (int j = 0; j < (int)foundGreen.size(); j++) {
1437 if (!foundGreen[j]) {
1438 NBEdge* e = fromEdges[j];
1439 if (std::find(toProc.begin(), toProc.end(), e) == toProc.end()) {
1440 toProc.push_back(e);
1441 }
1442 }
1443 }
1444}
1445
1446
1447void
1448NBOwnTLDef::addPedestrianScramble(NBTrafficLightLogic* logic, int totalNumLinks, SUMOTime /* greenTime */, SUMOTime brakingTime,
1449 const std::vector<NBNode::Crossing*>& crossings, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
1450 // check both indices for each crossing (they may have green in different phases)
1451 std::vector<bool> foundGreen(crossings.size() * 2, false);
1452 const std::vector<NBTrafficLightLogic::PhaseDefinition>& phases = logic->getPhases();
1453 for (int i = 0; i < (int)phases.size(); i++) {
1454 const std::string state = phases[i].state;
1455 int j = 0;
1456 for (auto cross : crossings) {
1457 LinkState ls = (LinkState)state[cross->tlLinkIndex];
1458 LinkState ls2 = cross->tlLinkIndex2 >= 0 ? (LinkState)state[cross->tlLinkIndex2] : ls;
1460 foundGreen[j] = true;
1461 }
1463 foundGreen[j + crossings.size()] = true;
1464 }
1465 j++;
1466 }
1467 }
1468#ifdef DEBUG_PHASES
1469 if (DEBUGCOND2(logic)) {
1470 std::cout << " foundCrossingGreen=" << toString(foundGreen) << "\n";
1471 }
1472#endif
1473 for (int j = 0; j < (int)foundGreen.size(); j++) {
1474 if (!foundGreen[j]) {
1475 // add a phase where all pedestrians may walk, (preceded by a yellow phase and followed by a clearing phase)
1476 if (phases.size() > 0) {
1477 bool needYellowPhase = false;
1478 std::string state = phases.back().state;
1479 for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1480 if (state[i1] == 'G' || state[i1] == 'g') {
1481 state[i1] = 'y';
1482 needYellowPhase = true;
1483 }
1484 }
1485 // add yellow step
1486 if (needYellowPhase && brakingTime > 0) {
1487 logic->addStep(brakingTime, state);
1488 }
1489 }
1490 const SUMOTime pedClearingTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.crossing-clearance.time"));
1491 const SUMOTime scrambleTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.scramble.time"));
1492 addPedestrianPhases(logic, scrambleTime + pedClearingTime, UNSPECIFIED_DURATION,
1493 UNSPECIFIED_DURATION, UNSPECIFIED_DURATION, UNSPECIFIED_DURATION, std::string(totalNumLinks, 'r'), crossings, fromEdges, toEdges);
1494 break;
1495 }
1496 }
1497}
1498
1499
1500void
1501NBOwnTLDef::buildAllRedState(SUMOTime allRedTime, NBTrafficLightLogic* logic, const std::string& state) {
1502 if (allRedTime > 0) {
1503 // build all-red phase
1504 std::string allRedState = state;
1505 for (int i = 0; i < (int)state.size(); i++) {
1506 if (allRedState[i] == 'Y' || allRedState[i] == 'y') {
1507 allRedState[i] = 'r';
1508 }
1509 }
1510 logic->addStep(TIME2STEPS(ceil(STEPS2TIME(allRedTime))), allRedState);
1511 }
1512}
1513
1514
1515int
1517 int result = 0;
1518 for (auto crossing : node->getCrossings()) {
1519 result = MAX2(result, crossing->tlLinkIndex);
1520 result = MAX2(result, crossing->tlLinkIndex2);
1521 }
1522 return result;
1523}
1524
1525void
1527 // assume that yellow states last at most one phase
1528 const int n = logic->getNumLinks();
1529 const int p = (int)logic->getPhases().size();
1530 for (int i1 = 0; i1 < n; ++i1) {
1531 LinkState prev = (LinkState)logic->getPhases().back().state[i1];
1532 for (int i2 = 0; i2 < p; ++i2) {
1533 LinkState cur = (LinkState)logic->getPhases()[i2].state[i1];
1534 LinkState next = (LinkState)logic->getPhases()[(i2 + 1) % p].state[i1];
1535 if (cur == LINKSTATE_TL_YELLOW_MINOR
1537 && next == LINKSTATE_TL_GREEN_MAJOR) {
1538 logic->setPhaseState(i2, i1, prev);
1539 }
1540 prev = cur;
1541 }
1542 }
1543}
1544
1545
1546void
1548 const int n = logic->getNumLinks();
1549 std::vector<bool> alwaysGreen(n, true);
1550 for (int i1 = 0; i1 < n; ++i1) {
1551 for (const auto& phase : logic->getPhases()) {
1552 if (phase.state[i1] != 'G') {
1553 alwaysGreen[i1] = false;
1554 break;
1555 }
1556 }
1557 }
1558 const int p = (int)logic->getPhases().size();
1559 for (int i1 = 0; i1 < n; ++i1) {
1560 if (alwaysGreen[i1]) {
1561 for (int i2 = 0; i2 < p; ++i2) {
1563 }
1564 }
1565 }
1566}
1567
1568
1569void
1571 const int n = (int)fromEdges.size();
1572 const int p = (int)logic->getPhases().size();
1573 for (int i1 = 0; i1 < n; ++i1) {
1574 if (fromEdges[i1]->isInsideTLS()) {
1575 for (int i2 = 0; i2 < p; ++i2) {
1577 }
1578 }
1579 }
1580}
1581
1582
1584NBOwnTLDef::computeEscapeTime(const std::string& state, const EdgeVector& fromEdges, const EdgeVector& toEdges) const {
1585 const int n = (int)fromEdges.size();
1586 double maxTime = 0;
1587 for (int i1 = 0; i1 < n; ++i1) {
1588 if (state[i1] == 'y' && !fromEdges[i1]->isInsideTLS()) {
1589 for (int i2 = 0; i2 < n; ++i2) {
1590 if (fromEdges[i2]->isInsideTLS()) {
1591 double gapSpeed = (toEdges[i1]->getSpeed() + fromEdges[i2]->getSpeed()) / 2;
1592 double time = fromEdges[i1]->getGeometry().back().distanceTo2D(fromEdges[i2]->getGeometry().back()) / gapSpeed;
1593 maxTime = MAX2(maxTime, time);
1594 }
1595 }
1596 }
1597 }
1598 // give some slack
1599 return TIME2STEPS(floor(maxTime * 1.2 + 5));
1600}
1601
1602
1603int
1607 if (logic != nullptr) {
1608 return logic->getNumLinks() - 1;
1609 } else {
1610 return -1;
1611 }
1612}
1613
1614
1615bool
1617 if (getID() == DummyID) {
1618 // avoid infinite recursion
1619 return true;
1620 }
1621 // setParticipantsInformation resets myAmInTLS so we need to make a copy
1622 std::vector<bool> edgeInsideTLS;
1623 for (const NBEdge* e : myIncomingEdges) {
1624 edgeInsideTLS.push_back(e->isInsideTLS());
1625 }
1626 assert(myControlledNodes.size() >= 2);
1629 NBTrafficLightLogic* tllDummy = dummy.computeLogicAndConts(0, true);
1630 int greenPhases = 0;
1631 for (const auto& phase : tllDummy->getPhases()) {
1632 if (phase.state.find_first_of("gG") != std::string::npos) {
1633 greenPhases++;
1634 }
1635 }
1636 delete tllDummy;
1637 for (const auto& controlledNode : myControlledNodes) {
1638 controlledNode->removeTrafficLight(&dummy);
1639 }
1640 int i = 0;
1641 for (NBEdge* e : myIncomingEdges) {
1642 e->setInsideTLS(edgeInsideTLS[i]);
1643 i++;
1644 }
1645 return greenPhases <= 2;
1646}
1647
1648
1651 const EdgeVector& fromEdges,
1652 const EdgeVector& toEdges,
1653 const std::vector<NBNode::Crossing*>& crossings,
1654 const std::vector<std::pair<NBEdge*, NBEdge*> >& chosenList,
1655 const std::vector<std::string>& straightStates,
1656 const std::vector<std::string>& leftStates) {
1657 if (chosenList.size() != 2) {
1658 return nullptr;
1659 }
1660 const SUMOTime dur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.cycle.time"));
1661 const SUMOTime vehExt = TIME2STEPS(OptionsCont::getOptions().getInt("tls.nema.vehExt"));
1662 const SUMOTime yellow = TIME2STEPS(OptionsCont::getOptions().getInt("tls.nema.yellow"));
1663 const SUMOTime red = TIME2STEPS(OptionsCont::getOptions().getInt("tls.nema.red"));
1664 const SUMOTime minMinDur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.min-dur"));
1665 const SUMOTime maxDur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.max-dur"));
1666 const SUMOTime earliestEnd = UNSPECIFIED_DURATION;
1667 const SUMOTime latestEnd = UNSPECIFIED_DURATION;
1668
1669 const int totalNumLinks = (int)straightStates[0].size();
1670 NBTrafficLightLogic* logic = new NBTrafficLightLogic(getID(), getProgramID(), totalNumLinks, myOffset, myType);
1671 std::vector<int> ring1({1, 2, 3, 4});
1672 std::vector<int> ring2({5, 6, 7, 8});
1673 std::vector<int> barrier1({4, 8});
1674 std::vector<int> barrier2({2, 6});
1675 int phaseNameLeft = 1;
1676 for (int i = 0; i < (int)chosenList.size(); i++) {
1677 NBEdge* e1 = chosenList[i].first;
1678 assert(e1 != nullptr);
1679 NBEdge* e2 = chosenList[i].second;
1680 if (i < (int)leftStates.size()) {
1681 std::string left1 = filterState(leftStates[i], fromEdges, e1);
1682 if (left1 != "") {
1683 logic->addStep(dur, left1, minMinDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, toString(phaseNameLeft));
1684 }
1685 }
1686 if (e2 != nullptr) {
1687 std::string straight2 = filterState(straightStates[i], fromEdges, e2);
1688 straight2 = patchNEMAStateForCrossings(straight2, crossings, fromEdges, toEdges, e2, e1);
1689
1690 logic->addStep(dur, straight2, minMinDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, toString(phaseNameLeft + 1));
1691 if (i < (int)leftStates.size()) {
1692 std::string left2 = filterState(leftStates[i], fromEdges, e2);
1693 if (left2 != "") {
1694 logic->addStep(dur, left2, minMinDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, toString(phaseNameLeft + 4));
1695 }
1696 }
1697
1698 }
1699 std::string straight1 = filterState(straightStates[i], fromEdges, e1);
1700 if (straight1 == "") {
1701 delete logic;
1702 return nullptr;
1703 }
1704 straight1 = patchNEMAStateForCrossings(straight1, crossings, fromEdges, toEdges, e1, e2);
1705 logic->addStep(dur, straight1, minMinDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, toString(phaseNameLeft + 5));
1706 phaseNameLeft += 2;
1707 }
1708 std::map<int, int> names; // nema phase name -> sumo phase index
1709 for (int i = 0; i < (int)logic->getPhases().size(); i++) {
1710 names[StringUtils::toInt(logic->getPhases()[i].name)] = i;
1711 }
1712
1713 filterMissingNames(ring1, names, false);
1714 filterMissingNames(ring2, names, false);
1715 filterMissingNames(barrier1, names, true, 8);
1716 filterMissingNames(barrier2, names, true, 6);
1717 if (ring1[0] == 0 && ring1[1] == 0) {
1718 ring1[1] = 6;
1719 }
1720 if (ring1[2] == 0 && ring1[3] == 0) {
1721 ring1[3] = 8;
1722 }
1723 fixDurationSum(logic, names, ring1[0], ring1[1], ring2[0], ring2[1]);
1724 fixDurationSum(logic, names, ring1[2], ring1[3], ring2[2], ring2[3]);
1725
1726 logic->setParameter("ring1", joinToString(ring1, ","));
1727 logic->setParameter("ring2", joinToString(ring2, ","));
1728 logic->setParameter("barrierPhases", joinToString(barrier1, ","));
1729 logic->setParameter("barrier2Phases", joinToString(barrier2, ","));
1730 return logic;
1731}
1732
1733
1734std::string
1735NBOwnTLDef::filterState(std::string state, const EdgeVector& fromEdges, const NBEdge* e) {
1736 bool haveGreen = false;
1737 for (int j = 0; j < (int)fromEdges.size(); j++) {
1738 if (fromEdges[j] != e) {
1739 state[j] = 'r';
1740 } else if (state[j] != 'r') {
1741 haveGreen = true;
1742 }
1743 }
1744 if (haveGreen) {
1745 return state;
1746 } else {
1747 return "";
1748 }
1749}
1750
1751void
1752NBOwnTLDef::filterMissingNames(std::vector<int>& vec, const std::map<int, int>& names, bool isBarrier, int barrierDefault) {
1753 for (int i = 0; i < (int)vec.size(); i++) {
1754 if (names.count(vec[i]) == 0) {
1755 if (isBarrier) {
1756 if (names.count(vec[i] - 1) > 0) {
1757 vec[i] = vec[i] - 1;
1758 } else {
1759 vec[i] = barrierDefault;
1760 }
1761 } else {
1762 vec[i] = 0;
1763 }
1764 }
1765 }
1766}
1767
1768void
1769NBOwnTLDef::fixDurationSum(NBTrafficLightLogic* logic, const std::map<int, int>& names, int ring1a, int ring1b, int ring2a, int ring2b) {
1770 std::set<int> ring1existing;
1771 std::set<int> ring2existing;
1772 if (names.count(ring1a) != 0) {
1773 ring1existing.insert(ring1a);
1774 }
1775 if (names.count(ring1b) != 0) {
1776 ring1existing.insert(ring1b);
1777 }
1778 if (names.count(ring2a) != 0) {
1779 ring2existing.insert(ring2a);
1780 }
1781 if (names.count(ring2b) != 0) {
1782 ring2existing.insert(ring2b);
1783 }
1784 if (ring1existing.size() > 0 && ring2existing.size() > 0 &&
1785 ring1existing.size() != ring2existing.size()) {
1786 int pI; // sumo phase index
1787 if (ring1existing.size() < ring2existing.size()) {
1788 pI = names.find(*ring1existing.begin())->second;
1789 } else {
1790 pI = names.find(*ring2existing.begin())->second;
1791 }
1792 const auto& p = logic->getPhases()[pI];
1793 SUMOTime newMaxDur = 2 * p.maxDur + p.yellow + p.red;
1794 logic->setPhaseMaxDuration(pI, newMaxDur);
1795 }
1796}
1797
1798/****************************************************************************/
long long int SUMOTime
Definition GUI.h:36
#define DEBUGCOND(PED)
#define DEBUGCOND2(LANE)
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:287
#define TL(string)
Definition MsgHandler.h:304
std::vector< NBEdge * > EdgeVector
container for (sorted) edges
Definition NBCont.h:42
#define MIN_GREEN_TIME
#define HEIGH_WEIGHT
#define DEBUGEDGE(edge)
#define LOW_WEIGHT
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition SUMOTime.cpp:91
#define STEPS2TIME(x)
Definition SUMOTime.h:55
#define SUMOTime_MAX
Definition SUMOTime.h:34
#define TIME2STEPS(x)
Definition SUMOTime.h:57
bool isRailway(SVCPermissions permissions)
Returns whether an edge with the given permissions is a (exclusive) railway edge.
bool isForbidden(SVCPermissions permissions)
Returns whether an edge with the given permissions is a forbidden edge.
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
@ SVC_PASSENGER
vehicle is a passenger car (a "normal" car)
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_DELIVERY
vehicle is a small delivery vehicle
@ SVC_TRAM
vehicle is a light rail
@ SVC_PEDESTRIAN
pedestrian
TrafficLightLayout
LinkDirection
The different directions a link between two lanes may take (or a stream between two edges)....
@ PARTLEFT
The link is a partial left direction.
@ RIGHT
The link is a (hard) right direction.
@ TURN
The link is a 180 degree turn.
@ LEFT
The link is a (hard) left direction.
@ STRAIGHT
The link is a straight direction.
@ PARTRIGHT
The link is a partial right direction.
LinkState
The right-of-way state of a link between two lanes used when constructing a NBTrafficLightLogic,...
@ LINKSTATE_TL_GREEN_MAJOR
The link has green light, may pass.
@ LINKSTATE_TL_YELLOW_MINOR
The link has yellow light, has to brake anyway.
@ LINKSTATE_TL_RED
The link has red light (must brake)
@ LINKSTATE_TL_GREEN_MINOR
The link has green light, has to brake.
@ LINKSTATE_TL_OFF_NOSIGNAL
The link is controlled by a tls which is off, not blinking, may pass.
T MIN2(T a, T b)
Definition StdDefs.h:80
T MAX2(T a, T b)
Definition StdDefs.h:86
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition ToString.h:289
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
static double getMinAngleDiff(double angle1, double angle2)
Returns the minimum distance (clockwise/counter-clockwise) between both angles.
NBEdge * getFrom() const
returns the from-edge (start of the connection)
static void nextCCW(const EdgeVector &edges, EdgeVector::const_iterator &from)
static void nextCW(const EdgeVector &edges, EdgeVector::const_iterator &from)
The representation of a single edge during network building.
Definition NBEdge.h:92
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition NBEdge.cpp:4540
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition NBEdge.h:1047
NBNode * getToNode() const
Returns the destination node of the edge.
Definition NBEdge.h:552
const std::string & getID() const
Definition NBEdge.h:1551
bool setControllingTLInformation(const NBConnection &c, const std::string &tlID)
Returns if the link could be set as to be controlled.
Definition NBEdge.cpp:3869
int getNumLanes() const
Returns the number of lanes.
Definition NBEdge.h:526
std::vector< Connection > getConnectionsFromLane(int lane, const NBEdge *to=nullptr, int toLane=-1) const
Returns connections from a given lane.
Definition NBEdge.cpp:1310
int getJunctionPriority(const NBNode *const node) const
Returns the junction priority (normalised for the node currently build)
Definition NBEdge.cpp:2157
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition NBEdge.h:545
NBEdge * getTurnDestination(bool possibleDestination=false) const
Definition NBEdge.cpp:4169
static double relAngle(double angle1, double angle2)
computes the relative angle between the two angles
Definition NBHelpers.cpp:45
A definition of a pedestrian crossing.
Definition NBNode.h:135
const NBNode * node
The parent node of this crossing.
Definition NBNode.h:140
EdgeVector edges
The edges being crossed.
Definition NBNode.h:142
Represents a single node (junction) during network building.
Definition NBNode.h:66
LinkDirection getDirection(const NBEdge *const incoming, const NBEdge *const outgoing, bool leftHand=false) const
Returns the representation of the described stream's direction.
Definition NBNode.cpp:2498
static bool rightTurnConflict(const NBEdge *from, const NBEdge *to, int fromLane, const NBEdge *prohibitorFrom, const NBEdge *prohibitorTo, int prohibitorFromLane)
return whether the given laneToLane connection is a right turn which must yield to a bicycle crossing...
Definition NBNode.cpp:2183
bool mustBrakeForCrossing(const NBEdge *const from, const NBEdge *const to, const Crossing &crossing) const
Returns the information whether the described flow must brake for the given crossing.
Definition NBNode.cpp:2162
std::vector< Crossing * > getCrossings() const
return this junctions pedestrian crossings
Definition NBNode.cpp:3098
const EdgeVector & getEdges() const
Returns all edges which participate in this node (Edges that start or end at this node)
Definition NBNode.h:278
Sorts edges by their priority within the node they end at.
Definition NBOwnTLDef.h:321
A traffic light logics which must be computed (only nodes/edges are given)
Definition NBOwnTLDef.h:44
void fixSuperfluousYellow(NBTrafficLightLogic *logic) const
avoid yellow signal between successive green (major) phases
std::string correctConflicting(std::string state, const EdgeVector &fromEdges, const EdgeVector &toEdges, const std::vector< bool > &isTurnaround, const std::vector< int > &fromLanes, const std::vector< int > &toLanes, const std::vector< bool > &hadGreenMajor, bool &haveForbiddenLeftMover, std::vector< bool > &rightTurnConflicts, std::vector< bool > &mergeConflicts)
change 'G' to 'g' for conflicting connections
NBTrafficLightLogic * buildNemaPhases(const EdgeVector &fromEdges, const EdgeVector &toEdges, const std::vector< NBNode::Crossing * > &crossings, const std::vector< std::pair< NBEdge *, NBEdge * > > &chosenList, const std::vector< std::string > &straightStates, const std::vector< std::string > &leftStates)
static std::string patchStateForCrossings(const std::string &state, const std::vector< NBNode::Crossing * > &crossings, const EdgeVector &fromEdges, const EdgeVector &toEdges)
compute phase state in regard to pedestrian crossings
std::string allowByVClass(std::string state, const EdgeVector &fromEdges, const EdgeVector &toEdges, SVCPermissions perm)
int getMaxIndex()
Returns the maximum index controlled by this traffic light.
bool myHaveSinglePhase
Whether left-mover should not have an additional phase.
Definition NBOwnTLDef.h:338
bool corridorLike() const
test whether a joined tls with layout 'opposites' would be built without dedicated left-turn phase
SUMOTime computeEscapeTime(const std::string &state, const EdgeVector &fromEdges, const EdgeVector &toEdges) const
compute time to clear all vehicles from within an alternateOneWay layout
void replaceRemoved(NBEdge *removed, int removedLane, NBEdge *by, int byLane, bool incoming)
Replaces a removed edge/lane.
std::pair< NBEdge *, NBEdge * > getBestPair(EdgeVector &incoming)
Returns the combination of two edges from the given which has most unblocked streams.
~NBOwnTLDef()
Destructor.
void collectLinks()
Collects the links participating in this traffic light.
void deactivateAlwaysGreen(NBTrafficLightLogic *logic) const
switch of signal for links that are always green
static void addGreenWithin(NBTrafficLightLogic *logic, const EdgeVector &fromEdges, EdgeVector &toProc)
ensure inner edges all get the green light eventually
NBTrafficLightLogic * computeLogicAndConts(int brakingTimeSeconds, bool onlyConts=false)
helper function for myCompute
static bool hasCrossing(const NBEdge *from, const NBEdge *to, const std::vector< NBNode::Crossing * > &crossings)
compute whether the given connection is crossed by pedestrians
std::string allowPredecessors(std::string state, const EdgeVector &fromEdges, const EdgeVector &toEdges, const std::vector< int > &fromLanes, const std::vector< int > &toLanes)
void deactivateInsideEdges(NBTrafficLightLogic *logic, const EdgeVector &fromEdges) const
switch of signal for links that are inside a joined tls
double computeUnblockedWeightedStreamNumber(const NBEdge *const e1, const NBEdge *const e2)
Returns how many streams outgoing from the edges can pass the junction without being blocked.
void remapRemoved(NBEdge *removed, const EdgeVector &incoming, const EdgeVector &outgoing)
Replaces occurrences of the removed edge in incoming/outgoing edges of all definitions.
int getToPrio(const NBEdge *const e)
Returns this edge's priority at the node it ends at.
std::string allowCompatible(std::string state, const EdgeVector &fromEdges, const EdgeVector &toEdges, const std::vector< int > &fromLanes, const std::vector< int > &toLanes)
allow connections that are compatible with the chosen edges
std::string correctMixed(std::string state, const EdgeVector &fromEdges, const std::vector< int > &fromLanes, bool &buildMixedGreenPhase, std::vector< bool > &mixedGreen)
prevent green and red from the same lane
void fixDurationSum(NBTrafficLightLogic *logic, const std::map< int, int > &names, int ring1a, int ring1b, int ring2a, int ring2b)
ensure that phase max durations before each barrier have the same sum in both rings
NBTrafficLightLogic * myCompute(int brakingTimeSeconds)
Computes the traffic light logic finally in dependence to the type.
static std::string patchNEMAStateForCrossings(const std::string &state, const std::vector< NBNode::Crossing * > &crossings, const EdgeVector &fromEdges, const EdgeVector &toEdges, const NBEdge *greenEdge, NBEdge *otherChosen)
std::string filterState(std::string state, const EdgeVector &fromEdges, const NBEdge *e)
mask out all greens that do not originate at the given edge
std::string allowFollowers(std::string state, const EdgeVector &fromEdges, const EdgeVector &toEdges)
int maxCrossingIndex(const NBNode *node) const
find maximum crossing index
void buildAllRedState(SUMOTime allRedTime, NBTrafficLightLogic *logic, const std::string &state)
double getDirectionalWeight(LinkDirection dir)
Returns the weight of a stream given its direction.
std::string allowSingleEdge(std::string state, const EdgeVector &fromEdges)
bool hasStraightConnection(const NBEdge *fromEdge)
check whether there is a straight connection from this edge
std::pair< NBEdge *, NBEdge * > getBestCombination(const EdgeVector &edges)
Returns the combination of two edges from the given which has most unblocked streams.
void initNeedsContRelation() const
static void addPedestrianScramble(NBTrafficLightLogic *logic, int totalNumLinks, SUMOTime greenTime, SUMOTime yellowTime, const std::vector< NBNode::Crossing * > &crossings, const EdgeVector &fromEdges, const EdgeVector &toEdges)
add an additional pedestrian phase if there are crossings that did not get green yet
NBOwnTLDef(const std::string &id, const std::vector< NBNode * > &junctions, SUMOTime offset, TrafficLightType type)
Constructor.
static EdgeVector getConnectedOuterEdges(const EdgeVector &incoming)
get edges that have connections
TrafficLightLayout myLayout
the layout for generated signal plans
Definition NBOwnTLDef.h:341
bool forbidden(const std::string &state, int index, const EdgeVector &fromEdges, const EdgeVector &toEdges, bool allowCont)
whether the given index is forbidden by a green link in the current state
static std::string addPedestrianPhases(NBTrafficLightLogic *logic, const SUMOTime greenTime, const SUMOTime minDur, const SUMOTime maxDur, const SUMOTime earliestEnd, const SUMOTime latestEnd, std::string state, const std::vector< NBNode::Crossing * > &crossings, const EdgeVector &fromEdges, const EdgeVector &toEdges)
add 1 or 2 phases depending on the presence of pedestrian crossings
std::string allowUnrelated(std::string state, const EdgeVector &fromEdges, const EdgeVector &toEdges, const std::vector< bool > &isTurnaround, const std::vector< NBNode::Crossing * > &crossings)
void filterMissingNames(std::vector< int > &vec, const std::map< int, int > &names, bool isBarrier, int barrierDefault=0)
keep only valid NEMA phase names (for params)
void setTLControllingInformation() const
Informs edges about being controlled by a tls.
static const double MIN_SPEED_CROSSING_TIME
minimum speed for computing time to cross intersection
Definition NBOwnTLDef.h:152
The base class for traffic light logic definitions.
const std::string & getProgramID() const
Returns the ProgramID.
const EdgeVector & getIncomingEdges() const
Returns the list of incoming edges (must be build first)
bool needsCont(const NBEdge *fromE, const NBEdge *toE, const NBEdge *otherFromE, const NBEdge *otherToE) const
std::vector< NBNode * > myControlledNodes
The container with participating nodes.
TrafficLightType getType() const
get the algorithm type (static etc..)
EdgeVector myIncomingEdges
The list of incoming edges.
NBTrafficLightLogic * compute(const OptionsCont &oc)
Computes the traffic light logic.
TrafficLightType myType
The algorithm type for the traffic light.
EdgeVector myEdgesWithin
The list of edges within the area controlled by the tls.
static const std::string DummyID
id for temporary definitions
bool forbids(const NBEdge *const possProhibitorFrom, const NBEdge *const possProhibitorTo, const NBEdge *const possProhibitedFrom, const NBEdge *const possProhibitedTo, bool regardNonSignalisedLowerPriority, bool sameNodeOnly=false) const
Returns the information whether "prohibited" flow must let "prohibitor" flow pass.
NBConnectionVector myControlledLinks
The list of controlled links.
virtual void setType(TrafficLightType type)
set the algorithm type (static etc..)
virtual void setParticipantsInformation()
Builds the list of participating nodes/edges/links.
void collectAllLinks(NBConnectionVector &into)
helper method for use in NBOwnTLDef and NBLoadedSUMOTLDef
SUMOTime myOffset
The offset in the program.
static const SUMOTime UNSPECIFIED_DURATION
bool foes(const NBEdge *const from1, const NBEdge *const to1, const NBEdge *const from2, const NBEdge *const to2) const
Returns the information whether the given flows cross.
virtual void collectEdges()
Build the list of participating edges.
A SUMO-compliant built logic for a traffic light.
SUMOTime getDuration() const
Returns the duration of the complete cycle.
void closeBuilding(bool checkVarDurations=true)
closes the building process
void setPhaseDuration(int phaseIndex, SUMOTime duration)
Modifies the duration for an existing phase (used by netedit)
void setPhaseState(int phaseIndex, int tlIndex, LinkState linkState)
Modifies the state for an existing phase (used by netedit)
void setPhaseMaxDuration(int phaseIndex, SUMOTime duration)
Modifies the max duration for an existing phase (used by netedit)
int getNumLinks()
Returns the number of participating links.
void setType(TrafficLightType type)
set the algorithm type (static etc..)
void setPhaseNext(int phaseIndex, const std::vector< int > &next)
Modifies the next phase (used by netedit)
TrafficLightType getType() const
get the algorithm type (static etc..)
const std::vector< PhaseDefinition > & getPhases() const
Returns the phases.
void addStep(const SUMOTime duration, const std::string &state, const std::vector< int > &next=std::vector< int >(), const std::string &name="", const int index=-1)
Adds a phase to the logic (static)
static std::string getIDSecure(const T *obj, const std::string &fallBack="NULL")
get an identifier for Named-like object which may be Null
Definition Named.h:67
const std::string & getID() const
Returns the id.
Definition Named.h:74
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
int getInt(const std::string &name) const
Returns the int-value of the named option (only for Option_Integer)
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
static OptionsCont & getOptions()
Retrieves the options.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
static StringBijection< TrafficLightLayout > TrafficLightLayouts
traffic light layouts
T get(const std::string &str) const
get key
static int toInt(const std::string &sData)
converts a string into the integer value described by it by calling the char-type converter,...
#define UNUSED_PARAMETER(x)
NLOHMANN_BASIC_JSON_TPL_DECLARATION void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL &j1, nlohmann::NLOHMANN_BASIC_JSON_TPL &j2) noexcept(//NOLINT(readability-inconsistent-declaration-parameter-name) is_nothrow_move_constructible< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value &&//NOLINT(misc-redundant-expression) is_nothrow_move_assignable< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value)
exchanges the values of two JSON objects
Definition json.hpp:21884
A structure which describes a connection between edges or lanes.
Definition NBEdge.h:201
data structure for caching needsCont information