Eclipse SUMO - Simulation of Urban MObility
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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-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/****************************************************************************/
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 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
1011std::string
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
1086void
1091
1092
1093void
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
1105void
1106NBOwnTLDef::remapRemoved(NBEdge* /*removed*/, const EdgeVector& /*incoming*/,
1107 const EdgeVector& /*outgoing*/) {}
1108
1109
1110void
1111NBOwnTLDef::replaceRemoved(NBEdge* /*removed*/, int /*removedLane*/,
1112 NBEdge* /*by*/, int /*byLane*/, bool /*incoming*/) {}
1113
1114
1115void
1118 if (myControlledNodes.size() > 0) {
1119 // we use a dummy node just to maintain const-correctness
1120 myNeedsContRelation.clear();
1121 for (NBNode* n : myControlledNodes) {
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 }
1140 }
1141}
1142
1143
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
1158std::string
1159NBOwnTLDef::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
1185std::string
1186NBOwnTLDef::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
1210std::string
1211NBOwnTLDef::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
1240std::string
1241NBOwnTLDef::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
1273std::string
1274NBOwnTLDef::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
1297std::string
1298NBOwnTLDef::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
1309bool
1310NBOwnTLDef::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
1324std::string
1325NBOwnTLDef::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')) {
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
1400std::string
1401NBOwnTLDef::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
1423void
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];
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
1446void
1447NBOwnTLDef::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;
1459 foundGreen[j] = true;
1460 }
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
1499void
1500NBOwnTLDef::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
1514int
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
1524void
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
1536 && next == LINKSTATE_TL_GREEN_MAJOR) {
1537 logic->setPhaseState(i2, i1, prev);
1538 }
1539 prev = cur;
1540 }
1541 }
1542}
1543
1544
1545void
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) {
1562 }
1563 }
1564 }
1565}
1566
1567
1568void
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) {
1576 }
1577 }
1578 }
1579}
1580
1581
1583NBOwnTLDef::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
1602int
1606 if (logic != nullptr) {
1607 return logic->getNumLinks() - 1;
1608 } else {
1609 return -1;
1610 }
1611}
1612
1613
1614bool
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);
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
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
1733std::string
1734NBOwnTLDef::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
1750void
1751NBOwnTLDef::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
1767void
1768NBOwnTLDef::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/****************************************************************************/
long long int SUMOTime
Definition GUI.h:36
@ DEFAULT
default cursor
#define DEBUGCOND(PED)
#define DEBUGCOND2(LANE)
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:288
#define TL(string)
Definition MsgHandler.h:305
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:76
T MAX2(T a, T b)
Definition StdDefs.h:82
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition ToString.h:283
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:4469
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition NBEdge.h:1041
NBNode * getToNode() const
Returns the destination node of the edge.
Definition NBEdge.h:546
const std::string & getID() const
Definition NBEdge.h:1531
bool setControllingTLInformation(const NBConnection &c, const std::string &tlID)
Returns if the link could be set as to be controlled.
Definition NBEdge.cpp:3817
int getNumLanes() const
Returns the number of lanes.
Definition NBEdge.h:520
std::vector< Connection > getConnectionsFromLane(int lane, const NBEdge *to=nullptr, int toLane=-1) const
Returns connections from a given lane.
Definition NBEdge.cpp:1287
int getJunctionPriority(const NBNode *const node) const
Returns the junction priority (normalised for the node currently build)
Definition NBEdge.cpp:2134
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition NBEdge.h:539
NBEdge * getTurnDestination(bool possibleDestination=false) const
Definition NBEdge.cpp:4098
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:2473
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:2158
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:2137
std::vector< Crossing * > getCrossings() const
return this junctions pedestrian crossings
Definition NBNode.cpp:3073
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