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