1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    NBOwnTLDef.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Sascha Krieg
14 /// @author  Michael Behrisch
15 /// @date    Tue, 29.05.2005
16 /// @version $Id$
17 ///
18 // A traffic light logics which must be computed (only nodes/edges are given)
19 /****************************************************************************/
20 
21 
22 // ===========================================================================
23 // included modules
24 // ===========================================================================
25 #include <config.h>
26 
27 #include <vector>
28 #include <cassert>
29 #include <iterator>
30 #include "NBTrafficLightDefinition.h"
31 #include "NBNode.h"
32 #include "NBOwnTLDef.h"
33 #include "NBTrafficLightLogic.h"
34 #include <utils/common/MsgHandler.h>
35 #include <utils/common/UtilExceptions.h>
36 #include <utils/common/ToString.h>
37 #include <utils/options/OptionsCont.h>
38 #include <utils/options/Option.h>
39 
40 #define HEIGH_WEIGHT 2
41 #define LOW_WEIGHT .5;
42 
43 #define MIN_GREEN_TIME 5
44 
45 //#define DEBUG_STREAM_ORDERING
46 //#define DEBUG_PHASES
47 //#define DEBUGCOND (getID() == "cluster_251050941_280598736_280598739_28902891_3142549227_3142550438")
48 //#define DEBUGEDGE(edge) (edge->getID() == "23209153#1" || edge->getID() == "319583927#0")
49 //#define DEBUGCOND (true)
50 //#define DEBUGEDGE(edge) (true)
51 
52 // ===========================================================================
53 // member method definitions
54 // ===========================================================================
NBOwnTLDef(const std::string & id,const std::vector<NBNode * > & junctions,SUMOTime offset,TrafficLightType type)55 NBOwnTLDef::NBOwnTLDef(const std::string& id,
56                        const std::vector<NBNode*>& junctions, SUMOTime offset,
57                        TrafficLightType type) :
58     NBTrafficLightDefinition(id, junctions, DefaultProgramID, offset, type),
59     myHaveSinglePhase(false) {
60 }
61 
62 
NBOwnTLDef(const std::string & id,NBNode * junction,SUMOTime offset,TrafficLightType type)63 NBOwnTLDef::NBOwnTLDef(const std::string& id, NBNode* junction, SUMOTime offset,
64                        TrafficLightType type) :
65     NBTrafficLightDefinition(id, junction, DefaultProgramID, offset, type),
66     myHaveSinglePhase(false) {
67 }
68 
69 
NBOwnTLDef(const std::string & id,SUMOTime offset,TrafficLightType type)70 NBOwnTLDef::NBOwnTLDef(const std::string& id, SUMOTime offset,
71                        TrafficLightType type) :
72     NBTrafficLightDefinition(id, DefaultProgramID, offset, type),
73     myHaveSinglePhase(false) {
74 }
75 
76 
~NBOwnTLDef()77 NBOwnTLDef::~NBOwnTLDef() {}
78 
79 
80 int
getToPrio(const NBEdge * const e)81 NBOwnTLDef::getToPrio(const NBEdge* const e) {
82     return e->getJunctionPriority(e->getToNode());
83 }
84 
85 
86 double
getDirectionalWeight(LinkDirection dir)87 NBOwnTLDef::getDirectionalWeight(LinkDirection dir) {
88     switch (dir) {
89         case LINKDIR_STRAIGHT:
90         case LINKDIR_PARTLEFT:
91         case LINKDIR_PARTRIGHT:
92             return HEIGH_WEIGHT;
93         case LINKDIR_LEFT:
94         case LINKDIR_RIGHT:
95             return LOW_WEIGHT;
96         default:
97             break;
98     }
99     return 0;
100 }
101 
102 double
computeUnblockedWeightedStreamNumber(const NBEdge * const e1,const NBEdge * const e2)103 NBOwnTLDef::computeUnblockedWeightedStreamNumber(const NBEdge* const e1, const NBEdge* const e2) {
104     double val = 0;
105     for (int e1l = 0; e1l < e1->getNumLanes(); e1l++) {
106         std::vector<NBEdge::Connection> approached1 = e1->getConnectionsFromLane(e1l);
107         for (int e2l = 0; e2l < e2->getNumLanes(); e2l++) {
108             std::vector<NBEdge::Connection> approached2 = e2->getConnectionsFromLane(e2l);
109             for (std::vector<NBEdge::Connection>::iterator e1c = approached1.begin(); e1c != approached1.end(); ++e1c) {
110                 if (e1->getTurnDestination() == (*e1c).toEdge) {
111                     continue;
112                 }
113                 for (std::vector<NBEdge::Connection>::iterator e2c = approached2.begin(); e2c != approached2.end(); ++e2c) {
114                     if (e2->getTurnDestination() == (*e2c).toEdge) {
115                         continue;
116                     }
117                     const double sign = (forbids(e1, (*e1c).toEdge, e2, (*e2c).toEdge, true)
118                                          || forbids(e2, (*e2c).toEdge, e1, (*e1c).toEdge, true)) ? -1 : 1;
119                     double w1;
120                     double w2;
121                     if (e1->getJunctionPriority(e1->getToNode()) == e2->getJunctionPriority(e2->getToNode())) {
122                         w1 = getDirectionalWeight(e1->getToNode()->getDirection(e1, (*e1c).toEdge));
123                         w2 = getDirectionalWeight(e2->getToNode()->getDirection(e2, (*e2c).toEdge));
124                     } else {
125                         if (e1->getJunctionPriority(e1->getToNode()) > e2->getJunctionPriority(e2->getToNode())) {
126                             w1 = HEIGH_WEIGHT;
127                             w2 = LOW_WEIGHT;
128                         } else {
129                             w1 = LOW_WEIGHT;
130                             w2 = HEIGH_WEIGHT;
131                         }
132                         if (sign == -1) {
133                             // extra penalty if edges with different junction priority are in conflict
134                             w1 *= 2;
135                             w2 *= 2;
136                         }
137                     }
138                     val += sign * w1;
139                     val += sign * w2;
140 #ifdef DEBUG_STREAM_ORDERING
141                     if (DEBUGCOND && DEBUGEDGE(e2) && DEBUGEDGE(e1)) {
142                         std::cout << "      sign=" << sign << " w1=" << w1 << " w2=" << w2 << " val=" << val
143                                   << " c1=" << (*e1c).getDescription(e1)
144                                   << " c2=" << (*e2c).getDescription(e2)
145                                   << "\n";
146                     }
147 #endif
148                 }
149             }
150         }
151     }
152 #ifdef DEBUG_STREAM_ORDERING
153     if (DEBUGCOND && DEBUGEDGE(e2) && DEBUGEDGE(e1)) {
154         std::cout << "     computeUnblockedWeightedStreamNumber e1=" << e1->getID() << " e2=" << e2->getID() << " val=" << val << "\n";
155     }
156 #endif
157     return val;
158 }
159 
160 
161 std::pair<NBEdge*, NBEdge*>
getBestCombination(const EdgeVector & edges)162 NBOwnTLDef::getBestCombination(const EdgeVector& edges) {
163     std::pair<NBEdge*, NBEdge*> bestPair(static_cast<NBEdge*>(nullptr), static_cast<NBEdge*>(nullptr));
164     double bestValue = -std::numeric_limits<double>::max();
165     for (EdgeVector::const_iterator i = edges.begin(); i != edges.end(); ++i) {
166         for (EdgeVector::const_iterator j = i + 1; j != edges.end(); ++j) {
167             const double value = computeUnblockedWeightedStreamNumber(*i, *j);
168             if (value > bestValue) {
169                 bestValue = value;
170                 bestPair = std::pair<NBEdge*, NBEdge*>(*i, *j);
171             } else if (value == bestValue) {
172                 const double ca = GeomHelper::getMinAngleDiff((*i)->getAngleAtNode((*i)->getToNode()), (*j)->getAngleAtNode((*j)->getToNode()));
173                 const double oa = GeomHelper::getMinAngleDiff(bestPair.first->getAngleAtNode(bestPair.first->getToNode()), bestPair.second->getAngleAtNode(bestPair.second->getToNode()));
174                 if (fabs(oa - ca) < NUMERICAL_EPS) { // break ties by id
175                     if (bestPair.first->getID() < (*i)->getID()) {
176                         bestPair = std::pair<NBEdge*, NBEdge*>(*i, *j);
177                     }
178                 } else if (oa < ca) {
179                     bestPair = std::pair<NBEdge*, NBEdge*>(*i, *j);
180                 }
181             }
182         }
183     }
184     if (bestValue <= 0) {
185         // do not group edges
186         bestPair.second = nullptr;
187 
188     }
189 #ifdef DEBUG_STREAM_ORDERING
190     if (DEBUGCOND) {
191         std::cout << "   getBestCombination bestValue=" << bestValue << "  best=" << Named::getIDSecure(bestPair.first) << ", " << Named::getIDSecure(bestPair.second) << "\n";
192     }
193 #endif
194     return bestPair;
195 }
196 
197 
198 std::pair<NBEdge*, NBEdge*>
getBestPair(EdgeVector & incoming)199 NBOwnTLDef::getBestPair(EdgeVector& incoming) {
200     if (incoming.size() == 1) {
201         // only one there - return the one
202         std::pair<NBEdge*, NBEdge*> ret(*incoming.begin(), static_cast<NBEdge*>(nullptr));
203         incoming.clear();
204         return ret;
205     }
206     // determine the best combination
207     //  by priority, first
208     EdgeVector used;
209     std::sort(incoming.begin(), incoming.end(), edge_by_incoming_priority_sorter());
210     used.push_back(*incoming.begin()); // the first will definitely be used
211     // get the ones with the same priority
212     int prio = getToPrio(*used.begin());
213     for (EdgeVector::iterator i = incoming.begin() + 1; i != incoming.end() && prio == getToPrio(*i); ++i) {
214         used.push_back(*i);
215     }
216     //  if there only lower priorised, use these, too
217     if (used.size() < 2) {
218         used = incoming;
219     }
220     std::pair<NBEdge*, NBEdge*> ret = getBestCombination(used);
221 #ifdef DEBUG_STREAM_ORDERING
222     if (DEBUGCOND) {
223         std::cout << "getBestPair tls=" << getID() << " incoming=" << toString(incoming) << " prio=" << prio << " used=" << toString(used) << " best=" << Named::getIDSecure(ret.first) << ", " << Named::getIDSecure(ret.second) << "\n";
224     }
225 #endif
226 
227     incoming.erase(find(incoming.begin(), incoming.end(), ret.first));
228     if (ret.second != nullptr) {
229         incoming.erase(find(incoming.begin(), incoming.end(), ret.second));
230     }
231     return ret;
232 }
233 
234 NBTrafficLightLogic*
myCompute(int brakingTimeSeconds)235 NBOwnTLDef::myCompute(int brakingTimeSeconds) {
236     return computeLogicAndConts(brakingTimeSeconds);
237 }
238 
239 NBTrafficLightLogic*
computeLogicAndConts(int brakingTimeSeconds,bool onlyConts)240 NBOwnTLDef::computeLogicAndConts(int brakingTimeSeconds, bool onlyConts) {
241     myNeedsContRelation.clear();
242     myRightOnRedConflicts.clear();
243     const SUMOTime brakingTime = TIME2STEPS(brakingTimeSeconds);
244     const SUMOTime leftTurnTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.left-green.time"));
245     const SUMOTime minMinDur = myType == TLTYPE_STATIC ? UNSPECIFIED_DURATION : TIME2STEPS(OptionsCont::getOptions().getInt("tls.min-dur"));
246     const SUMOTime maxDur = myType == TLTYPE_STATIC ? UNSPECIFIED_DURATION : TIME2STEPS(OptionsCont::getOptions().getInt("tls.max-dur"));
247 
248     // build complete lists first
249     const EdgeVector& incoming = getIncomingEdges();
250     EdgeVector fromEdges, toEdges;
251     std::vector<bool> isTurnaround;
252     std::vector<bool> hasTurnLane;
253     std::vector<int> fromLanes;
254     std::vector<int> toLanes;
255     int noLinksAll = 0;
256     for (NBEdge* const fromEdge : incoming) {
257         const int numLanes = fromEdge->getNumLanes();
258         for (int i2 = 0; i2 < numLanes; i2++) {
259             bool hasLeft = false;
260             bool hasStraight = false;
261             bool hasRight = false;
262             bool hasTurnaround = false;
263             for (const NBEdge::Connection& approached : fromEdge->getConnectionsFromLane(i2)) {
264                 if (!fromEdge->mayBeTLSControlled(i2, approached.toEdge, approached.toLane)) {
265                     continue;
266                 }
267                 fromEdges.push_back(fromEdge);
268                 fromLanes.push_back(i2);
269                 toLanes.push_back(approached.toLane);
270                 toEdges.push_back(approached.toEdge);
271                 if (approached.toEdge != nullptr) {
272                     isTurnaround.push_back(fromEdge->isTurningDirectionAt(approached.toEdge));
273                 } else {
274                     isTurnaround.push_back(true);
275                 }
276                 LinkDirection dir = fromEdge->getToNode()->getDirection(fromEdge, approached.toEdge);
277                 if (dir == LINKDIR_STRAIGHT) {
278                     hasStraight = true;
279                 } else if (dir == LINKDIR_RIGHT || dir == LINKDIR_PARTRIGHT) {
280                     hasRight = true;
281                 } else if (dir == LINKDIR_LEFT || dir == LINKDIR_PARTLEFT) {
282                     hasLeft = true;
283                 } else if (dir == LINKDIR_TURN) {
284                     hasTurnaround = true;
285                 }
286                 noLinksAll++;
287             }
288             for (const NBEdge::Connection& approached : fromEdge->getConnectionsFromLane(i2)) {
289                 if (!fromEdge->mayBeTLSControlled(i2, approached.toEdge, approached.toLane)) {
290                     continue;
291                 }
292                 hasTurnLane.push_back(
293                     (hasLeft && !hasStraight && !hasRight)
294                     || (!hasLeft && !hasTurnaround && hasRight));
295             }
296             //std::cout << " from=" << fromEdge->getID() << "_" << i2 << " hasTurnLane=" << hasTurnLane.back() << " s=" << hasStraight << " l=" << hasLeft << " r=" << hasRight << " t=" << hasTurnaround << "\n";
297         }
298     }
299     // collect crossings
300     std::vector<NBNode::Crossing*> crossings;
301     for (NBNode* const node : myControlledNodes) {
302         const std::vector<NBNode::Crossing*>& c = node->getCrossings();
303         if (!onlyConts) {
304             // set tl indices for crossings
305             node->setCrossingTLIndices(getID(), noLinksAll);
306         }
307         copy(c.begin(), c.end(), std::back_inserter(crossings));
308         noLinksAll += (int)c.size();
309     }
310 
311     NBTrafficLightLogic* logic = new NBTrafficLightLogic(getID(), getProgramID(), noLinksAll, myOffset, myType);
312     EdgeVector toProc = getConnectedOuterEdges(incoming);
313     const SUMOTime greenTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.green.time"));
314     const SUMOTime allRedTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.allred.time"));
315     const double minorLeftSpeedThreshold = OptionsCont::getOptions().getFloat("tls.minor-left.max-speed");
316     // left-turn phases do not work well for joined tls, so we build incoming instead
317     const double groupOpposites = (OptionsCont::getOptions().getString("tls.layout") == "opposites"
318                                    && (myControlledNodes.size() <= 2 || corridorLike()));
319 
320     // build all phases
321     std::vector<int> greenPhases; // indices of green phases
322     std::vector<bool> hadGreenMajor(noLinksAll, false);
323     while (toProc.size() > 0) {
324         bool groupTram = false;
325         bool groupOther = false;
326         std::pair<NBEdge*, NBEdge*> chosen;
327         if (groupOpposites) {
328             if (incoming.size() == 2) {
329                 // if there are only 2 incoming edges we need to decide whether they are a crossing or a "continuation"
330                 // @node: this heuristic could be extended to also check the number of outgoing edges
331                 double angle = fabs(NBHelpers::relAngle(incoming[0]->getAngleAtNode(incoming[0]->getToNode()), incoming[1]->getAngleAtNode(incoming[1]->getToNode())));
332                 // angle would be 180 for straight opposing incoming edges
333                 if (angle < 135) {
334                     chosen = std::pair<NBEdge*, NBEdge*>(toProc[0], static_cast<NBEdge*>(nullptr));
335                     toProc.erase(toProc.begin());
336                 } else {
337                     chosen = getBestPair(toProc);
338                 }
339             } else {
340                 chosen = getBestPair(toProc);
341                 if (chosen.second == nullptr && chosen.first->getPermissions() == SVC_TRAM) {
342                     groupTram = true;
343                     for (auto it = toProc.begin(); it != toProc.end();) {
344                         if ((*it)->getPermissions() == SVC_TRAM) {
345                             it = toProc.erase(it);
346                         } else {
347                             it++;
348                         }
349                     }
350                 }
351             }
352         } else {
353             NBEdge* chosenEdge = toProc[0];
354             chosen = std::pair<NBEdge*, NBEdge*>(chosenEdge, static_cast<NBEdge*>(nullptr));
355             toProc.erase(toProc.begin());
356             SVCPermissions perms = chosenEdge->getPermissions();
357             if (perms == SVC_TRAM) {
358                 groupTram = true;
359             } else if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_DELIVERY)) == 0) {
360                 groupOther = true;
361             }
362             // group all edges with the same permissions into a single phase (later)
363             if (groupTram || groupOther) {
364                 for (auto it = toProc.begin(); it != toProc.end();) {
365                     if ((*it)->getPermissions() == perms) {
366                         it = toProc.erase(it);
367                     } else {
368                         it++;
369                     }
370                 }
371             }
372         }
373         int pos = 0;
374         std::string state((int) noLinksAll, 'r');
375 #ifdef DEBUG_PHASES
376         if (DEBUGCOND) {
377             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";
378         }
379 #endif
380         // plain straight movers
381         double maxSpeed = 0;
382         bool haveGreen = false;
383         for (const NBEdge* const fromEdge : incoming) {
384             const bool inChosen = fromEdge == chosen.first || fromEdge == chosen.second; //chosen.find(fromEdge)!=chosen.end();
385             const int numLanes = fromEdge->getNumLanes();
386             for (int i2 = 0; i2 < numLanes; i2++) {
387                 for (const NBEdge::Connection& approached : fromEdge->getConnectionsFromLane(i2)) {
388                     if (!fromEdge->mayBeTLSControlled(i2, approached.toEdge, approached.toLane)) {
389                         continue;
390                     }
391                     if (inChosen) {
392                         state[pos] = 'G';
393                         haveGreen = true;
394                         maxSpeed = MAX2(maxSpeed, fromEdge->getSpeed());
395                     } else {
396                         state[pos] = 'r';
397                     }
398                     ++pos;
399                 }
400             }
401         }
402         if (!haveGreen) {
403             continue;
404         }
405 
406 #ifdef DEBUG_PHASES
407         if (DEBUGCOND) {
408             std::cout << " state after plain straight movers " << state << "\n";
409         }
410 #endif
411         // correct behaviour for those that are not in chosen, but may drive, though
412         state = allowCompatible(state, fromEdges, toEdges, fromLanes, toLanes);
413         if (groupTram) {
414             state = allowByVClass(state, fromEdges, toEdges, SVC_TRAM);
415         } else if (groupOther) {
416             state = allowByVClass(state, fromEdges, toEdges, SVC_PEDESTRIAN | SVC_BICYCLE | SVC_DELIVERY);
417         }
418 #ifdef DEBUG_PHASES
419         if (DEBUGCOND) {
420             std::cout << " state after grouping by vClass " << state << "\n";
421         }
422 #endif
423         if (groupOpposites || chosen.first->getToNode()->getType() == NODETYPE_TRAFFIC_LIGHT_RIGHT_ON_RED) {
424             state = allowUnrelated(state, fromEdges, toEdges, isTurnaround, crossings);
425         }
426 #ifdef DEBUG_PHASES
427         if (DEBUGCOND) {
428             std::cout << " state after finding allowUnrelated " << state << "\n";
429         }
430 #endif
431         // correct behaviour for those that have to wait (mainly left-mover)
432         bool haveForbiddenLeftMover = false;
433         std::vector<bool> rightTurnConflicts(pos, false);
434         state = correctConflicting(state, fromEdges, toEdges, isTurnaround, fromLanes, hadGreenMajor, haveForbiddenLeftMover, rightTurnConflicts);
435         for (int i1 = 0; i1 < pos; ++i1) {
436             if (state[i1] == 'G') {
437                 hadGreenMajor[i1] = true;
438             }
439         }
440 #ifdef DEBUG_PHASES
441         if (DEBUGCOND) {
442             std::cout << " state after correcting left movers=" << state << "\n";
443         }
444 #endif
445 
446         std::vector<bool> leftGreen(pos, false);
447         // check whether at least one left-turn lane exist
448         bool foundLeftTurnLane = false;
449         for (int i1 = 0; i1 < pos; ++i1) {
450             if (state[i1] == 'g' && !rightTurnConflicts[i1] && hasTurnLane[i1]) {
451                 foundLeftTurnLane = true;
452             }
453         }
454         const bool buildLeftGreenPhase = (haveForbiddenLeftMover && !myHaveSinglePhase && leftTurnTime > 0 && foundLeftTurnLane
455                                           && groupOpposites && !groupTram && !groupOther);
456 
457         // find indices for exclusive left green phase and apply option minor-left.max-speed
458         for (int i1 = 0; i1 < pos; ++i1) {
459             if (state[i1] == 'g' && !rightTurnConflicts[i1]
460                     // only activate turn-around together with a real left-turn
461                     && (!isTurnaround[i1] || (i1 > 0 && leftGreen[i1 - 1]))) {
462                 leftGreen[i1] = true;
463                 if (fromEdges[i1]->getSpeed() > minorLeftSpeedThreshold) {
464                     if (buildLeftGreenPhase) {
465                         state[i1] = 'r';
466                         //std::cout << " disabling minorLeft " << i1 << " (speed=" << fromEdges[i1]->getSpeed() << " thresh=" << minorLeftSpeedThreshold << ")\n";
467                     } else if (!isTurnaround[i1]) {
468                         WRITE_WARNING("Minor green from edge '" + fromEdges[i1]->getID() + "' to edge '" + toEdges[i1]->getID() + "' exceeds "
469                                       + toString(minorLeftSpeedThreshold) + "m/s. Maybe a left-turn lane is missing.");
470                     }
471                 }
472             }
473         }
474 
475 #ifdef DEBUG_PHASES
476         if (DEBUGCOND) {
477             std::cout << getID() << " state=" << state << " buildLeft=" << buildLeftGreenPhase << " hFLM=" << haveForbiddenLeftMover << " turnLane=" << foundLeftTurnLane
478                       << "   \nrtC=" << toString(rightTurnConflicts)
479                       << "   \nhTL=" << toString(hasTurnLane)
480                       << "   \nlGr=" << toString(leftGreen)
481                       << "\n";
482         }
483 #endif
484 
485         const std::string vehicleState = state; // backup state before pedestrian modifications
486         greenPhases.push_back((int)logic->getPhases().size());
487 
488         // 5s at 50km/h, 10s at 80km/h, rounded to full seconds
489         const double minDurBySpeed = maxSpeed * 3.6 / 6 - 3.3;
490         SUMOTime minDur = MAX2(minMinDur, TIME2STEPS(floor(minDurBySpeed + 0.5)));
491         if (chosen.first->getPermissions() == SVC_TRAM && (chosen.second == nullptr || chosen.second->getPermissions() == SVC_TRAM)) {
492             // shorter minDuration for tram phase (only if the phase is
493             // exclusively for tram)
494             bool tramExclusive = true;
495             for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
496                 if (state[i1] == 'G') {
497                     SVCPermissions linkPerm = (fromEdges[i1]->getPermissions() & toEdges[i1]->getPermissions());
498                     if (linkPerm != SVC_TRAM) {
499                         tramExclusive = false;
500                         break;
501                     }
502                 }
503             }
504             if (tramExclusive) {
505                 // one tram per actuated phase
506                 minDur = TIME2STEPS(1);
507             }
508         }
509 
510         state = addPedestrianPhases(logic, greenTime, minDur, maxDur, state, crossings, fromEdges, toEdges);
511         // pedestrians have 'r' from here on
512         for (int i1 = pos; i1 < pos + (int)crossings.size(); ++i1) {
513             state[i1] = 'r';
514         }
515         if (brakingTime > 0) {
516             // build yellow (straight)
517             for (int i1 = 0; i1 < pos; ++i1) {
518                 if (state[i1] != 'G' && state[i1] != 'g') {
519                     continue;
520                 }
521                 if ((vehicleState[i1] >= 'a' && vehicleState[i1] <= 'z')
522                         && buildLeftGreenPhase
523                         && !rightTurnConflicts[i1]
524                         && leftGreen[i1]) {
525                     continue;
526                 }
527                 state[i1] = 'y';
528             }
529             // add step
530             logic->addStep(brakingTime, state);
531             // add optional all-red state
532             buildAllRedState(allRedTime, logic, state);
533         }
534 
535 
536         if (buildLeftGreenPhase) {
537             // build left green
538             for (int i1 = 0; i1 < pos; ++i1) {
539                 if (state[i1] == 'Y' || state[i1] == 'y') {
540                     state[i1] = 'r';
541                     continue;
542                 }
543                 if (leftGreen[i1]) {
544                     state[i1] = 'G';
545                 }
546             }
547             state = allowCompatible(state, fromEdges, toEdges, fromLanes, toLanes);
548             state = correctConflicting(state, fromEdges, toEdges, isTurnaround, fromLanes, hadGreenMajor, haveForbiddenLeftMover, rightTurnConflicts);
549 
550             // add step
551             logic->addStep(leftTurnTime, state, minDur, maxDur);
552 
553             // build left yellow
554             if (brakingTime > 0) {
555                 for (int i1 = 0; i1 < pos; ++i1) {
556                     if (state[i1] != 'G' && state[i1] != 'g') {
557                         continue;
558                     }
559                     state[i1] = 'y';
560                 }
561                 // add step
562                 logic->addStep(brakingTime, state);
563                 // add optional all-red state
564                 buildAllRedState(allRedTime, logic, state);
565             }
566         }
567     }
568     // fix pedestrian crossings that did not get the green light yet
569     if (crossings.size() > 0) {
570         addPedestrianScramble(logic, noLinksAll, TIME2STEPS(10), brakingTime, crossings, fromEdges, toEdges);
571     }
572     // add optional red phase if there where no foes
573     if (logic->getPhases().size() == 2 && brakingTime > 0
574             && OptionsCont::getOptions().getInt("tls.red.time") > 0) {
575         const SUMOTime redTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.red.time"));
576         logic->addStep(redTime, std::string(noLinksAll, 'r'));
577     }
578     // fix states to account for custom crossing link indices
579     if (crossings.size() > 0 && !onlyConts) {
580         checkCustomCrossingIndices(logic);
581     }
582 
583     SUMOTime totalDuration = logic->getDuration();
584     if (OptionsCont::getOptions().isDefault("tls.green.time") || !OptionsCont::getOptions().isDefault("tls.cycle.time")) {
585         const SUMOTime cycleTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.cycle.time"));
586         // adapt to cycle time by changing the duration of the green phases
587         SUMOTime greenPhaseTime = 0;
588         SUMOTime minGreenDuration = SUMOTime_MAX;
589         for (std::vector<int>::const_iterator it = greenPhases.begin(); it != greenPhases.end(); ++it) {
590             const SUMOTime dur = logic->getPhases()[*it].duration;
591             greenPhaseTime += dur;
592             minGreenDuration = MIN2(minGreenDuration, dur);
593         }
594         const int patchSeconds = (int)(STEPS2TIME(cycleTime - totalDuration) / greenPhases.size());
595         const int patchSecondsRest = (int)(STEPS2TIME(cycleTime - totalDuration)) - patchSeconds * (int)greenPhases.size();
596         //std::cout << "cT=" << cycleTime << " td=" << totalDuration << " pS=" << patchSeconds << " pSR=" << patchSecondsRest << "\n";
597         if (STEPS2TIME(minGreenDuration) + patchSeconds < MIN_GREEN_TIME
598                 || STEPS2TIME(minGreenDuration) + patchSeconds + patchSecondsRest < MIN_GREEN_TIME
599                 || greenPhases.size() == 0) {
600             if (getID() != DummyID) {
601                 WRITE_WARNING("The traffic light '" + getID() + "' cannot be adapted to a cycle time of " + time2string(cycleTime) + ".");
602             }
603             // @todo use a multiple of cycleTime ?
604         } else {
605             for (std::vector<int>::const_iterator it = greenPhases.begin(); it != greenPhases.end(); ++it) {
606                 logic->setPhaseDuration(*it, logic->getPhases()[*it].duration + TIME2STEPS(patchSeconds));
607             }
608             if (greenPhases.size() > 0) {
609                 logic->setPhaseDuration(greenPhases.front(), logic->getPhases()[greenPhases.front()].duration + TIME2STEPS(patchSecondsRest));
610             }
611             totalDuration = logic->getDuration();
612         }
613     }
614 
615     myRightOnRedConflictsReady = true;
616     // this computation only makes sense for single nodes
617     myNeedsContRelationReady = (myControlledNodes.size() == 1);
618     if (totalDuration > 0) {
619         if (totalDuration > 3 * (greenTime + 2 * brakingTime + leftTurnTime)) {
620             WRITE_WARNING("The traffic light '" + getID() + "' has a high cycle time of " + time2string(totalDuration) + ".");
621         }
622         logic->closeBuilding();
623         return logic;
624     } else {
625         delete logic;
626         return nullptr;
627     }
628 }
629 
630 
631 bool
hasCrossing(const NBEdge * from,const NBEdge * to,const std::vector<NBNode::Crossing * > & crossings)632 NBOwnTLDef::hasCrossing(const NBEdge* from, const NBEdge* to, const std::vector<NBNode::Crossing*>& crossings) {
633     assert(to != 0);
634     for (auto c : crossings) {
635         const NBNode::Crossing& cross = *c;
636         // only check connections at this crossings node
637         if (to->getFromNode() == cross.node) {
638             for (EdgeVector::const_iterator it_e = cross.edges.begin(); it_e != cross.edges.end(); ++it_e) {
639                 const NBEdge* edge = *it_e;
640                 if (edge == from || edge == to) {
641                     return true;
642                 }
643             }
644         }
645     }
646     return false;
647 }
648 
649 
650 std::string
addPedestrianPhases(NBTrafficLightLogic * logic,SUMOTime greenTime,SUMOTime minDur,SUMOTime maxDur,std::string state,const std::vector<NBNode::Crossing * > & crossings,const EdgeVector & fromEdges,const EdgeVector & toEdges)651 NBOwnTLDef::addPedestrianPhases(NBTrafficLightLogic* logic, SUMOTime greenTime,
652                                 SUMOTime minDur, SUMOTime maxDur,
653                                 std::string state, const std::vector<NBNode::Crossing*>& crossings, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
654     // compute based on length of the crossing if not set by the user
655     const SUMOTime pedClearingTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.crossing-clearance.time"));
656     // compute if not set by user: must be able to reach the middle of the second "Richtungsfahrbahn"
657     const SUMOTime minPedTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.crossing-min.time"));
658     const std::string orig = state;
659     state = patchStateForCrossings(state, crossings, fromEdges, toEdges);
660     if (orig == state) {
661         // add step
662         logic->addStep(greenTime, state, minDur, maxDur);
663     } else {
664         const SUMOTime pedTime = greenTime - pedClearingTime;
665         if (pedTime >= minPedTime) {
666             // ensure clearing time for pedestrians
667             const int pedStates = (int)crossings.size();
668             logic->addStep(pedTime, state, minDur, maxDur);
669             state = state.substr(0, state.size() - pedStates) + std::string(pedStates, 'r');
670             logic->addStep(pedClearingTime, state);
671         } else {
672             state = orig;
673             // not safe for pedestrians.
674             logic->addStep(greenTime, state, minDur, maxDur);
675         }
676     }
677     return state;
678 }
679 
680 
681 std::string
patchStateForCrossings(const std::string & state,const std::vector<NBNode::Crossing * > & crossings,const EdgeVector & fromEdges,const EdgeVector & toEdges)682 NBOwnTLDef::patchStateForCrossings(const std::string& state, const std::vector<NBNode::Crossing*>& crossings, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
683     std::string result = state;
684     const int pos = (int)(state.size() - crossings.size()); // number of controlled vehicle links
685     for (int ic = 0; ic < (int)crossings.size(); ++ic) {
686         const int i1 = pos + ic;
687         const NBNode::Crossing& cross = *crossings[ic];
688         bool isForbidden = false;
689         for (int i2 = 0; i2 < pos && !isForbidden; ++i2) {
690             // only check connections at this crossings node
691             if (fromEdges[i2] != 0 && toEdges[i2] != 0 && fromEdges[i2]->getToNode() == cross.node) {
692                 for (EdgeVector::const_iterator it = cross.edges.begin(); it != cross.edges.end(); ++it) {
693                     const NBEdge* edge = *it;
694                     const LinkDirection i2dir = cross.node->getDirection(fromEdges[i2], toEdges[i2]);
695                     if (state[i2] != 'r' && state[i2] != 's' && (edge == fromEdges[i2] ||
696                             (edge == toEdges[i2] && (i2dir == LINKDIR_STRAIGHT || i2dir == LINKDIR_PARTLEFT || i2dir == LINKDIR_PARTRIGHT)))) {
697                         isForbidden = true;
698                         break;
699                     }
700                 }
701             }
702         }
703         if (!isForbidden) {
704             result[i1] = 'G';
705         } else {
706             result[i1] = 'r';
707         }
708     }
709 
710     // correct behaviour for roads that are in conflict with a pedestrian crossing
711     for (int i1 = 0; i1 < pos; ++i1) {
712         if (result[i1] == 'G') {
713             for (int ic = 0; ic < (int)crossings.size(); ++ic) {
714                 const NBNode::Crossing& crossing = *crossings[ic];
715                 if (fromEdges[i1] != 0 && toEdges[i1] != 0 && fromEdges[i1]->getToNode() == crossing.node) {
716                     const int i2 = pos + ic;
717                     if (result[i2] == 'G' && crossing.node->mustBrakeForCrossing(fromEdges[i1], toEdges[i1], crossing)) {
718                         result[i1] = 'g';
719                         break;
720                     }
721                 }
722             }
723         }
724     }
725     return result;
726 }
727 
728 
729 void
collectLinks()730 NBOwnTLDef::collectLinks() {
731     collectAllLinks();
732 }
733 
734 
735 void
setTLControllingInformation() const736 NBOwnTLDef::setTLControllingInformation() const {
737     // set the information about the link's positions within the tl into the
738     //  edges the links are starting at, respectively
739     for (NBConnectionVector::const_iterator j = myControlledLinks.begin(); j != myControlledLinks.end(); ++j) {
740         const NBConnection& conn = *j;
741         NBEdge* edge = conn.getFrom();
742         edge->setControllingTLInformation(conn, getID());
743     }
744 }
745 
746 
747 void
remapRemoved(NBEdge *,const EdgeVector &,const EdgeVector &)748 NBOwnTLDef::remapRemoved(NBEdge* /*removed*/, const EdgeVector& /*incoming*/,
749                          const EdgeVector& /*outgoing*/) {}
750 
751 
752 void
replaceRemoved(NBEdge *,int,NBEdge *,int)753 NBOwnTLDef::replaceRemoved(NBEdge* /*removed*/, int /*removedLane*/,
754                            NBEdge* /*by*/, int /*byLane*/) {}
755 
756 
757 void
initNeedsContRelation() const758 NBOwnTLDef::initNeedsContRelation() const {
759     if (!myNeedsContRelationReady) {
760         if (myControlledNodes.size() > 0) {
761             // we use a dummy node just to maintain const-correctness
762             myNeedsContRelation.clear();
763             NBOwnTLDef dummy(DummyID, myControlledNodes, 0, TLTYPE_STATIC);
764             dummy.setParticipantsInformation();
765             NBTrafficLightLogic* tllDummy = dummy.computeLogicAndConts(0, true);
766             delete tllDummy;
767             myNeedsContRelation = dummy.myNeedsContRelation;
768             for (std::vector<NBNode*>::const_iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
769                 (*i)->removeTrafficLight(&dummy);
770             }
771         }
772         myNeedsContRelationReady = true;
773     }
774 }
775 
776 
777 EdgeVector
getConnectedOuterEdges(const EdgeVector & incoming)778 NBOwnTLDef::getConnectedOuterEdges(const EdgeVector& incoming) {
779     EdgeVector result = incoming;
780     for (EdgeVector::iterator it = result.begin(); it != result.end();) {
781         if ((*it)->getConnections().size() == 0 || (*it)->isInternal()) {
782             it = result.erase(it);
783         } else {
784             ++it;
785         }
786     }
787     return result;
788 }
789 
790 
791 std::string
allowCompatible(std::string state,const EdgeVector & fromEdges,const EdgeVector & toEdges,const std::vector<int> & fromLanes,const std::vector<int> & toLanes)792 NBOwnTLDef::allowCompatible(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
793                             const std::vector<int>& fromLanes, const std::vector<int>& toLanes) {
794     state = allowSingleEdge(state, fromEdges);
795     state = allowFollowers(state, fromEdges, toEdges);
796     state = allowPredecessors(state, fromEdges, toEdges, fromLanes, toLanes);
797     return state;
798 }
799 
800 
801 std::string
allowSingleEdge(std::string state,const EdgeVector & fromEdges)802 NBOwnTLDef::allowSingleEdge(std::string state, const EdgeVector& fromEdges) {
803     // if only one edge has green, ensure sure that all connections from that edge are green
804     const int size = (int)fromEdges.size();
805     NBEdge* greenEdge = nullptr;
806     for (int i1 = 0; i1 < size; ++i1) {
807         if (state[i1] == 'G') {
808             if (greenEdge == nullptr) {
809                 greenEdge = fromEdges[i1];
810             } else if (greenEdge != fromEdges[i1]) {
811                 return state;
812             }
813         }
814     }
815     if (greenEdge != nullptr) {
816         for (int i1 = 0; i1 < size; ++i1) {
817             if (fromEdges[i1] == greenEdge) {
818                 state[i1] = 'G';
819             }
820         }
821     }
822     return state;
823 }
824 
825 
826 std::string
allowFollowers(std::string state,const EdgeVector & fromEdges,const EdgeVector & toEdges)827 NBOwnTLDef::allowFollowers(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
828     // check continuation within joined traffic lights
829     bool check = true;
830     while (check) {
831         check = false;
832         for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
833             if (state[i1] == 'G') {
834                 continue;
835             }
836             //if (forbidden(state, i1, fromEdges, toEdges)) {
837             //    continue;
838             //}
839             bool followsChosen = false;
840             for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
841                 if (state[i2] == 'G' && fromEdges[i1] == toEdges[i2]) {
842                     followsChosen = true;
843                     break;
844                 }
845             }
846             if (followsChosen) {
847                 state[i1] = 'G';
848                 check = true;
849             }
850         }
851     }
852     return state;
853 }
854 
855 
856 std::string
allowPredecessors(std::string state,const EdgeVector & fromEdges,const EdgeVector & toEdges,const std::vector<int> & fromLanes,const std::vector<int> & toLanes)857 NBOwnTLDef::allowPredecessors(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
858                               const std::vector<int>& fromLanes, const std::vector<int>& toLanes) {
859     // also allow predecessors of chosen edges if the lanes match and there is no conflict
860     // (must be done after the followers are done because followers are less specific)
861     bool check = true;
862     while (check) {
863         check = false;
864         for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
865             if (state[i1] == 'G') {
866                 continue;
867             }
868             if (forbidden(state, i1, fromEdges, toEdges)) {
869                 continue;
870             }
871             bool preceedsChosen = false;
872             for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
873                 if (state[i2] == 'G' && fromEdges[i2] == toEdges[i1]
874                         && fromLanes[i2] == toLanes[i1]) {
875                     preceedsChosen = true;
876                     break;
877                 }
878             }
879             if (preceedsChosen) {
880                 state[i1] = 'G';
881                 check = true;
882             }
883         }
884     }
885     return state;
886 }
887 
888 
889 std::string
allowUnrelated(std::string state,const EdgeVector & fromEdges,const EdgeVector & toEdges,const std::vector<bool> & isTurnaround,const std::vector<NBNode::Crossing * > & crossings)890 NBOwnTLDef::allowUnrelated(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
891                            const std::vector<bool>& isTurnaround,
892                            const std::vector<NBNode::Crossing*>& crossings) {
893     for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
894         if (state[i1] == 'G') {
895             continue;
896         }
897         bool isForbidden = false;
898         for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
899             if (state[i2] == 'G' && !isTurnaround[i2] &&
900                     (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true) || forbids(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2], true))) {
901                 isForbidden = true;
902                 break;
903             }
904         }
905         if (!isForbidden && !hasCrossing(fromEdges[i1], toEdges[i1], crossings)) {
906             state[i1] = 'G';
907         }
908     }
909     return state;
910 }
911 
912 
913 std::string
allowByVClass(std::string state,const EdgeVector & fromEdges,const EdgeVector & toEdges,SVCPermissions perm)914 NBOwnTLDef::allowByVClass(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges, SVCPermissions perm) {
915     for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
916         SVCPermissions linkPerm = (fromEdges[i1]->getPermissions() & toEdges[i1]->getPermissions());
917         if ((linkPerm & ~perm) == 0) {
918             state[i1] = 'G';
919         }
920     }
921     return state;
922 }
923 
924 
925 bool
forbidden(const std::string & state,int index,const EdgeVector & fromEdges,const EdgeVector & toEdges)926 NBOwnTLDef::forbidden(const std::string& state, int index, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
927     for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
928         if (state[i2] == 'G' && foes(fromEdges[i2], toEdges[i2], fromEdges[index], toEdges[index])) {
929             return true;
930         }
931     }
932     return false;
933 }
934 
935 
936 std::string
correctConflicting(std::string state,const EdgeVector & fromEdges,const EdgeVector & toEdges,const std::vector<bool> & isTurnaround,const std::vector<int> & fromLanes,const std::vector<bool> & hadGreenMajor,bool & haveForbiddenLeftMover,std::vector<bool> & rightTurnConflicts)937 NBOwnTLDef::correctConflicting(std::string state, const EdgeVector& fromEdges, const EdgeVector& toEdges,
938                                const std::vector<bool>& isTurnaround,
939                                const std::vector<int>& fromLanes,
940                                const std::vector<bool>& hadGreenMajor,
941                                bool& haveForbiddenLeftMover,
942                                std::vector<bool>& rightTurnConflicts) {
943     const bool controlledWithin = !OptionsCont::getOptions().getBool("tls.uncontrolled-within");
944     for (int i1 = 0; i1 < (int)fromEdges.size(); ++i1) {
945         if (state[i1] == 'G') {
946             for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
947                 if ((state[i2] == 'G' || state[i2] == 'g')) {
948                     if (NBNode::rightTurnConflict(
949                                 fromEdges[i1], toEdges[i1], fromLanes[i1], fromEdges[i2], toEdges[i2], fromLanes[i2])) {
950                         rightTurnConflicts[i1] = true;
951                     }
952                     if (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true, controlledWithin) || rightTurnConflicts[i1]) {
953                         state[i1] = 'g';
954                         myNeedsContRelation.insert(StreamPair(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2]));
955                         if (!isTurnaround[i1] && !hadGreenMajor[i1] && !rightTurnConflicts[i1]) {
956                             haveForbiddenLeftMover = true;
957                         }
958                     }
959                 }
960             }
961         }
962         if (state[i1] == 'r') {
963             if (fromEdges[i1]->getToNode()->getType() == NODETYPE_TRAFFIC_LIGHT_RIGHT_ON_RED &&
964                     fromEdges[i1]->getToNode()->getDirection(fromEdges[i1], toEdges[i1]) == LINKDIR_RIGHT) {
965                 state[i1] = 's';
966                 // do not allow right-on-red when in conflict with exclusive left-turn phase
967                 for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
968                     if (state[i2] == 'G' && !isTurnaround[i2] &&
969                             (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true) ||
970                              forbids(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2], true))) {
971                         const LinkDirection foeDir = fromEdges[i2]->getToNode()->getDirection(fromEdges[i2], toEdges[i2]);
972                         if (foeDir == LINKDIR_LEFT || foeDir == LINKDIR_PARTLEFT) {
973                             state[i1] = 'r';
974                             break;
975                         }
976                     }
977                 }
978                 if (state[i1] == 's') {
979                     // handle right-on-red conflicts
980                     for (int i2 = 0; i2 < (int)fromEdges.size(); ++i2) {
981                         if (state[i2] == 'G' && !isTurnaround[i2] &&
982                                 (forbids(fromEdges[i2], toEdges[i2], fromEdges[i1], toEdges[i1], true) ||
983                                  forbids(fromEdges[i1], toEdges[i1], fromEdges[i2], toEdges[i2], true))) {
984                             myRightOnRedConflicts.insert(std::make_pair(i1, i2));
985                         }
986                     }
987                 }
988             }
989         }
990     }
991     return state;
992 }
993 
994 
995 void
addPedestrianScramble(NBTrafficLightLogic * logic,int noLinksAll,SUMOTime,SUMOTime brakingTime,const std::vector<NBNode::Crossing * > & crossings,const EdgeVector & fromEdges,const EdgeVector & toEdges)996 NBOwnTLDef::addPedestrianScramble(NBTrafficLightLogic* logic, int noLinksAll, SUMOTime /* greenTime */, SUMOTime brakingTime,
997                                   const std::vector<NBNode::Crossing*>& crossings, const EdgeVector& fromEdges, const EdgeVector& toEdges) {
998     const int vehLinks = noLinksAll - (int)crossings.size();
999     std::vector<bool> foundGreen(crossings.size(), false);
1000     const std::vector<NBTrafficLightLogic::PhaseDefinition>& phases = logic->getPhases();
1001     for (int i = 0; i < (int)phases.size(); ++i) {
1002         const std::string state = phases[i].state;
1003         for (int j = 0; j < (int)crossings.size(); ++j) {
1004             LinkState ls = (LinkState)state[vehLinks + j];
1005             if (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR) {
1006                 foundGreen[j] = true;
1007             }
1008         }
1009     }
1010     for (int j = 0; j < (int)foundGreen.size(); ++j) {
1011         if (!foundGreen[j]) {
1012 
1013             // add a phase where all pedestrians may walk, (preceded by a yellow phase and followed by a clearing phase)
1014             if (phases.size() > 0) {
1015                 bool needYellowPhase = false;
1016                 std::string state = phases.back().state;
1017                 for (int i1 = 0; i1 < vehLinks; ++i1) {
1018                     if (state[i1] == 'G' || state[i1] == 'g') {
1019                         state[i1] = 'y';
1020                         needYellowPhase = true;
1021                     }
1022                 }
1023                 // add yellow step
1024                 if (needYellowPhase && brakingTime > 0) {
1025                     logic->addStep(brakingTime, state);
1026                 }
1027             }
1028             const SUMOTime pedClearingTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.crossing-clearance.time"));
1029             const SUMOTime scrambleTime = TIME2STEPS(OptionsCont::getOptions().getInt("tls.scramble.time"));
1030             addPedestrianPhases(logic, scrambleTime + pedClearingTime, UNSPECIFIED_DURATION, UNSPECIFIED_DURATION, std::string(noLinksAll, 'r'), crossings, fromEdges, toEdges);
1031             break;
1032         }
1033     }
1034 }
1035 
1036 
1037 void
buildAllRedState(SUMOTime allRedTime,NBTrafficLightLogic * logic,const std::string & state)1038 NBOwnTLDef::buildAllRedState(SUMOTime allRedTime, NBTrafficLightLogic* logic, const std::string& state) {
1039     if (allRedTime > 0) {
1040         // build all-red phase
1041         std::string allRedState = state;
1042         for (int i1 = 0; i1 < (int)state.size(); ++i1) {
1043             if (allRedState[i1] == 'Y' || allRedState[i1] == 'y') {
1044                 allRedState[i1] = 'r';
1045             }
1046         }
1047         logic->addStep(allRedTime, allRedState);
1048     }
1049 }
1050 
1051 void
checkCustomCrossingIndices(NBTrafficLightLogic * logic) const1052 NBOwnTLDef::checkCustomCrossingIndices(NBTrafficLightLogic* logic) const {
1053     int minCustomIndex = -1;
1054     int maxCustomIndex = -1;
1055     // collect crossings
1056     for (std::vector<NBNode*>::const_iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
1057         const std::vector<NBNode::Crossing*>& c = (*i)->getCrossings();
1058         for (auto crossing : c) {
1059             minCustomIndex = MIN2(minCustomIndex, crossing->customTLIndex);
1060             minCustomIndex = MIN2(minCustomIndex, crossing->customTLIndex2);
1061             maxCustomIndex = MAX2(maxCustomIndex, crossing->customTLIndex);
1062             maxCustomIndex = MAX2(maxCustomIndex, crossing->customTLIndex2);
1063         }
1064     }
1065     // custom crossing linkIndex could lead to longer states. ensure that every index has a state
1066     if (maxCustomIndex >= logic->getNumLinks()) {
1067         logic->setStateLength(maxCustomIndex + 1);
1068     }
1069     // XXX shorter state vectors are possible as well
1070     // XXX if the indices are shuffled the guessed crossing states should be shuffled correspondingly
1071     // XXX initialize the backward index to the same state as the forward index
1072 }
1073 
1074 
1075 int
getMaxIndex()1076 NBOwnTLDef::getMaxIndex() {
1077     setParticipantsInformation();
1078     NBTrafficLightLogic* logic = compute(OptionsCont::getOptions());
1079     if (logic != nullptr) {
1080         return logic->getNumLinks() - 1;
1081     } else {
1082         return -1;
1083     }
1084 }
1085 
1086 
1087 bool
corridorLike() const1088 NBOwnTLDef::corridorLike() const {
1089     if (getID() == DummyID) {
1090         // avoid infinite recursion
1091         return true;
1092     }
1093     assert(myControlledNodes.size() >= 2);
1094     NBOwnTLDef dummy(DummyID, myControlledNodes, 0, TLTYPE_STATIC);
1095     dummy.setParticipantsInformation();
1096     NBTrafficLightLogic* tllDummy = dummy.computeLogicAndConts(0, true);
1097     int greenPhases = 0;
1098     for (const auto& phase : tllDummy->getPhases()) {
1099         if (phase.state.find_first_of("gG") != std::string::npos) {
1100             greenPhases++;
1101         }
1102     }
1103     delete tllDummy;
1104     for (std::vector<NBNode*>::const_iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
1105         (*i)->removeTrafficLight(&dummy);
1106     }
1107     return greenPhases <= 2;
1108 }
1109 
1110 /****************************************************************************/
1111