Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
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 884 : NBOwnTLDef::NBOwnTLDef(const std::string& id,
64 : const std::vector<NBNode*>& junctions, SUMOTime offset,
65 884 : TrafficLightType type) :
66 : NBTrafficLightDefinition(id, junctions, DefaultProgramID, offset, type),
67 884 : myHaveSinglePhase(false),
68 884 : myLayout(TrafficLightLayout::DEFAULT) {
69 884 : }
70 :
71 :
72 5834 : NBOwnTLDef::NBOwnTLDef(const std::string& id, NBNode* junction, SUMOTime offset,
73 5834 : TrafficLightType type) :
74 : NBTrafficLightDefinition(id, junction, DefaultProgramID, offset, type),
75 5834 : myHaveSinglePhase(false),
76 5834 : myLayout(TrafficLightLayout::DEFAULT) {
77 5834 : }
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 10271 : NBOwnTLDef::~NBOwnTLDef() {}
89 :
90 :
91 : int
92 11111 : NBOwnTLDef::getToPrio(const NBEdge* const e) {
93 11111 : return e->getJunctionPriority(e->getToNode());
94 : }
95 :
96 :
97 : double
98 124320 : NBOwnTLDef::getDirectionalWeight(LinkDirection dir) {
99 124320 : switch (dir) {
100 : case LinkDirection::STRAIGHT:
101 : case LinkDirection::PARTLEFT:
102 : case LinkDirection::PARTRIGHT:
103 : return HEIGH_WEIGHT;
104 55724 : case LinkDirection::LEFT:
105 : case LinkDirection::RIGHT:
106 55724 : return LOW_WEIGHT;
107 : default:
108 : break;
109 : }
110 0 : return 0;
111 : }
112 :
113 : double
114 8072 : NBOwnTLDef::computeUnblockedWeightedStreamNumber(const NBEdge* const e1, const NBEdge* const e2) {
115 : double val = 0;
116 23235 : for (int e1l = 0; e1l < e1->getNumLanes(); e1l++) {
117 15163 : std::vector<NBEdge::Connection> approached1 = e1->getConnectionsFromLane(e1l);
118 46178 : for (int e2l = 0; e2l < e2->getNumLanes(); e2l++) {
119 31015 : std::vector<NBEdge::Connection> approached2 = e2->getConnectionsFromLane(e2l);
120 79852 : for (std::vector<NBEdge::Connection>::iterator e1c = approached1.begin(); e1c != approached1.end(); ++e1c) {
121 48837 : if (e1->getTurnDestination() == (*e1c).toEdge) {
122 5346 : continue;
123 : }
124 121764 : for (std::vector<NBEdge::Connection>::iterator e2c = approached2.begin(); e2c != approached2.end(); ++e2c) {
125 78273 : if (e2->getTurnDestination() == (*e2c).toEdge) {
126 10415 : continue;
127 : }
128 67858 : const double sign = (forbids(e1, (*e1c).toEdge, e2, (*e2c).toEdge, true)
129 67858 : || forbids(e2, (*e2c).toEdge, e1, (*e1c).toEdge, true)) ? -1 : 1;
130 : double w1;
131 : double w2;
132 67858 : const int prio1 = e1->getJunctionPriority(e1->getToNode());
133 67858 : const int prio2 = e2->getJunctionPriority(e2->getToNode());
134 67858 : if (prio1 == prio2) {
135 62160 : w1 = getDirectionalWeight(e1->getToNode()->getDirection(e1, (*e1c).toEdge));
136 62160 : w2 = getDirectionalWeight(e2->getToNode()->getDirection(e2, (*e2c).toEdge));
137 : } else {
138 5698 : if (prio1 > prio2) {
139 : w1 = HEIGH_WEIGHT;
140 : w2 = LOW_WEIGHT;
141 : } else {
142 : w1 = LOW_WEIGHT;
143 : w2 = HEIGH_WEIGHT;
144 : }
145 5698 : if (sign == -1) {
146 : // extra penalty if edges with different junction priority are in conflict
147 3431 : w1 *= 2;
148 3431 : w2 *= 2;
149 : }
150 : }
151 67858 : if (isRailway(e1->getPermissions()) != isRailway(e2->getPermissions())) {
152 2429 : w1 *= 0.1;
153 2429 : w2 *= 0.1;
154 : }
155 67858 : if ((e1->getPermissions() & SVC_PASSENGER) == 0) {
156 7511 : w1 *= 0.1;
157 : }
158 67858 : if ((e2->getPermissions() & SVC_PASSENGER) == 0) {
159 6584 : w2 *= 0.1;
160 : }
161 67858 : val += sign * w1;
162 67858 : 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 31015 : }
174 15163 : }
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 8072 : return val;
181 : }
182 :
183 :
184 : std::pair<NBEdge*, NBEdge*>
185 4142 : 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 13566 : for (EdgeVector::const_iterator i = edges.begin(); i != edges.end(); ++i) {
189 17496 : for (EdgeVector::const_iterator j = i + 1; j != edges.end(); ++j) {
190 8072 : const double value = computeUnblockedWeightedStreamNumber(*i, *j);
191 8072 : if (value > bestValue) {
192 : bestValue = value;
193 : bestPair = std::pair<NBEdge*, NBEdge*>(*i, *j);
194 3142 : } else if (value == bestValue) {
195 909 : const double ca = GeomHelper::getMinAngleDiff((*i)->getAngleAtNode((*i)->getToNode()), (*j)->getAngleAtNode((*j)->getToNode()));
196 909 : const double oa = GeomHelper::getMinAngleDiff(bestPair.first->getAngleAtNode(bestPair.first->getToNode()), bestPair.second->getAngleAtNode(bestPair.second->getToNode()));
197 909 : if (fabs(oa - ca) < NUMERICAL_EPS) { // break ties by id
198 12 : if (bestPair.first->getID() < (*i)->getID()) {
199 : bestPair = std::pair<NBEdge*, NBEdge*>(*i, *j);
200 : }
201 897 : } else if (oa < ca) {
202 : bestPair = std::pair<NBEdge*, NBEdge*>(*i, *j);
203 : }
204 : }
205 : }
206 : }
207 4142 : if (bestValue < 0 || (bestValue == 0 && !hasStraightConnection(bestPair.first) && !hasStraightConnection(bestPair.second))) {
208 : // do not group edges
209 422 : 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 4142 : return bestPair;
220 : }
221 :
222 :
223 : std::pair<NBEdge*, NBEdge*>
224 5808 : NBOwnTLDef::getBestPair(EdgeVector& incoming) {
225 5808 : 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 1666 : return ret;
230 : }
231 : // determine the best combination
232 : // by priority, first
233 : EdgeVector used;
234 4142 : std::sort(incoming.begin(), incoming.end(), edge_by_incoming_priority_sorter());
235 4142 : used.push_back(*incoming.begin()); // the first will definitely be used
236 : // get the ones with the same priority
237 4142 : int prio = getToPrio(*used.begin());
238 8723 : for (EdgeVector::iterator i = incoming.begin() + 1; i != incoming.end() && prio == getToPrio(*i); ++i) {
239 4581 : used.push_back(*i);
240 : }
241 : // if there only lower priorised, use these, too
242 4142 : if (used.size() < 2) {
243 407 : used = incoming;
244 : }
245 4142 : 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 4142 : incoming.erase(find(incoming.begin(), incoming.end(), ret.first));
253 4142 : if (ret.second != nullptr) {
254 3720 : incoming.erase(find(incoming.begin(), incoming.end(), ret.second));
255 : }
256 4142 : return ret;
257 4142 : }
258 :
259 : bool
260 12552 : NBOwnTLDef::hasStraightConnection(const NBEdge* fromEdge) {
261 22419 : for (const NBEdge::Connection& c : fromEdge->getConnections()) {
262 20125 : LinkDirection dir = fromEdge->getToNode()->getDirection(fromEdge, c.toEdge);
263 20125 : if (dir == LinkDirection::STRAIGHT) {
264 : return true;
265 : }
266 : }
267 : return false;
268 : }
269 :
270 : NBTrafficLightLogic*
271 2195 : NBOwnTLDef::myCompute(int brakingTimeSeconds) {
272 2195 : if (myControlledNodes.size() > 1) {
273 : // call this first so that the following call to computeLogicAndConts resets linkIndices
274 128 : initNeedsContRelation();
275 : // reset insideTLS info
276 128 : collectEdges();
277 : }
278 2195 : return computeLogicAndConts(brakingTimeSeconds);
279 : }
280 :
281 :
282 : NBTrafficLightLogic*
283 3663 : NBOwnTLDef::computeLogicAndConts(int brakingTimeSeconds, bool onlyConts) {
284 3663 : if (myControlledNodes.size() == 1) {
285 : // otherwise, use values from previous call to initNeedsContRelation
286 : myNeedsContRelation.clear();
287 : }
288 : myExtraConflicts.clear();
289 3663 : const bool isNEMA = myType == TrafficLightType::NEMA;
290 3663 : const SUMOTime brakingTime = TIME2STEPS(brakingTimeSeconds);
291 3663 : const SUMOTime leftTurnTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.left-green.time"));
292 3860 : const SUMOTime minMinDur = (myType == TrafficLightType::STATIC) ? UNSPECIFIED_DURATION : TIME2STEPS(OptionsCont::getOptions().getInt("tls.min-dur"));
293 3860 : const SUMOTime maxDur = (myType == TrafficLightType::STATIC) ? UNSPECIFIED_DURATION : TIME2STEPS(OptionsCont::getOptions().getInt("tls.max-dur"));
294 3663 : const SUMOTime earliestEnd = UNSPECIFIED_DURATION;
295 : const SUMOTime latestEnd = UNSPECIFIED_DURATION;
296 3663 : const SUMOTime greenTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.green.time"));
297 3663 : SUMOTime allRedTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.allred.time"));
298 3663 : const double minorLeftSpeedThreshold = OptionsCont::getOptions().getFloat("tls.minor-left.max-speed");
299 3663 : const bool noMixed = OptionsCont::getOptions().getBool("tls.no-mixed");
300 : // left-turn phases do not work well for joined tls, so we build incoming instead
301 3663 : if (myLayout == TrafficLightLayout::DEFAULT) {
302 : // @note this prevents updating after loading plain-xml into netedit computing tls and then changing the default layout
303 6070 : myLayout = SUMOXMLDefinitions::TrafficLightLayouts.get(OptionsCont::getOptions().getString("tls.layout"));
304 : }
305 : // corridorLike() resets crossing indices so should be called first
306 3663 : 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 3663 : 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 16085 : for (NBEdge* const fromEdge : incoming) {
323 : const int numLanes = fromEdge->getNumLanes();
324 12422 : const bool edgeHasStraight = hasStraightConnection(fromEdge);
325 37610 : 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 64037 : for (const NBEdge::Connection& approached : fromEdge->getConnectionsFromLane(i2)) {
332 38849 : if (!fromEdge->mayBeTLSControlled(i2, approached.toEdge, approached.toLane)) {
333 57 : continue;
334 : }
335 38792 : fromEdges.push_back(fromEdge);
336 38792 : fromLanes.push_back(i2);
337 38792 : toLanes.push_back(approached.toLane);
338 38792 : toEdges.push_back(approached.toEdge);
339 11255 : if (approached.vmax < NUMERICAL_EPS || (fromEdge->getPermissions() & SVC_PASSENGER) == 0
340 49401 : || (approached.toEdge->getPermissions() & SVC_PASSENGER) == 0) {
341 28533 : crossingTime.push_back(0);
342 : } else {
343 12930 : 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 38792 : if (approached.toEdge != nullptr) {
347 38792 : isTurnaround.push_back(fromEdge->isTurningDirectionAt(approached.toEdge));
348 : } else {
349 0 : isTurnaround.push_back(true);
350 : }
351 38792 : 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 38792 : totalNumLinks++;
364 25188 : }
365 64037 : for (const NBEdge::Connection& approached : fromEdge->getConnectionsFromLane(i2)) {
366 38849 : if (!fromEdge->mayBeTLSControlled(i2, approached.toEdge, approached.toLane)) {
367 57 : continue;
368 : }
369 38792 : hasTurnLane.push_back(
370 : (hasLeft && !hasPartLeft && !hasStraight && !hasRight)
371 17187 : || (hasPartLeft && !hasLeft && !hasStraight && !hasRight)
372 34694 : || (hasPartLeft && hasLeft && edgeHasStraight && !hasRight)
373 73037 : || (!hasLeft && !hasPartLeft && !hasTurnaround && hasRight));
374 25188 : }
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 7906 : for (NBNode* const node : myControlledNodes) {
381 4243 : const std::vector<NBNode::Crossing*>& c = node->getCrossings();
382 4243 : node->setCrossingTLIndices(getID(), totalNumLinks, onlyConts);
383 4243 : totalNumLinks = MAX2(totalNumLinks, maxCrossingIndex(node) + 1);
384 : copy(c.begin(), c.end(), std::back_inserter(crossings));
385 4243 : }
386 :
387 3663 : NBTrafficLightLogic* logic = new NBTrafficLightLogic(getID(), getProgramID(), totalNumLinks, myOffset, myType);
388 3663 : EdgeVector toProc = getConnectedOuterEdges(incoming);
389 :
390 : // build all phases
391 : std::vector<int> greenPhases; // indices of green phases
392 3663 : std::vector<bool> hadGreenMajor(totalNumLinks, false);
393 10649 : while (toProc.size() > 0) {
394 : bool groupTram = false;
395 : bool groupOther = false;
396 6986 : std::pair<NBEdge*, NBEdge*> chosen;
397 : std::set<const NBEdge*> chosenSet;
398 6986 : if (groupOpposites) {
399 6777 : 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 1240 : 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 1240 : if (angle < 135) {
405 : chosen = std::pair<NBEdge*, NBEdge*>(toProc[0], static_cast<NBEdge*>(nullptr));
406 : toProc.erase(toProc.begin());
407 : } else {
408 271 : chosen = getBestPair(toProc);
409 : }
410 : } else {
411 5537 : chosen = getBestPair(toProc);
412 5537 : if (chosen.second == nullptr && chosen.first->getPermissions() == SVC_TRAM) {
413 : groupTram = true;
414 101 : for (auto it = toProc.begin(); it != toProc.end();) {
415 42 : if ((*it)->getPermissions() == SVC_TRAM) {
416 : it = toProc.erase(it);
417 : } else {
418 : it++;
419 : }
420 : }
421 : }
422 : }
423 : } else {
424 209 : NBEdge* chosenEdge = toProc[0];
425 : chosen = std::pair<NBEdge*, NBEdge*>(chosenEdge, static_cast<NBEdge*>(nullptr));
426 : toProc.erase(toProc.begin());
427 209 : SVCPermissions perms = chosenEdge->getPermissions();
428 209 : if (perms == SVC_TRAM) {
429 : groupTram = true;
430 205 : } else if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_DELIVERY)) == 0) {
431 36 : 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 209 : if (groupTram || groupOther) {
439 44 : for (auto it = toProc.begin(); it != toProc.end();) {
440 36 : 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 6986 : chosenList.push_back(chosen);
456 : chosenSet.insert(chosen.first);
457 6986 : if (chosen.second != nullptr) {
458 : chosenSet.insert(chosen.second);
459 : }
460 : // find parallel bike edge for the chosen (passenger) edges
461 17759 : for (const NBEdge* e : chosenSet) {
462 10773 : if ((e->getPermissions() & SVC_PASSENGER) != 0) {
463 : std::vector<NBEdge*> parallelBikeEdges;
464 19684 : for (NBEdge* cand : toProc) {
465 9653 : if ((cand->getPermissions() & ~SVC_PEDESTRIAN) == SVC_BICYCLE) {
466 317 : double angle = fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), cand->getAngleAtNode(cand->getToNode())));
467 317 : if (angle < 30) {
468 : // roughly parallel
469 109 : parallelBikeEdges.push_back(cand);
470 : }
471 : }
472 : }
473 10140 : 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 109 : toProc.erase(std::find(toProc.begin(), toProc.end(), be));
481 : }
482 10031 : }
483 : }
484 : // plain straight movers
485 : double maxSpeed = 0;
486 : bool haveGreen = false;
487 36283 : for (const NBEdge* const fromEdge : incoming) {
488 : const bool inChosen = chosenSet.count(fromEdge) != 0;
489 : const int numLanes = fromEdge->getNumLanes();
490 86895 : for (int i2 = 0; i2 < numLanes; i2++) {
491 148134 : for (const NBEdge::Connection& approached : fromEdge->getConnectionsFromLane(i2)) {
492 90536 : if (!fromEdge->mayBeTLSControlled(i2, approached.toEdge, approached.toLane)) {
493 137 : continue;
494 : }
495 90399 : if (inChosen) {
496 37357 : state[pos] = 'G';
497 : haveGreen = true;
498 37357 : maxSpeed = MAX2(maxSpeed, fromEdge->getSpeed());
499 : } else {
500 53042 : state[pos] = 'r';
501 : }
502 90399 : ++pos;
503 57598 : }
504 : }
505 : }
506 6986 : 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 6979 : if (!isNEMA) {
516 : // correct behaviour for those that are not in chosen, but may drive, though
517 6928 : 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 6928 : if (groupTram) {
524 126 : state = allowByVClass(state, fromEdges, toEdges, SVC_TRAM);
525 6865 : } else if (groupOther) {
526 8 : 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 6928 : if (groupOpposites || chosen.first->getToNode()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED) {
534 13450 : 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 6979 : bool haveForbiddenLeftMover = false;
544 6979 : std::vector<bool> rightTurnConflicts(pos, false);
545 6979 : std::vector<bool> mergeConflicts(pos, false);
546 6979 : state = correctConflicting(state, fromEdges, toEdges, isTurnaround, fromLanes, toLanes, hadGreenMajor, haveForbiddenLeftMover, rightTurnConflicts, mergeConflicts);
547 97341 : for (int i1 = 0; i1 < pos; ++i1) {
548 90362 : 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 6979 : std::vector<bool> leftGreen(pos, false);
559 : // check whether at least one left-turn lane exist
560 : bool foundLeftTurnLane = false;
561 97341 : for (int i1 = 0; i1 < pos; ++i1) {
562 90362 : if (state[i1] == 'g' && !rightTurnConflicts[i1] && !mergeConflicts[i1] && hasTurnLane[i1]) {
563 : foundLeftTurnLane = true;
564 : }
565 : }
566 2923 : const bool buildLeftGreenPhase = (haveForbiddenLeftMover && !myHaveSinglePhase && leftTurnTime > 0 && foundLeftTurnLane
567 8116 : && groupOpposites && !groupTram && !groupOther);
568 :
569 : // find indices for exclusive left green phase and apply option minor-left.max-speed
570 97341 : for (int i1 = 0; i1 < pos; ++i1) {
571 90362 : if (state[i1] == 'g' && !rightTurnConflicts[i1] && !mergeConflicts[i1]
572 : // only activate turn-around together with a real left-turn
573 100897 : && (!isTurnaround[i1] || (i1 > 0 && leftGreen[i1 - 1]))) {
574 : leftGreen[i1] = true;
575 9384 : if (fromEdges[i1]->getSpeed() > minorLeftSpeedThreshold) {
576 235 : if (buildLeftGreenPhase) {
577 50 : state[i1] = 'r';
578 : //std::cout << " disabling minorLeft " << i1 << " (speed=" << fromEdges[i1]->getSpeed() << " thresh=" << minorLeftSpeedThreshold << ")\n";
579 185 : } else if (!isTurnaround[i1]) {
580 412 : 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 6979 : straightStates.push_back(state);
598 :
599 : const std::string vehicleState = state; // backup state before pedestrian modifications
600 6979 : greenPhases.push_back((int)logic->getPhases().size());
601 :
602 : // 5s at 50km/h, 10s at 80km/h, rounded to full seconds
603 6979 : const double minDurBySpeed = maxSpeed * 3.6 / 6 - 3.3;
604 7013 : SUMOTime minDur = MAX2(minMinDur, TIME2STEPS(floor(minDurBySpeed + 0.5)));
605 6979 : 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 1081 : for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
610 1007 : if (state[i1] == 'G') {
611 297 : SVCPermissions linkPerm = (fromEdges[i1]->getPermissions() & toEdges[i1]->getPermissions());
612 297 : if (linkPerm != SVC_TRAM) {
613 : tramExclusive = false;
614 : break;
615 : }
616 : }
617 : }
618 175 : if (tramExclusive) {
619 : // one tram per actuated phase
620 : minDur = TIME2STEPS(1);
621 : }
622 : }
623 :
624 6979 : state = addPedestrianPhases(logic, greenTime, minDur, maxDur, earliestEnd, latestEnd, state, crossings, fromEdges, toEdges);
625 : // pedestrians have 'r' from here on
626 10028 : for (int i1 = pos; i1 < (int)state.size(); ++i1) {
627 3049 : state[i1] = 'r';
628 : }
629 6979 : if (brakingTime > 0) {
630 : SUMOTime maxCross = 0;
631 : // build yellow (straight)
632 65420 : for (int i1 = 0; i1 < pos; ++i1) {
633 61308 : if (state[i1] != 'G' && state[i1] != 'g') {
634 33633 : continue;
635 : }
636 8742 : if ((vehicleState[i1] >= 'a' && vehicleState[i1] <= 'z')
637 8742 : && buildLeftGreenPhase
638 3388 : && !rightTurnConflicts[i1]
639 3283 : && !mergeConflicts[i1]
640 30932 : && leftGreen[i1]) {
641 3130 : continue;
642 : }
643 24545 : state[i1] = 'y';
644 24545 : maxCross = MAX2(maxCross, crossingTime[i1]);
645 : }
646 : // add step
647 4112 : logic->addStep(brakingTime, state);
648 : // add optional all-red state
649 4112 : if (!buildLeftGreenPhase) {
650 3302 : if (myLayout == TrafficLightLayout::ALTERNATE_ONEWAY) {
651 24 : allRedTime = computeEscapeTime(state, fromEdges, toEdges);
652 : }
653 3302 : buildAllRedState(allRedTime + MAX2(0ll, maxCross - brakingTime - allRedTime), logic, state);
654 : }
655 : }
656 :
657 :
658 6979 : if (buildLeftGreenPhase) {
659 : // build left green
660 21150 : for (int i1 = 0; i1 < pos; ++i1) {
661 20023 : if (state[i1] == 'Y' || state[i1] == 'y') {
662 4457 : state[i1] = 'r';
663 4457 : continue;
664 : }
665 15566 : if (leftGreen[i1]) {
666 4192 : state[i1] = 'G';
667 : }
668 : }
669 1127 : leftStates.push_back(state);
670 2254 : state = allowCompatible(state, fromEdges, toEdges, fromLanes, toLanes);
671 1127 : state = correctConflicting(state, fromEdges, toEdges, isTurnaround, fromLanes, toLanes, hadGreenMajor, haveForbiddenLeftMover, rightTurnConflicts, mergeConflicts);
672 1127 : bool buildMixedGreenPhase = false;
673 1127 : std::vector<bool> mixedGreen(pos, false);
674 : const std::string oldState = state;
675 1127 : if (noMixed) {
676 20 : state = correctMixed(state, fromEdges, fromLanes, buildMixedGreenPhase, mixedGreen);
677 : }
678 1127 : if (state != oldState) {
679 120 : for (int i1 = 0; i1 < pos; ++i1) {
680 112 : if (mixedGreen[i1]) {
681 : // patch previous yellow and allred phase
682 8 : int yellowIndex = (int)logic->getPhases().size() - 1;
683 8 : if (allRedTime > 0) {
684 0 : logic->setPhaseState(yellowIndex--, i1, LINKSTATE_TL_RED);
685 : }
686 8 : if (brakingTime > 0) {
687 8 : logic->setPhaseState(yellowIndex, i1, LINKSTATE_TL_YELLOW_MINOR);
688 : }
689 : }
690 : }
691 16 : state = allowCompatible(state, fromEdges, toEdges, fromLanes, toLanes);
692 : }
693 :
694 : // add step
695 2254 : logic->addStep(leftTurnTime, state, minDur, maxDur, earliestEnd, latestEnd);
696 :
697 : // build left yellow
698 1127 : if (brakingTime > 0) {
699 : SUMOTime maxCross = 0;
700 16167 : for (int i1 = 0; i1 < pos; ++i1) {
701 15357 : if (state[i1] != 'G' && state[i1] != 'g') {
702 11842 : continue;
703 : }
704 3515 : state[i1] = 'y';
705 3515 : maxCross = MAX2(maxCross, crossingTime[i1]);
706 : }
707 : // add step
708 810 : logic->addStep(brakingTime, state);
709 : // add optional all-red state
710 810 : buildAllRedState(allRedTime + MAX2(0ll, maxCross - brakingTime - allRedTime), logic, state);
711 : }
712 :
713 1127 : 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 68 : for (int i1 = 0; i1 < pos; ++i1) {
720 64 : if (state[i1] == 'Y' || state[i1] == 'y') {
721 16 : state[i1] = 'r';
722 16 : continue;
723 : }
724 48 : if (mixedGreen[i1]) {
725 4 : state[i1] = 'G';
726 : }
727 : }
728 8 : state = allowCompatible(state, fromEdges, toEdges, fromLanes, toLanes);
729 8 : state = correctConflicting(state, fromEdges, toEdges, isTurnaround, fromLanes, toLanes, hadGreenMajor, haveForbiddenLeftMover, rightTurnConflicts, mergeConflicts);
730 :
731 : // add step
732 8 : logic->addStep(leftTurnTime, state, minDur, maxDur, earliestEnd, latestEnd);
733 :
734 : // build mixed yellow
735 4 : if (brakingTime > 0) {
736 : SUMOTime maxCross = 0;
737 68 : for (int i1 = 0; i1 < pos; ++i1) {
738 64 : if (state[i1] != 'G' && state[i1] != 'g') {
739 48 : continue;
740 : }
741 16 : state[i1] = 'y';
742 16 : maxCross = MAX2(maxCross, crossingTime[i1]);
743 : }
744 : // add step
745 4 : logic->addStep(brakingTime, state);
746 : // add optional all-red state
747 4 : buildAllRedState(allRedTime + MAX2(0ll, maxCross - brakingTime - allRedTime), logic, state);
748 : }
749 : }
750 :
751 5852 : } else if (isNEMA) {
752 : std::string& s = straightStates.back();
753 : std::string leftState = s;
754 211 : for (int ii = 0; ii < pos; ++ii) {
755 192 : if (s[ii] != 'r') {
756 88 : NBEdge* fromEdge = fromEdges[ii];
757 88 : NBEdge* toEdge = toEdges[ii];
758 88 : LinkDirection dir = fromEdge->getToNode()->getDirection(fromEdge, toEdge);
759 88 : if (hasTurnLane[ii] && (dir == LinkDirection::LEFT || dir == LinkDirection::TURN)) {
760 13 : s[ii] = 'r';
761 13 : leftState[ii] = 'G';
762 : } else {
763 75 : leftState[ii] = 'r';
764 : }
765 : }
766 : }
767 19 : leftStates.push_back(leftState);
768 : }
769 : // fix edges within joined traffic lights that did not get the green light yet
770 6979 : if (myEdgesWithin.size() > 0 && !isNEMA && toProc.size() == 0 && !onlyConts) {
771 116 : addGreenWithin(logic, fromEdges, toProc);
772 : }
773 : }
774 : // fix pedestrian crossings that did not get the green light yet
775 3663 : if (crossings.size() > 0) {
776 324 : addPedestrianScramble(logic, totalNumLinks, TIME2STEPS(10), brakingTime, crossings, fromEdges, toEdges);
777 : }
778 : // add optional red phase if there were no foes
779 1346 : if (logic->getPhases().size() == 2 && brakingTime > 0
780 5665 : && OptionsCont::getOptions().getInt("tls.red.time") > 0) {
781 656 : const SUMOTime redTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.red.time"));
782 1968 : logic->addStep(redTime, std::string(totalNumLinks, 'r'));
783 : }
784 :
785 3663 : if (myLayout == TrafficLightLayout::ALTERNATE_ONEWAY) {
786 : // exiting the oneway section should always be possible
787 11 : deactivateInsideEdges(logic, fromEdges);
788 : }
789 3663 : if (isNEMA) {
790 26 : NBTrafficLightLogic* nemaLogic = buildNemaPhases(fromEdges, toEdges, crossings, chosenList, straightStates, leftStates);
791 26 : if (nemaLogic == nullptr) {
792 6 : WRITE_WARNINGF(TL("Generating NEMA phases is not supported for traffic light '%' with % incoming edges. Using tlType 'actuated' as fallback"), getID(), incoming.size());
793 : logic->setType(TrafficLightType::ACTUATED);
794 2 : setType(TrafficLightType::ACTUATED);
795 : } else {
796 24 : delete logic;
797 : logic = nemaLogic;
798 : }
799 : }
800 :
801 3663 : SUMOTime totalDuration = logic->getDuration();
802 :
803 3691 : if ((OptionsCont::getOptions().isDefault("tls.green.time") || !OptionsCont::getOptions().isDefault("tls.cycle.time")) && !isNEMA) {
804 3635 : 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 10555 : for (std::vector<int>::const_iterator it = greenPhases.begin(); it != greenPhases.end(); ++it) {
808 6920 : const SUMOTime dur = logic->getPhases()[*it].duration;
809 : minGreenDuration = MIN2(minGreenDuration, dur);
810 : }
811 3635 : const int patchSeconds = (int)(STEPS2TIME(cycleTime - totalDuration) / (double)greenPhases.size());
812 3635 : const int patchSecondsRest = (int)(STEPS2TIME(cycleTime - totalDuration)) - patchSeconds * (int)greenPhases.size();
813 : //std::cout << "cT=" << cycleTime << " td=" << totalDuration << " pS=" << patchSeconds << " pSR=" << patchSecondsRest << "\n";
814 3635 : if (STEPS2TIME(minGreenDuration) + patchSeconds < MIN_GREEN_TIME
815 3630 : || STEPS2TIME(minGreenDuration) + patchSeconds + patchSecondsRest < MIN_GREEN_TIME
816 7259 : || greenPhases.size() == 0) {
817 11 : if (getID() != DummyID) {
818 30 : 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 10452 : for (std::vector<int>::const_iterator it = greenPhases.begin(); it != greenPhases.end(); ++it) {
823 8407 : logic->setPhaseDuration(*it, logic->getPhases()[*it].duration + TIME2STEPS(patchSeconds));
824 : }
825 3624 : if (greenPhases.size() > 0) {
826 3711 : logic->setPhaseDuration(greenPhases.front(), logic->getPhases()[greenPhases.front()].duration + TIME2STEPS(patchSecondsRest));
827 : }
828 3624 : 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 3663 : const int phaseCount = (int)allPhases.size();
835 : const int stateSize = (int)logic->getNumLinks();
836 18332 : for (int i = 0; i < phaseCount; ++i) {
837 14669 : std::string currState = allPhases[i].state;
838 14669 : const int prevIndex = (i == 0) ? phaseCount - 1 : i - 1;
839 14669 : const std::string prevState = allPhases[prevIndex].state;
840 14669 : const std::string nextState = allPhases[(i + 1) % phaseCount].state;
841 : bool updatedState = false;
842 229665 : for (int i1 = 0; i1 < stateSize; ++i1) {
843 214996 : if (currState[i1] == 'y' && (nextState[i1] == prevState[i1] || nextState[i1] == 'G') && (prevState[i1] == 'g' || prevState[i1] == 'G')) {
844 1898 : logic->setPhaseState(i, i1, (LinkState)prevState[i1]);
845 : updatedState = true;
846 : }
847 : }
848 : UNUSED_PARAMETER(updatedState); // disable warning
849 : #ifdef DEBUG_PHASES
850 : if (DEBUGCOND) {
851 : if (updatedState) {
852 : std::cout << getID() << " state of phase index " << i << " was patched due to yellow in between green\n";
853 : }
854 :
855 : }
856 : #endif
857 : }
858 :
859 :
860 3663 : myExtraConflictsReady = true;
861 : // this computation only makes sense for single nodes
862 3663 : if (myControlledNodes.size() == 1) {
863 3330 : myNeedsContRelationReady = true;
864 : }
865 3663 : if (totalDuration > 0) {
866 3663 : if (totalDuration > 3 * (greenTime + 2 * brakingTime + leftTurnTime) && !isNEMA && getID() != DummyID) {
867 36 : WRITE_WARNINGF(TL("The traffic light '%' has a high cycle time of %."), getID(), time2string(totalDuration));
868 : }
869 3663 : logic->closeBuilding();
870 : return logic;
871 : } else {
872 0 : delete logic;
873 0 : return nullptr;
874 : }
875 7326 : }
876 :
877 :
878 : bool
879 3734 : NBOwnTLDef::hasCrossing(const NBEdge* from, const NBEdge* to, const std::vector<NBNode::Crossing*>& crossings) {
880 : assert(to != 0);
881 5248 : for (auto c : crossings) {
882 : const NBNode::Crossing& cross = *c;
883 : // only check connections at this crossings node
884 2064 : if (to->getFromNode() == cross.node) {
885 3843 : for (EdgeVector::const_iterator it_e = cross.edges.begin(); it_e != cross.edges.end(); ++it_e) {
886 2702 : const NBEdge* edge = *it_e;
887 2702 : if (edge == from || edge == to) {
888 : return true;
889 : }
890 : }
891 : }
892 : }
893 : return false;
894 : }
895 :
896 :
897 : std::string
898 7105 : NBOwnTLDef::addPedestrianPhases(NBTrafficLightLogic* logic, const SUMOTime greenTime, const SUMOTime minDur, const SUMOTime maxDur,
899 : const SUMOTime earliestEnd, const SUMOTime latestEnd,
900 : std::string state, const std::vector<NBNode::Crossing*>& crossings, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
901 : // compute based on length of the crossing if not set by the user
902 7105 : 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 7105 : const SUMOTime minPedTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.crossing-min.time"));
905 : const std::string orig = state;
906 7105 : state = patchStateForCrossings(state, crossings, fromEdges, toEdges);
907 7105 : if (orig == state) {
908 : // add step
909 12868 : logic->addStep(greenTime, state, minDur, maxDur, earliestEnd, latestEnd);
910 : } else {
911 671 : const SUMOTime pedTime = greenTime - pedClearingTime;
912 671 : if (pedTime >= minPedTime) {
913 : // ensure clearing time for pedestrians
914 : const bool isSimpleActuatedCrossing = logic->getType() == TrafficLightType::ACTUATED
915 648 : && 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 1296 : 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 3623 : for (auto cross : crossings) {
927 2975 : if (cross->tlLinkIndex >= (int)fromEdges.size() || fromEdges[cross->tlLinkIndex] == nullptr) {
928 2925 : state[cross->tlLinkIndex] = 'r';
929 : }
930 2975 : if (cross->tlLinkIndex2 >= 0 && (cross->tlLinkIndex2 >= (int)fromEdges.size() || fromEdges[cross->tlLinkIndex2] == nullptr)) {
931 64 : state[cross->tlLinkIndex2] = 'r';
932 : }
933 : }
934 648 : logic->addStep(pedClearingTime, state);
935 : } else {
936 : state = orig;
937 : // not safe for pedestrians.
938 46 : 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 14210 : return state;
947 : }
948 :
949 :
950 : std::string
951 7105 : NBOwnTLDef::patchStateForCrossings(const std::string& state, const std::vector<NBNode::Crossing*>& crossings, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
952 : std::string result = state;
953 10349 : for (const NBNode::Crossing* cross : crossings) {
954 : bool isForbidden = false;
955 46843 : for (int i2 = 0; i2 < (int)fromEdges.size() && !isForbidden; ++i2) {
956 : // only check connections at this crossings node
957 43599 : if (fromEdges[i2] != 0 && toEdges[i2] != 0 && fromEdges[i2]->getToNode() == cross->node) {
958 98073 : for (EdgeVector::const_iterator it = cross->edges.begin(); it != cross->edges.end(); ++it) {
959 61409 : const NBEdge* edge = *it;
960 61409 : const LinkDirection i2dir = cross->node->getDirection(fromEdges[i2], toEdges[i2]);
961 61409 : if (state[i2] != 'r' && state[i2] != 's' && (edge == fromEdges[i2] ||
962 20156 : (edge == toEdges[i2] && (i2dir == LinkDirection::STRAIGHT || i2dir == LinkDirection::PARTLEFT || i2dir == LinkDirection::PARTRIGHT)))) {
963 : isForbidden = true;
964 : break;
965 : }
966 : }
967 : }
968 : }
969 3244 : const int i1 = cross->tlLinkIndex;
970 : assert(i1 >= 0 && i1 < (int)result.size());
971 3244 : const char newState = isForbidden ? 'r' : 'G';
972 3244 : if (i1 < (int)toEdges.size() && toEdges[i1] != nullptr && (result[i1] != newState || !isForbidden)) {
973 66 : if (cross->tlID != DummyID) {
974 54 : WRITE_WARNINGF(TL("Custom crossing linkIndex % conflicts with vehicular connections at tlLogic '%'"), i1, cross->tlID);
975 : }
976 : } else {
977 3178 : result[i1] = newState;
978 : }
979 3244 : if (cross->tlLinkIndex2 >= 0) {
980 : const int i2 = cross->tlLinkIndex2;
981 82 : if (i2 < (int)toEdges.size() && toEdges[i2] != nullptr && (result[i2] != newState || !isForbidden)) {
982 13 : if (cross->tlID != DummyID) {
983 24 : WRITE_WARNINGF(TL("Custom crossing linkIndex2 % conflicts with vehicular connections at tlLogic '%'"), i2, cross->tlID);
984 : }
985 : } else {
986 69 : result[i2] = newState;
987 : }
988 : }
989 : }
990 :
991 : // correct behaviour for roads that are in conflict with a pedestrian crossing
992 : assert(fromEdges.size() <= result.size());
993 91531 : for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
994 91531 : if (result[i1] == 'G') {
995 45236 : for (const NBNode::Crossing* cross : crossings) {
996 14431 : const int i2 = cross->tlLinkIndex;
997 14431 : const int i3 = cross->tlLinkIndex2;
998 14431 : if (fromEdges[i1] != 0 && toEdges[i1] != 0 && fromEdges[i1]->getToNode() == cross->node) {
999 12754 : if ((result[i2] == 'G' || (i3 >= 0 && result[i3] == 'G'))
1000 12770 : && cross->node->mustBrakeForCrossing(fromEdges[i1], toEdges[i1], *cross)) {
1001 991 : result[i1] = 'g';
1002 991 : break;
1003 : }
1004 : }
1005 : }
1006 : }
1007 : }
1008 7105 : return result;
1009 : }
1010 :
1011 :
1012 : std::string
1013 86 : NBOwnTLDef::patchNEMAStateForCrossings(const std::string& state,
1014 : const std::vector<NBNode::Crossing*>& crossings,
1015 : const EdgeVector& fromEdges,
1016 : const EdgeVector& toEdges,
1017 : const NBEdge* greenEdge, NBEdge* otherChosen) {
1018 : std::string result = state;
1019 86 : const int pos = (int)(state.size() - crossings.size()); // number of controlled vehicle links
1020 86 : const EdgeVector& all = greenEdge->getToNode()->getEdges();
1021 86 : EdgeVector::const_iterator start = std::find(all.begin(), all.end(), greenEdge);
1022 :
1023 : // permit crossings over edges between the current green edge and it's straight continuation
1024 86 : const NBEdge* endEdge = nullptr;
1025 653 : for (int i = 0; i < (int)state.size(); i++) {
1026 641 : if (state[i] == 'G' && fromEdges[i] == greenEdge
1027 792 : && greenEdge->getToNode()->getDirection(greenEdge, toEdges[i]) == LinkDirection::STRAIGHT) {
1028 : // straight edge found
1029 74 : endEdge = toEdges[i];
1030 74 : break;
1031 : }
1032 : }
1033 86 : if (endEdge == nullptr) {
1034 12 : endEdge = otherChosen;
1035 : }
1036 86 : if (endEdge == nullptr) {
1037 : // try to find the reverse edge of the green edge
1038 8 : auto itCW = start;
1039 8 : NBContHelper::nextCW(all, itCW);
1040 8 : if ((*itCW)->getFromNode() == greenEdge->getToNode()) {
1041 8 : endEdge = *itCW;
1042 : }
1043 : }
1044 86 : if (endEdge == nullptr) {
1045 : // at least prevent an infinite loop
1046 0 : endEdge = greenEdge;
1047 : }
1048 : //std::cout << " patchNEMAStateForCrossings green=" << greenEdge->getID() << " other=" << Named::getIDSecure(otherChosen) << " end=" << Named::getIDSecure(end) << " all=" << toString(all) << "\n";
1049 :
1050 86 : EdgeVector::const_iterator end = std::find(all.begin(), all.end(), endEdge);
1051 86 : if (end == all.end()) {
1052 : // at least prevent an infinite loop
1053 0 : end = start;
1054 : }
1055 86 : auto it = start;
1056 86 : NBContHelper::nextCCW(all, it);
1057 260 : for (; it != end; NBContHelper::nextCCW(all, it)) {
1058 274 : for (int ic = 0; ic < (int)crossings.size(); ++ic) {
1059 100 : const int i1 = pos + ic;
1060 100 : const NBNode::Crossing& cross = *crossings[ic];
1061 258 : for (const NBEdge* crossed : cross.edges) {
1062 : //std::cout << " cand=" << (*it)->getID() << " crossed=" << crossed->getID() << "\n";
1063 186 : if (crossed == *it) {
1064 28 : result[i1] = 'G';
1065 28 : break;
1066 : }
1067 : }
1068 : }
1069 : }
1070 : // correct behaviour for roads that are in conflict with a pedestrian crossing
1071 1246 : for (int i1 = 0; i1 < pos; ++i1) {
1072 1160 : if (result[i1] == 'G') {
1073 224 : for (int ic = 0; ic < (int)crossings.size(); ++ic) {
1074 72 : const NBNode::Crossing& crossing = *crossings[ic];
1075 72 : const int i2 = pos + ic;
1076 72 : if (result[i2] == 'G' && crossing.node->mustBrakeForCrossing(fromEdges[i1], toEdges[i1], crossing)) {
1077 12 : result[i1] = 'g';
1078 12 : break;
1079 : }
1080 : }
1081 : }
1082 : }
1083 86 : return result;
1084 : }
1085 :
1086 :
1087 : void
1088 5627 : NBOwnTLDef::collectLinks() {
1089 : myControlledLinks.clear();
1090 5627 : collectAllLinks(myControlledLinks);
1091 5627 : }
1092 :
1093 :
1094 : void
1095 4159 : NBOwnTLDef::setTLControllingInformation() const {
1096 : // set the information about the link's positions within the tl into the
1097 : // edges the links are starting at, respectively
1098 22065 : for (NBConnectionVector::const_iterator j = myControlledLinks.begin(); j != myControlledLinks.end(); ++j) {
1099 : const NBConnection& conn = *j;
1100 17906 : NBEdge* edge = conn.getFrom();
1101 17906 : edge->setControllingTLInformation(conn, getID());
1102 : }
1103 4159 : }
1104 :
1105 :
1106 : void
1107 0 : NBOwnTLDef::remapRemoved(NBEdge* /*removed*/, const EdgeVector& /*incoming*/,
1108 0 : const EdgeVector& /*outgoing*/) {}
1109 :
1110 :
1111 : void
1112 4186 : NBOwnTLDef::replaceRemoved(NBEdge* /*removed*/, int /*removedLane*/,
1113 4186 : NBEdge* /*by*/, int /*byLane*/, bool /*incoming*/) {}
1114 :
1115 :
1116 : void
1117 288 : NBOwnTLDef::initNeedsContRelation() const {
1118 288 : if (!myNeedsContRelationReady) {
1119 242 : if (myControlledNodes.size() > 0) {
1120 : // we use a dummy node just to maintain const-correctness
1121 : myNeedsContRelation.clear();
1122 910 : for (NBNode* n : myControlledNodes) {
1123 668 : NBOwnTLDef dummy(DummyID, n, 0, TrafficLightType::STATIC);
1124 668 : dummy.setParticipantsInformation();
1125 668 : NBTrafficLightLogic* tllDummy = dummy.computeLogicAndConts(0, true);
1126 668 : delete tllDummy;
1127 668 : myNeedsContRelation.insert(dummy.myNeedsContRelation.begin(), dummy.myNeedsContRelation.end());
1128 668 : n->removeTrafficLight(&dummy);
1129 668 : }
1130 : #ifdef DEBUG_CONTRELATION
1131 : if (DEBUGCOND) {
1132 : std::cout << " contRelations at " << getID() << " prog=" << getProgramID() << ":\n";
1133 : for (const StreamPair& s : myNeedsContRelation) {
1134 : std::cout << " " << s.from1->getID() << "->" << s.to1->getID() << " foe " << s.from2->getID() << "->" << s.to2->getID() << "\n";
1135 : }
1136 : }
1137 : #endif
1138 :
1139 : }
1140 242 : myNeedsContRelationReady = true;
1141 : }
1142 288 : }
1143 :
1144 :
1145 : EdgeVector
1146 3663 : NBOwnTLDef::getConnectedOuterEdges(const EdgeVector& incoming) {
1147 3663 : EdgeVector result = incoming;
1148 16085 : for (EdgeVector::iterator it = result.begin(); it != result.end();) {
1149 12422 : if ((*it)->getConnections().size() == 0 || (*it)->isInsideTLS()) {
1150 : it = result.erase(it);
1151 : } else {
1152 : ++it;
1153 : }
1154 : }
1155 3663 : return result;
1156 : }
1157 :
1158 :
1159 : std::string
1160 8067 : NBOwnTLDef::allowCompatible(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
1161 : const std::vector<int>& fromLanes, const std::vector<int>& toLanes) {
1162 16134 : state = allowSingleEdge(state, fromEdges);
1163 : #ifdef DEBUG_PHASES
1164 : if (DEBUGCOND) {
1165 : std::cout << " state after allowSingle " << state << "\n";
1166 : }
1167 : #endif
1168 8067 : if (myControlledNodes.size() > 1) {
1169 2044 : state = allowFollowers(state, fromEdges, toEdges);
1170 : #ifdef DEBUG_PHASES
1171 : if (DEBUGCOND) {
1172 : std::cout << " state after allowFollowers " << state << "\n";
1173 : }
1174 : #endif
1175 2044 : state = allowPredecessors(state, fromEdges, toEdges, fromLanes, toLanes);
1176 : #ifdef DEBUG_PHASES
1177 : if (DEBUGCOND) {
1178 : std::cout << " state after allowPredecessors " << state << "\n";
1179 : }
1180 : #endif
1181 : }
1182 8067 : return state;
1183 : }
1184 :
1185 :
1186 : std::string
1187 8067 : NBOwnTLDef::allowSingleEdge(std::string state, const EdgeVector& fromEdges) {
1188 : // if only one edge has green, ensure sure that all connections from that edge are green
1189 8067 : const int size = (int)fromEdges.size();
1190 : NBEdge* greenEdge = nullptr;
1191 89194 : for (int i1 = 0; i1 < size; ++i1) {
1192 85810 : if (state[i1] == 'G') {
1193 30233 : if (greenEdge == nullptr) {
1194 8067 : greenEdge = fromEdges[i1];
1195 22166 : } else if (greenEdge != fromEdges[i1]) {
1196 4683 : return state;
1197 : }
1198 : }
1199 : }
1200 3384 : if (greenEdge != nullptr) {
1201 35708 : for (int i1 = 0; i1 < size; ++i1) {
1202 32324 : if (fromEdges[i1] == greenEdge) {
1203 9677 : state[i1] = 'G';
1204 : }
1205 : }
1206 : }
1207 3384 : return state;
1208 : }
1209 :
1210 :
1211 : std::string
1212 1022 : NBOwnTLDef::allowFollowers(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
1213 : // check continuation within joined traffic lights
1214 : bool check = true;
1215 2628 : while (check) {
1216 : check = false;
1217 37140 : for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1218 35534 : if (state[i1] == 'G') {
1219 9884 : continue;
1220 : }
1221 25650 : if (forbidden(state, i1, fromEdges, toEdges, true)) {
1222 10152 : continue;
1223 : }
1224 : bool followsChosen = false;
1225 604426 : for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1226 591027 : if (state[i2] == 'G' && fromEdges[i1] == toEdges[i2]) {
1227 : followsChosen = true;
1228 : break;
1229 : }
1230 : }
1231 15498 : if (followsChosen) {
1232 2099 : state[i1] = 'G';
1233 : check = true;
1234 : }
1235 : }
1236 : }
1237 1022 : return state;
1238 : }
1239 :
1240 :
1241 : std::string
1242 1022 : NBOwnTLDef::allowPredecessors(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
1243 : const std::vector<int>& fromLanes, const std::vector<int>& toLanes) {
1244 : // also allow predecessors of chosen edges if the lanes match and there is no conflict
1245 : // (must be done after the followers are done because followers are less specific)
1246 : bool check = true;
1247 2057 : while (check) {
1248 : check = false;
1249 23060 : for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1250 22025 : if (state[i1] == 'G') {
1251 6911 : continue;
1252 : }
1253 15114 : if (forbidden(state, i1, fromEdges, toEdges, false)) {
1254 8334 : continue;
1255 : }
1256 : bool preceedsChosen = false;
1257 283514 : for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1258 276752 : if (state[i2] == 'G' && fromEdges[i2] == toEdges[i1]
1259 276831 : && fromLanes[i2] == toLanes[i1]) {
1260 : preceedsChosen = true;
1261 : break;
1262 : }
1263 : }
1264 6780 : if (preceedsChosen) {
1265 18 : state[i1] = 'G';
1266 : check = true;
1267 : }
1268 : }
1269 : }
1270 1022 : return state;
1271 : }
1272 :
1273 :
1274 : std::string
1275 6725 : NBOwnTLDef::allowUnrelated(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
1276 : const std::vector<bool>& isTurnaround,
1277 : const std::vector<NBNode::Crossing*>& crossings) {
1278 89688 : for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1279 82963 : if (state[i1] == 'G') {
1280 38007 : continue;
1281 : }
1282 : bool isForbidden = false;
1283 361992 : for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1284 491657 : if (state[i2] == 'G' && !isTurnaround[i2] &&
1285 243843 : (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true) || forbids(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2], true))) {
1286 : isForbidden = true;
1287 : break;
1288 : }
1289 : }
1290 44956 : if (!isForbidden && !hasCrossing(fromEdges[i1], toEdges[i1], crossings)) {
1291 3184 : state[i1] = 'G';
1292 : }
1293 : }
1294 6725 : return state;
1295 : }
1296 :
1297 :
1298 : std::string
1299 67 : NBOwnTLDef::allowByVClass(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges, SVCPermissions perm) {
1300 937 : for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1301 870 : SVCPermissions linkPerm = (fromEdges[i1]->getPermissions() & toEdges[i1]->getPermissions());
1302 870 : if ((linkPerm & ~perm) == 0) {
1303 279 : state[i1] = 'G';
1304 : }
1305 : }
1306 67 : return state;
1307 : }
1308 :
1309 :
1310 : bool
1311 40764 : NBOwnTLDef::forbidden(const std::string& state, int index, const EdgeVector& fromEdges, const EdgeVector& toEdges, bool allowCont) {
1312 1187613 : for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1313 1165335 : if (state[i2] == 'G' && foes(fromEdges[i2], toEdges[i2], fromEdges[index], toEdges[index])) {
1314 40802 : if (!allowCont || (
1315 29161 : !needsCont(fromEdges[i2], toEdges[i2], fromEdges[index], toEdges[index]) &&
1316 12927 : !needsCont(fromEdges[index], toEdges[index], fromEdges[i2], toEdges[i2]))) {
1317 18486 : return true;
1318 : }
1319 : }
1320 : }
1321 : return false;
1322 : }
1323 :
1324 :
1325 : std::string
1326 8110 : NBOwnTLDef::correctConflicting(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
1327 : const std::vector<bool>& isTurnaround,
1328 : const std::vector<int>& fromLanes,
1329 : const std::vector<int>& toLanes,
1330 : const std::vector<bool>& hadGreenMajor,
1331 : bool& haveForbiddenLeftMover,
1332 : std::vector<bool>& rightTurnConflicts,
1333 : std::vector<bool>& mergeConflicts) {
1334 8110 : const bool controlledWithin = !OptionsCont::getOptions().getBool("tls.uncontrolled-within");
1335 118559 : for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1336 110449 : if (state[i1] == 'G') {
1337 932459 : for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1338 883587 : if ((state[i2] == 'G' || state[i2] == 'g')) {
1339 424792 : if (NBNode::rightTurnConflict(
1340 : fromEdges[i1], toEdges[i1], fromLanes[i1], fromEdges[i2], toEdges[i2], fromLanes[i2])) {
1341 : rightTurnConflicts[i1] = true;
1342 : }
1343 424792 : if (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true, controlledWithin) || rightTurnConflicts[i1]) {
1344 31648 : state[i1] = 'g';
1345 31648 : if (myControlledNodes.size() == 1) {
1346 28667 : myNeedsContRelation.insert(StreamPair(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2]));
1347 : #ifdef DEBUG_CONTRELATION
1348 : if (DEBUGCOND) {
1349 : std::cout << getID() << " p=" << getProgramID() << " contRel: " << fromEdges[i1]->getID() << "->" << toEdges[i1]->getID()
1350 : << " foe " << fromEdges[i2]->getID() << "->" << toEdges[i2]->getID() << "\n";
1351 : }
1352 : #endif
1353 : }
1354 31648 : if (!isTurnaround[i1] && !hadGreenMajor[i1] && !rightTurnConflicts[i1]) {
1355 19260 : haveForbiddenLeftMover = true;
1356 : }
1357 393144 : } else if (fromEdges[i1] == fromEdges[i2]
1358 185509 : && fromLanes[i1] != fromLanes[i2]
1359 79928 : && toEdges[i1] == toEdges[i2]
1360 22505 : && toLanes[i1] == toLanes[i2]
1361 393450 : && fromEdges[i1]->getToNode()->mergeConflictYields(fromEdges[i1], fromLanes[i1], fromLanes[i2], toEdges[i1], toLanes[i1])) {
1362 : mergeConflicts[i1] = true;
1363 148 : state[i1] = 'g';
1364 : }
1365 : }
1366 : }
1367 : }
1368 110449 : if (state[i1] == 'r') {
1369 61759 : if (fromEdges[i1]->getToNode()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED &&
1370 261 : fromEdges[i1]->getToNode()->getDirection(fromEdges[i1], toEdges[i1]) == LinkDirection::RIGHT) {
1371 56 : state[i1] = 's';
1372 : // do not allow right-on-red when in conflict with exclusive left-turn phase
1373 824 : for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1374 1046 : if (state[i2] == 'G' && !isTurnaround[i2] &&
1375 499 : (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true) ||
1376 233 : forbids(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2], true))) {
1377 74 : const LinkDirection foeDir = fromEdges[i2]->getToNode()->getDirection(fromEdges[i2], toEdges[i2]);
1378 74 : if (foeDir == LinkDirection::LEFT || foeDir == LinkDirection::PARTLEFT) {
1379 12 : state[i1] = 'r';
1380 12 : break;
1381 : }
1382 : }
1383 : }
1384 56 : if (state[i1] == 's') {
1385 : // handle right-on-red conflicts
1386 716 : for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1387 913 : if (state[i2] == 'G' && !isTurnaround[i2] &&
1388 449 : (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true) ||
1389 208 : forbids(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2], true))) {
1390 62 : myExtraConflicts.insert(std::make_pair(i1, i2));
1391 : }
1392 : }
1393 : }
1394 : }
1395 : }
1396 : }
1397 8110 : return state;
1398 : }
1399 :
1400 :
1401 : std::string
1402 10 : NBOwnTLDef::correctMixed(std::string state, const EdgeVector& fromEdges,
1403 : const std::vector<int>& fromLanes,
1404 : bool& buildMixedGreenPhase, std::vector<bool>& mixedGreen) {
1405 152 : for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1406 142 : if ((state[i1] == 'G' || state[i1] == 'g')) {
1407 368 : for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
1408 320 : if (i1 != i2 && fromEdges[i1] == fromEdges[i2] && fromLanes[i1] == fromLanes[i2]
1409 360 : && state[i2] != 'G' && state[i2] != 'g') {
1410 12 : state[i1] = state[i2];
1411 : //std::cout << " mixedGreen i1=" << i1 << " i2=" << i2 << "\n";
1412 : mixedGreen[i1] = true;
1413 12 : if (fromEdges[i1]->getNumLanesThatAllow(SVC_PASSENGER) > 1) {
1414 4 : buildMixedGreenPhase = true;
1415 : }
1416 : }
1417 : }
1418 : }
1419 : }
1420 10 : return state;
1421 : }
1422 :
1423 :
1424 : void
1425 116 : NBOwnTLDef::addGreenWithin(NBTrafficLightLogic* logic, const EdgeVector& fromEdges, EdgeVector& toProc) {
1426 116 : std::vector<bool> foundGreen(fromEdges.size(), false);
1427 1111 : for (const auto& phase : logic->getPhases()) {
1428 : const std::string state = phase.state;
1429 27200 : for (int j = 0; j < (int)fromEdges.size(); j++) {
1430 26205 : LinkState ls = (LinkState)state[j];
1431 26205 : if (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR) {
1432 : foundGreen[j] = true;
1433 : }
1434 : }
1435 : }
1436 5040 : for (int j = 0; j < (int)foundGreen.size(); j++) {
1437 2462 : if (!foundGreen[j]) {
1438 4 : NBEdge* e = fromEdges[j];
1439 4 : if (std::find(toProc.begin(), toProc.end(), e) == toProc.end()) {
1440 3 : toProc.push_back(e);
1441 : }
1442 : }
1443 : }
1444 116 : }
1445 :
1446 :
1447 : void
1448 331 : NBOwnTLDef::addPedestrianScramble(NBTrafficLightLogic* logic, int totalNumLinks, SUMOTime /* greenTime */, SUMOTime brakingTime,
1449 : const std::vector<NBNode::Crossing*>& crossings, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
1450 : // check both indices for each crossing (they may have green in different phases)
1451 331 : std::vector<bool> foundGreen(crossings.size() * 2, false);
1452 : const std::vector<NBTrafficLightLogic::PhaseDefinition>& phases = logic->getPhases();
1453 2356 : for (int i = 0; i < (int)phases.size(); i++) {
1454 2025 : const std::string state = phases[i].state;
1455 : int j = 0;
1456 11503 : for (auto cross : crossings) {
1457 9478 : LinkState ls = (LinkState)state[cross->tlLinkIndex];
1458 9478 : LinkState ls2 = cross->tlLinkIndex2 >= 0 ? (LinkState)state[cross->tlLinkIndex2] : ls;
1459 9478 : if (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR) {
1460 : foundGreen[j] = true;
1461 : }
1462 9478 : if (ls2 == LINKSTATE_TL_GREEN_MAJOR || ls2 == LINKSTATE_TL_GREEN_MINOR) {
1463 1631 : foundGreen[j + crossings.size()] = true;
1464 : }
1465 9478 : j++;
1466 : }
1467 : }
1468 : #ifdef DEBUG_PHASES
1469 : if (DEBUGCOND2(logic)) {
1470 : std::cout << " foundCrossingGreen=" << toString(foundGreen) << "\n";
1471 : }
1472 : #endif
1473 4587 : for (int j = 0; j < (int)foundGreen.size(); j++) {
1474 2206 : if (!foundGreen[j]) {
1475 : // add a phase where all pedestrians may walk, (preceded by a yellow phase and followed by a clearing phase)
1476 78 : if (phases.size() > 0) {
1477 : bool needYellowPhase = false;
1478 : std::string state = phases.back().state;
1479 707 : for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
1480 629 : if (state[i1] == 'G' || state[i1] == 'g') {
1481 99 : state[i1] = 'y';
1482 : needYellowPhase = true;
1483 : }
1484 : }
1485 : // add yellow step
1486 78 : if (needYellowPhase && brakingTime > 0) {
1487 0 : logic->addStep(brakingTime, state);
1488 : }
1489 : }
1490 78 : const SUMOTime pedClearingTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.crossing-clearance.time"));
1491 78 : const SUMOTime scrambleTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.scramble.time"));
1492 78 : addPedestrianPhases(logic, scrambleTime + pedClearingTime, UNSPECIFIED_DURATION,
1493 78 : UNSPECIFIED_DURATION, UNSPECIFIED_DURATION, UNSPECIFIED_DURATION, std::string(totalNumLinks, 'r'), crossings, fromEdges, toEdges);
1494 78 : break;
1495 : }
1496 : }
1497 331 : }
1498 :
1499 :
1500 : void
1501 4116 : NBOwnTLDef::buildAllRedState(SUMOTime allRedTime, NBTrafficLightLogic* logic, const std::string& state) {
1502 4116 : if (allRedTime > 0) {
1503 : // build all-red phase
1504 : std::string allRedState = state;
1505 6053 : for (int i = 0; i < (int)state.size(); i++) {
1506 5777 : if (allRedState[i] == 'Y' || allRedState[i] == 'y') {
1507 1678 : allRedState[i] = 'r';
1508 : }
1509 : }
1510 276 : logic->addStep(TIME2STEPS(ceil(STEPS2TIME(allRedTime))), allRedState);
1511 : }
1512 4116 : }
1513 :
1514 :
1515 : int
1516 4243 : NBOwnTLDef::maxCrossingIndex(const NBNode* node) const {
1517 : int result = 0;
1518 5470 : for (auto crossing : node->getCrossings()) {
1519 1227 : result = MAX2(result, crossing->tlLinkIndex);
1520 1227 : result = MAX2(result, crossing->tlLinkIndex2);
1521 4243 : }
1522 4243 : return result;
1523 : }
1524 :
1525 : void
1526 0 : NBOwnTLDef::fixSuperfluousYellow(NBTrafficLightLogic* logic) const {
1527 : // assume that yellow states last at most one phase
1528 : const int n = logic->getNumLinks();
1529 0 : const int p = (int)logic->getPhases().size();
1530 0 : for (int i1 = 0; i1 < n; ++i1) {
1531 0 : LinkState prev = (LinkState)logic->getPhases().back().state[i1];
1532 0 : for (int i2 = 0; i2 < p; ++i2) {
1533 0 : LinkState cur = (LinkState)logic->getPhases()[i2].state[i1];
1534 0 : LinkState next = (LinkState)logic->getPhases()[(i2 + 1) % p].state[i1];
1535 0 : if (cur == LINKSTATE_TL_YELLOW_MINOR
1536 0 : && (prev == LINKSTATE_TL_GREEN_MAJOR || prev == LINKSTATE_TL_YELLOW_MINOR)
1537 0 : && next == LINKSTATE_TL_GREEN_MAJOR) {
1538 0 : logic->setPhaseState(i2, i1, prev);
1539 : }
1540 : prev = cur;
1541 : }
1542 : }
1543 0 : }
1544 :
1545 :
1546 : void
1547 0 : NBOwnTLDef::deactivateAlwaysGreen(NBTrafficLightLogic* logic) const {
1548 : const int n = logic->getNumLinks();
1549 0 : std::vector<bool> alwaysGreen(n, true);
1550 0 : for (int i1 = 0; i1 < n; ++i1) {
1551 0 : for (const auto& phase : logic->getPhases()) {
1552 0 : if (phase.state[i1] != 'G') {
1553 : alwaysGreen[i1] = false;
1554 0 : break;
1555 : }
1556 : }
1557 : }
1558 0 : const int p = (int)logic->getPhases().size();
1559 0 : for (int i1 = 0; i1 < n; ++i1) {
1560 0 : if (alwaysGreen[i1]) {
1561 0 : for (int i2 = 0; i2 < p; ++i2) {
1562 0 : logic->setPhaseState(i2, i1, LINKSTATE_TL_OFF_NOSIGNAL);
1563 : }
1564 : }
1565 : }
1566 0 : }
1567 :
1568 :
1569 : void
1570 11 : NBOwnTLDef::deactivateInsideEdges(NBTrafficLightLogic* logic, const EdgeVector& fromEdges) const {
1571 11 : const int n = (int)fromEdges.size();
1572 11 : const int p = (int)logic->getPhases().size();
1573 89 : for (int i1 = 0; i1 < n; ++i1) {
1574 78 : if (fromEdges[i1]->isInsideTLS()) {
1575 470 : for (int i2 = 0; i2 < p; ++i2) {
1576 428 : logic->setPhaseState(i2, i1, LINKSTATE_TL_OFF_NOSIGNAL);
1577 : }
1578 : }
1579 : }
1580 11 : }
1581 :
1582 :
1583 : SUMOTime
1584 24 : NBOwnTLDef::computeEscapeTime(const std::string& state, const EdgeVector& fromEdges, const EdgeVector& toEdges) const {
1585 24 : const int n = (int)fromEdges.size();
1586 : double maxTime = 0;
1587 232 : for (int i1 = 0; i1 < n; ++i1) {
1588 208 : if (state[i1] == 'y' && !fromEdges[i1]->isInsideTLS()) {
1589 344 : for (int i2 = 0; i2 < n; ++i2) {
1590 312 : if (fromEdges[i2]->isInsideTLS()) {
1591 180 : double gapSpeed = (toEdges[i1]->getSpeed() + fromEdges[i2]->getSpeed()) / 2;
1592 180 : double time = fromEdges[i1]->getGeometry().back().distanceTo2D(fromEdges[i2]->getGeometry().back()) / gapSpeed;
1593 : maxTime = MAX2(maxTime, time);
1594 : }
1595 : }
1596 : }
1597 : }
1598 : // give some slack
1599 24 : return TIME2STEPS(floor(maxTime * 1.2 + 5));
1600 : }
1601 :
1602 :
1603 : int
1604 0 : NBOwnTLDef::getMaxIndex() {
1605 0 : setParticipantsInformation();
1606 0 : NBTrafficLightLogic* logic = compute(OptionsCont::getOptions());
1607 0 : if (logic != nullptr) {
1608 0 : return logic->getNumLinks() - 1;
1609 : } else {
1610 : return -1;
1611 : }
1612 : }
1613 :
1614 :
1615 : bool
1616 133 : NBOwnTLDef::corridorLike() const {
1617 133 : if (getID() == DummyID) {
1618 : // avoid infinite recursion
1619 : return true;
1620 : }
1621 : // setParticipantsInformation resets myAmInTLS so we need to make a copy
1622 : std::vector<bool> edgeInsideTLS;
1623 441 : for (const NBEdge* e : myIncomingEdges) {
1624 399 : edgeInsideTLS.push_back(e->isInsideTLS());
1625 : }
1626 : assert(myControlledNodes.size() >= 2);
1627 42 : NBOwnTLDef dummy(DummyID, myControlledNodes, 0, TrafficLightType::STATIC);
1628 42 : dummy.setParticipantsInformation();
1629 42 : NBTrafficLightLogic* tllDummy = dummy.computeLogicAndConts(0, true);
1630 : int greenPhases = 0;
1631 162 : for (const auto& phase : tllDummy->getPhases()) {
1632 120 : if (phase.state.find_first_of("gG") != std::string::npos) {
1633 120 : greenPhases++;
1634 : }
1635 : }
1636 42 : delete tllDummy;
1637 212 : for (const auto& controlledNode : myControlledNodes) {
1638 170 : controlledNode->removeTrafficLight(&dummy);
1639 : }
1640 : int i = 0;
1641 441 : for (NBEdge* e : myIncomingEdges) {
1642 : e->setInsideTLS(edgeInsideTLS[i]);
1643 399 : i++;
1644 : }
1645 42 : return greenPhases <= 2;
1646 42 : }
1647 :
1648 :
1649 : NBTrafficLightLogic*
1650 26 : NBOwnTLDef::buildNemaPhases(
1651 : const EdgeVector& fromEdges,
1652 : const EdgeVector& toEdges,
1653 : const std::vector<NBNode::Crossing*>& crossings,
1654 : const std::vector<std::pair<NBEdge*, NBEdge*> >& chosenList,
1655 : const std::vector<std::string>& straightStates,
1656 : const std::vector<std::string>& leftStates) {
1657 26 : if (chosenList.size() != 2) {
1658 : return nullptr;
1659 : }
1660 25 : const SUMOTime dur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.cycle.time"));
1661 25 : const SUMOTime vehExt = TIME2STEPS(OptionsCont::getOptions().getInt("tls.nema.vehExt"));
1662 25 : const SUMOTime yellow = TIME2STEPS(OptionsCont::getOptions().getInt("tls.nema.yellow"));
1663 25 : const SUMOTime red = TIME2STEPS(OptionsCont::getOptions().getInt("tls.nema.red"));
1664 25 : const SUMOTime minMinDur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.min-dur"));
1665 25 : const SUMOTime maxDur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.max-dur"));
1666 25 : const SUMOTime earliestEnd = UNSPECIFIED_DURATION;
1667 : const SUMOTime latestEnd = UNSPECIFIED_DURATION;
1668 :
1669 25 : const int totalNumLinks = (int)straightStates[0].size();
1670 25 : NBTrafficLightLogic* logic = new NBTrafficLightLogic(getID(), getProgramID(), totalNumLinks, myOffset, myType);
1671 25 : std::vector<int> ring1({1, 2, 3, 4});
1672 25 : std::vector<int> ring2({5, 6, 7, 8});
1673 25 : std::vector<int> barrier1({4, 8});
1674 25 : std::vector<int> barrier2({2, 6});
1675 25 : int phaseNameLeft = 1;
1676 73 : for (int i = 0; i < (int)chosenList.size(); i++) {
1677 49 : NBEdge* e1 = chosenList[i].first;
1678 : assert(e1 != nullptr);
1679 49 : NBEdge* e2 = chosenList[i].second;
1680 49 : if (i < (int)leftStates.size()) {
1681 98 : std::string left1 = filterState(leftStates[i], fromEdges, e1);
1682 49 : if (left1 != "") {
1683 78 : logic->addStep(dur, left1, minMinDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, toString(phaseNameLeft));
1684 : }
1685 : }
1686 49 : if (e2 != nullptr) {
1687 38 : std::string straight2 = filterState(straightStates[i], fromEdges, e2);
1688 76 : straight2 = patchNEMAStateForCrossings(straight2, crossings, fromEdges, toEdges, e2, e1);
1689 :
1690 76 : logic->addStep(dur, straight2, minMinDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, toString(phaseNameLeft + 1));
1691 38 : if (i < (int)leftStates.size()) {
1692 76 : std::string left2 = filterState(leftStates[i], fromEdges, e2);
1693 38 : if (left2 != "") {
1694 52 : logic->addStep(dur, left2, minMinDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, toString(phaseNameLeft + 4));
1695 : }
1696 : }
1697 :
1698 : }
1699 49 : std::string straight1 = filterState(straightStates[i], fromEdges, e1);
1700 49 : if (straight1 == "") {
1701 1 : delete logic;
1702 : return nullptr;
1703 : }
1704 96 : straight1 = patchNEMAStateForCrossings(straight1, crossings, fromEdges, toEdges, e1, e2);
1705 96 : logic->addStep(dur, straight1, minMinDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, toString(phaseNameLeft + 5));
1706 48 : phaseNameLeft += 2;
1707 : }
1708 : std::map<int, int> names; // nema phase name -> sumo phase index
1709 174 : for (int i = 0; i < (int)logic->getPhases().size(); i++) {
1710 150 : names[StringUtils::toInt(logic->getPhases()[i].name)] = i;
1711 : }
1712 :
1713 24 : filterMissingNames(ring1, names, false);
1714 24 : filterMissingNames(ring2, names, false);
1715 24 : filterMissingNames(barrier1, names, true, 8);
1716 24 : filterMissingNames(barrier2, names, true, 6);
1717 24 : if (ring1[0] == 0 && ring1[1] == 0) {
1718 1 : ring1[1] = 6;
1719 : }
1720 24 : if (ring1[2] == 0 && ring1[3] == 0) {
1721 3 : ring1[3] = 8;
1722 : }
1723 24 : fixDurationSum(logic, names, ring1[0], ring1[1], ring2[0], ring2[1]);
1724 24 : fixDurationSum(logic, names, ring1[2], ring1[3], ring2[2], ring2[3]);
1725 :
1726 48 : logic->setParameter("ring1", joinToString(ring1, ","));
1727 48 : logic->setParameter("ring2", joinToString(ring2, ","));
1728 48 : logic->setParameter("barrierPhases", joinToString(barrier1, ","));
1729 48 : logic->setParameter("barrier2Phases", joinToString(barrier2, ","));
1730 : return logic;
1731 25 : }
1732 :
1733 :
1734 : std::string
1735 174 : NBOwnTLDef::filterState(std::string state, const EdgeVector& fromEdges, const NBEdge* e) {
1736 : bool haveGreen = false;
1737 2498 : for (int j = 0; j < (int)fromEdges.size(); j++) {
1738 2324 : if (fromEdges[j] != e) {
1739 1698 : state[j] = 'r';
1740 626 : } else if (state[j] != 'r') {
1741 : haveGreen = true;
1742 : }
1743 : }
1744 174 : if (haveGreen) {
1745 151 : return state;
1746 : } else {
1747 23 : return "";
1748 : }
1749 : }
1750 :
1751 : void
1752 96 : NBOwnTLDef::filterMissingNames(std::vector<int>& vec, const std::map<int, int>& names, bool isBarrier, int barrierDefault) {
1753 384 : for (int i = 0; i < (int)vec.size(); i++) {
1754 288 : if (names.count(vec[i]) == 0) {
1755 52 : if (isBarrier) {
1756 10 : if (names.count(vec[i] - 1) > 0) {
1757 6 : vec[i] = vec[i] - 1;
1758 : } else {
1759 4 : vec[i] = barrierDefault;
1760 : }
1761 : } else {
1762 42 : vec[i] = 0;
1763 : }
1764 : }
1765 : }
1766 96 : }
1767 :
1768 : void
1769 48 : NBOwnTLDef::fixDurationSum(NBTrafficLightLogic* logic, const std::map<int, int>& names, int ring1a, int ring1b, int ring2a, int ring2b) {
1770 : std::set<int> ring1existing;
1771 : std::set<int> ring2existing;
1772 : if (names.count(ring1a) != 0) {
1773 : ring1existing.insert(ring1a);
1774 : }
1775 : if (names.count(ring1b) != 0) {
1776 : ring1existing.insert(ring1b);
1777 : }
1778 : if (names.count(ring2a) != 0) {
1779 : ring2existing.insert(ring2a);
1780 : }
1781 : if (names.count(ring2b) != 0) {
1782 : ring2existing.insert(ring2b);
1783 : }
1784 48 : if (ring1existing.size() > 0 && ring2existing.size() > 0 &&
1785 : ring1existing.size() != ring2existing.size()) {
1786 : int pI; // sumo phase index
1787 6 : if (ring1existing.size() < ring2existing.size()) {
1788 0 : pI = names.find(*ring1existing.begin())->second;
1789 : } else {
1790 6 : pI = names.find(*ring2existing.begin())->second;
1791 : }
1792 6 : const auto& p = logic->getPhases()[pI];
1793 6 : SUMOTime newMaxDur = 2 * p.maxDur + p.yellow + p.red;
1794 6 : logic->setPhaseMaxDuration(pI, newMaxDur);
1795 : }
1796 48 : }
1797 :
1798 : /****************************************************************************/
|