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    NBTrafficLightDefinition.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Michael Behrisch
14 /// @date    Sept 2002
15 /// @version $Id$
16 ///
17 // The base class for traffic light logic definitions
18 /****************************************************************************/
19 
20 
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25 
26 #include <vector>
27 #include <string>
28 #include <algorithm>
29 #include <cassert>
30 #include <iterator>
31 #include <utils/common/MsgHandler.h>
32 #include <utils/common/ToString.h>
33 #include "NBTrafficLightDefinition.h"
34 #include <utils/options/OptionsCont.h>
35 #include "NBLinkPossibilityMatrix.h"
36 #include "NBTrafficLightLogic.h"
37 #include "NBOwnTLDef.h"
38 #include "NBContHelper.h"
39 
40 //#define DEBUG_RIGHT_OF_WAY
41 #define DEBUGCOND true
42 
43 // ===========================================================================
44 // static members
45 // ===========================================================================
46 const std::string NBTrafficLightDefinition::DefaultProgramID = "0";
47 const std::string NBTrafficLightDefinition::DummyID = "dummy";
48 const SUMOTime NBTrafficLightDefinition::UNSPECIFIED_DURATION(-1);
49 
50 // ===========================================================================
51 // method definitions
52 // ===========================================================================
NBTrafficLightDefinition(const std::string & id,const std::vector<NBNode * > & junctions,const std::string & programID,SUMOTime offset,TrafficLightType type)53 NBTrafficLightDefinition::NBTrafficLightDefinition(const std::string& id,
54         const std::vector<NBNode*>& junctions, const std::string& programID,
55         SUMOTime offset, TrafficLightType type) :
56     Named(id),
57     myControlledNodes(junctions),
58     mySubID(programID), myOffset(offset),
59     myType(type),
60     myNeedsContRelationReady(false),
61     myRightOnRedConflictsReady(false) {
62     std::vector<NBNode*>::iterator i = myControlledNodes.begin();
63     while (i != myControlledNodes.end()) {
64         for (std::vector<NBNode*>::iterator j = i + 1; j != myControlledNodes.end();) {
65             if (*i == *j) {
66                 j = myControlledNodes.erase(j);
67             } else {
68                 j++;
69             }
70         }
71         i++;
72     }
73     std::sort(myControlledNodes.begin(), myControlledNodes.end(), NBNode::nodes_by_id_sorter());
74     for (std::vector<NBNode*>::const_iterator i = junctions.begin(); i != junctions.end(); i++) {
75         (*i)->addTrafficLight(this);
76     }
77 }
78 
79 
NBTrafficLightDefinition(const std::string & id,NBNode * junction,const std::string & programID,SUMOTime offset,TrafficLightType type)80 NBTrafficLightDefinition::NBTrafficLightDefinition(const std::string& id,
81         NBNode* junction, const std::string& programID, SUMOTime offset, TrafficLightType type) :
82     Named(id),
83     mySubID(programID),
84     myOffset(offset),
85     myType(type),
86     myNeedsContRelationReady(false),
87     myRightOnRedConflictsReady(false) {
88     addNode(junction);
89 }
90 
91 
NBTrafficLightDefinition(const std::string & id,const std::string & programID,SUMOTime offset,TrafficLightType type)92 NBTrafficLightDefinition::NBTrafficLightDefinition(const std::string& id, const std::string& programID,
93         SUMOTime offset, TrafficLightType type) :
94     Named(id),
95     mySubID(programID),
96     myOffset(offset),
97     myType(type),
98     myNeedsContRelationReady(false),
99     myRightOnRedConflictsReady(false) {
100 }
101 
102 
~NBTrafficLightDefinition()103 NBTrafficLightDefinition::~NBTrafficLightDefinition() {}
104 
105 
106 NBTrafficLightLogic*
compute(OptionsCont & oc)107 NBTrafficLightDefinition::compute(OptionsCont& oc) {
108     // it is not really a traffic light if no incoming edge exists
109     if (amInvalid()) {
110         // make a copy of myControlledNodes because it will be modified;
111         std::vector<NBNode*> nodes = myControlledNodes;
112         for (std::vector<NBNode*>::iterator it = nodes.begin(); it != nodes.end(); it++) {
113             (*it)->removeTrafficLight(this);
114         }
115         WRITE_WARNING("The traffic light '" + getID() + "' does not control any links; it will not be build.");
116         return nullptr;
117     }
118     // compute the time needed to brake
119     int brakingTime = computeBrakingTime(oc.getFloat("tls.yellow.min-decel"));
120     // perform the computation depending on whether the traffic light
121     //  definition was loaded or shall be computed new completely
122     if (!oc.isDefault("tls.yellow.time")) {
123         brakingTime = oc.getInt("tls.yellow.time");
124     }
125     NBTrafficLightLogic* ret = myCompute(brakingTime);
126     ret->updateParameter(getParametersMap());
127     return ret;
128 }
129 
130 
131 bool
amInvalid() const132 NBTrafficLightDefinition::amInvalid() const {
133     return myControlledLinks.size() == 0;
134 }
135 
136 
137 int
computeBrakingTime(double minDecel) const138 NBTrafficLightDefinition::computeBrakingTime(double minDecel) const {
139     if (myIncomingEdges.size() == 0) {
140         // don't crash
141         return 3;
142     }
143     double vmax = NBContHelper::maxSpeed(myIncomingEdges);
144     if (vmax < 71 / 3.6) {
145         // up to 50kmh: 3 seconds , 60km/h: 4, 70kmh: 5
146         // @note: these are German regulations, other countries may differ
147         return 3 + (int)MAX2(0.0, (floor((vmax - 50 / 3.6) * 0.37)));
148     } else {
149         // above 70km/h we use a function that grows according to the "natural"
150         // formula (vmax / 2 * minDecel) but continues smoothly where the german
151         // rules leave of
152         return (int)(1.8 + vmax / 2 / minDecel);
153     }
154 }
155 
156 
157 void
setParticipantsInformation()158 NBTrafficLightDefinition::setParticipantsInformation() {
159     // collect the information about participating edges and links
160     collectEdges();
161     collectLinks();
162 }
163 
164 std::set<NBEdge*>
collectReachable(EdgeVector outer,const EdgeVector & within,bool checkControlled)165 NBTrafficLightDefinition::collectReachable(EdgeVector outer, const EdgeVector& within, bool checkControlled) {
166     std::set<NBEdge*> reachable;
167     while (outer.size() > 0) {
168         NBEdge* from = outer.back();
169         outer.pop_back();
170         std::vector<NBEdge::Connection>& cons = from->getConnections();
171         for (std::vector<NBEdge::Connection>::iterator k = cons.begin(); k != cons.end(); k++) {
172             NBEdge* to = (*k).toEdge;
173             if (reachable.count(to) == 0 &&
174                     (find(within.begin(), within.end(), to) != within.end()) &&
175                     (!checkControlled || from->mayBeTLSControlled((*k).fromLane, to, (*k).toLane))) {
176                 reachable.insert(to);
177                 outer.push_back(to);
178             }
179         }
180     }
181     return reachable;
182 }
183 
184 
185 void
collectEdges()186 NBTrafficLightDefinition::collectEdges() {
187     myIncomingEdges.clear();
188     myEdgesWithin.clear();
189     EdgeVector myOutgoing;
190     // collect the edges from the participating nodes
191     for (std::vector<NBNode*>::iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
192         const EdgeVector& incoming = (*i)->getIncomingEdges();
193         copy(incoming.begin(), incoming.end(), back_inserter(myIncomingEdges));
194         const EdgeVector& outgoing = (*i)->getOutgoingEdges();
195         copy(outgoing.begin(), outgoing.end(), back_inserter(myOutgoing));
196     }
197     EdgeVector outer;
198     // check which of the edges are completely within the junction
199     //  add them to the list of edges lying within the node
200     for (EdgeVector::iterator j = myIncomingEdges.begin(); j != myIncomingEdges.end(); ++j) {
201         NBEdge* edge = *j;
202         // an edge lies within the logic if it is outgoing as well as incoming
203         EdgeVector::iterator k = std::find(myOutgoing.begin(), myOutgoing.end(), edge);
204         if (k != myOutgoing.end()) {
205             myEdgesWithin.push_back(edge);
206         } else  {
207             outer.push_back(edge);
208         }
209     }
210     // collect edges that are reachable from the outside via controlled connections
211     std::set<NBEdge*> reachable = collectReachable(outer, myEdgesWithin, true);
212     // collect edges that are reachable from the outside regardless of controllability
213     std::set<NBEdge*> reachable2 = collectReachable(outer, myEdgesWithin, false);
214 
215     const bool uncontrolledWithin = OptionsCont::getOptions().getBool("tls.uncontrolled-within");
216     for (EdgeVector::iterator j = myEdgesWithin.begin(); j != myEdgesWithin.end(); ++j) {
217         NBEdge* edge = *j;
218         // edges that are marked as 'inner' will not get their own phase when
219         // computing traffic light logics (unless they cannot be reached from the outside at all)
220         if (reachable.count(edge) == 1) {
221             edge->setInternal();
222             // legacy behavior
223             if (uncontrolledWithin && myControlledInnerEdges.count(edge->getID()) == 0) {
224                 myIncomingEdges.erase(find(myIncomingEdges.begin(), myIncomingEdges.end(), edge));
225             }
226         }
227         if (reachable2.count(edge) == 0 && edge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true) >= 0
228                 && getID() != DummyID) {
229             WRITE_WARNING("Unreachable edge '" + edge->getID() + "' within tlLogic '" + getID() + "'");
230         }
231     }
232 }
233 
234 
235 bool
mustBrake(const NBEdge * const from,const NBEdge * const to) const236 NBTrafficLightDefinition::mustBrake(const NBEdge* const from, const NBEdge* const to) const {
237     std::vector<NBNode*>::const_iterator i =
238         find_if(myControlledNodes.begin(), myControlledNodes.end(),
239                 NBContHelper::node_with_incoming_finder(from));
240     assert(i != myControlledNodes.end());
241     NBNode* node = *i;
242     if (!node->hasOutgoing(to)) {
243         return true; // !!!
244     }
245     // @todo recheck relevance of lane indices
246     return node->mustBrake(from, to, -1, -1, true);
247 }
248 
249 
250 bool
mustBrake(const NBEdge * const possProhibitedFrom,const NBEdge * const possProhibitedTo,const NBEdge * const possProhibitorFrom,const NBEdge * const possProhibitorTo,bool regardNonSignalisedLowerPriority) const251 NBTrafficLightDefinition::mustBrake(const NBEdge* const possProhibitedFrom,
252                                     const NBEdge* const possProhibitedTo,
253                                     const NBEdge* const possProhibitorFrom,
254                                     const NBEdge* const possProhibitorTo,
255                                     bool regardNonSignalisedLowerPriority) const {
256     return forbids(possProhibitorFrom, possProhibitorTo,
257                    possProhibitedFrom, possProhibitedTo,
258                    regardNonSignalisedLowerPriority);
259 }
260 
261 
262 bool
mustBrake(const NBConnection & possProhibited,const NBConnection & possProhibitor,bool regardNonSignalisedLowerPriority) const263 NBTrafficLightDefinition::mustBrake(const NBConnection& possProhibited,
264                                     const NBConnection& possProhibitor,
265                                     bool regardNonSignalisedLowerPriority) const {
266     return forbids(possProhibitor.getFrom(), possProhibitor.getTo(),
267                    possProhibited.getFrom(), possProhibited.getTo(),
268                    regardNonSignalisedLowerPriority);
269 }
270 
271 
272 bool
forbids(const NBEdge * const possProhibitorFrom,const NBEdge * const possProhibitorTo,const NBEdge * const possProhibitedFrom,const NBEdge * const possProhibitedTo,bool regardNonSignalisedLowerPriority,bool sameNodeOnly) const273 NBTrafficLightDefinition::forbids(const NBEdge* const possProhibitorFrom,
274                                   const NBEdge* const possProhibitorTo,
275                                   const NBEdge* const possProhibitedFrom,
276                                   const NBEdge* const possProhibitedTo,
277                                   bool regardNonSignalisedLowerPriority,
278                                   bool sameNodeOnly) const {
279     if (possProhibitorFrom == nullptr || possProhibitorTo == nullptr || possProhibitedFrom == nullptr || possProhibitedTo == nullptr) {
280         return false;
281     }
282     // retrieve both nodes
283     std::vector<NBNode*>::const_iterator incoming =
284         find_if(myControlledNodes.begin(), myControlledNodes.end(), NBContHelper::node_with_incoming_finder(possProhibitorFrom));
285     std::vector<NBNode*>::const_iterator outgoing =
286         find_if(myControlledNodes.begin(), myControlledNodes.end(), NBContHelper::node_with_outgoing_finder(possProhibitedTo));
287     assert(incoming != myControlledNodes.end());
288     NBNode* incnode = *incoming;
289     NBNode* outnode = *outgoing;
290     EdgeVector::const_iterator i;
291 
292 #ifdef DEBUG_RIGHT_OF_WAY
293     if (DEBUGCOND) {
294         std::cout << "foribds tls=" << getID() << " from=" << possProhibitedFrom->getID() << " to=" << possProhibitedTo->getID() << " foeFrom=" << possProhibitorFrom->getID() << " foeTo=" << possProhibitorTo->getID() << " rnslp=" << regardNonSignalisedLowerPriority << " sameNodeOnly=" << sameNodeOnly;
295     }
296 #endif
297     if (incnode != outnode) {
298         if (sameNodeOnly) {
299 #ifdef DEBUG_RIGHT_OF_WAY
300             if (DEBUGCOND) {
301                 std::cout << "   differentNodes: allows (no check)\n";
302             }
303 #endif
304             return false;
305         }
306         // the links are located at different nodes
307         const EdgeVector& ev1 = possProhibitedTo->getConnectedEdges();
308         // go through the following edge,
309         //  check whether one of these connections is prohibited
310         for (i = ev1.begin(); i != ev1.end(); ++i) {
311             std::vector<NBNode*>::const_iterator outgoing2 =
312                 find_if(myControlledNodes.begin(), myControlledNodes.end(), NBContHelper::node_with_outgoing_finder(*i));
313             if (outgoing2 == myControlledNodes.end()) {
314                 continue;
315             }
316             NBNode* outnode2 = *outgoing2;
317             if (incnode != outnode2) {
318                 continue;
319             }
320             if (incnode->getDirection(possProhibitedTo, *i) != LINKDIR_STRAIGHT) {
321                 continue;
322             }
323             bool ret1 = incnode->foes(possProhibitorFrom, possProhibitorTo,
324                                       possProhibitedTo, *i);
325             bool ret2 = incnode->forbids(possProhibitorFrom, possProhibitorTo,
326                                          possProhibitedTo, *i,
327                                          regardNonSignalisedLowerPriority);
328             bool ret = ret1 || ret2;
329             if (ret) {
330 #ifdef DEBUG_RIGHT_OF_WAY
331                 if (DEBUGCOND) {
332                     std::cout << "   differentNodes: forbids\n";
333                 }
334 #endif
335                 return true;
336             }
337         }
338 
339         const EdgeVector& ev2 = possProhibitorTo->getConnectedEdges();
340         // go through the following edge,
341         //  check whether one of these connections is prohibited
342         for (i = ev2.begin(); i != ev2.end(); ++i) {
343             std::vector<NBNode*>::const_iterator incoming2 =
344                 find_if(myControlledNodes.begin(), myControlledNodes.end(), NBContHelper::node_with_incoming_finder(possProhibitorTo));
345             if (incoming2 == myControlledNodes.end()) {
346                 continue;
347             }
348             NBNode* incnode2 = *incoming2;
349             if (incnode2 != outnode) {
350                 continue;
351             }
352             if (incnode2->getDirection(possProhibitorTo, *i) != LINKDIR_STRAIGHT) {
353                 continue;
354             }
355             bool ret1 = incnode2->foes(possProhibitorTo, *i,
356                                        possProhibitedFrom, possProhibitedTo);
357             bool ret2 = incnode2->forbids(possProhibitorTo, *i,
358                                           possProhibitedFrom, possProhibitedTo,
359                                           regardNonSignalisedLowerPriority);
360             bool ret = ret1 || ret2;
361             if (ret) {
362 #ifdef DEBUG_RIGHT_OF_WAY
363                 if (DEBUGCOND) {
364                     std::cout << "   differentNodes: forbids (2)\n";
365                 }
366 #endif
367                 return true;
368             }
369         }
370 #ifdef DEBUG_RIGHT_OF_WAY
371         if (DEBUGCOND) {
372             std::cout << "   differentNodes: allows\n";
373         }
374 #endif
375         return false;
376     }
377     // both links are located at the same node
378     //  check using this node's information
379     const bool result = incnode->forbids(possProhibitorFrom, possProhibitorTo,
380                                          possProhibitedFrom, possProhibitedTo,
381                                          regardNonSignalisedLowerPriority);
382 #ifdef DEBUG_RIGHT_OF_WAY
383     if (DEBUGCOND) {
384         std::cout << "   sameNodes: " << (result ? "forbids" : "allows") << "\n";
385     }
386 #endif
387     return result;
388 }
389 
390 
391 bool
foes(const NBEdge * const from1,const NBEdge * const to1,const NBEdge * const from2,const NBEdge * const to2) const392 NBTrafficLightDefinition::foes(const NBEdge* const from1, const NBEdge* const to1,
393                                const NBEdge* const from2, const NBEdge* const to2) const {
394     if (to1 == nullptr || to2 == nullptr) {
395         return false;
396     }
397     // retrieve both nodes (it is possible that a connection
398     std::vector<NBNode*>::const_iterator incoming =
399         find_if(myControlledNodes.begin(), myControlledNodes.end(),
400                 NBContHelper::node_with_incoming_finder(from1));
401     std::vector<NBNode*>::const_iterator outgoing =
402         find_if(myControlledNodes.begin(), myControlledNodes.end(),
403                 NBContHelper::node_with_outgoing_finder(to1));
404     assert(incoming != myControlledNodes.end());
405     NBNode* incnode = *incoming;
406     NBNode* outnode = *outgoing;
407     if (incnode != outnode) {
408         return false;
409     }
410     return incnode->foes(from1, to1, from2, to2);
411 }
412 
413 
414 void
addNode(NBNode * node)415 NBTrafficLightDefinition::addNode(NBNode* node) {
416     if (std::find(myControlledNodes.begin(), myControlledNodes.end(), node) == myControlledNodes.end()) {
417         myControlledNodes.push_back(node);
418         std::sort(myControlledNodes.begin(), myControlledNodes.end(), NBNode::nodes_by_id_sorter());
419     }
420     node->addTrafficLight(this);
421 }
422 
423 
424 void
removeNode(NBNode * node)425 NBTrafficLightDefinition::removeNode(NBNode* node) {
426     std::vector<NBNode*>::iterator i = std::find(myControlledNodes.begin(), myControlledNodes.end(), node);
427     if (i != myControlledNodes.end()) {
428         myControlledNodes.erase(i);
429     }
430     // !!! remove in node?
431 }
432 
433 
434 void
addControlledInnerEdges(const std::vector<std::string> & edges)435 NBTrafficLightDefinition::addControlledInnerEdges(const std::vector<std::string>& edges) {
436     myControlledInnerEdges.insert(edges.begin(), edges.end());
437 }
438 
439 
440 std::vector<std::string>
getControlledInnerEdges() const441 NBTrafficLightDefinition::getControlledInnerEdges() const {
442     return std::vector<std::string>(myControlledInnerEdges.begin(), myControlledInnerEdges.end());
443 }
444 
445 
446 const EdgeVector&
getIncomingEdges() const447 NBTrafficLightDefinition::getIncomingEdges() const {
448     return myIncomingEdges;
449 }
450 
451 
452 void
collectAllLinks()453 NBTrafficLightDefinition::collectAllLinks() {
454     myControlledLinks.clear();
455     int tlIndex = 0;
456     // build the list of links which are controled by the traffic light
457     for (EdgeVector::iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
458         NBEdge* incoming = *i;
459         int noLanes = incoming->getNumLanes();
460         for (int j = 0; j < noLanes; j++) {
461             std::vector<NBEdge::Connection> connected = incoming->getConnectionsFromLane(j);
462             for (std::vector<NBEdge::Connection>::iterator k = connected.begin(); k != connected.end(); k++) {
463                 const NBEdge::Connection& el = *k;
464                 if (incoming->mayBeTLSControlled(el.fromLane, el.toEdge, el.toLane)) {
465                     if (el.toEdge != nullptr && el.toLane >= (int) el.toEdge->getNumLanes()) {
466                         throw ProcessError("Connection '" + incoming->getID() + "_" + toString(j) + "->" + el.toEdge->getID() + "_" + toString(el.toLane) + "' yields in a not existing lane.");
467                     }
468                     if (incoming->getToNode()->getType() == NODETYPE_RAIL_CROSSING
469                             && isRailway(incoming->getPermissions())) {
470                         // railways stay uncontrolled at rail crossing but they
471                         // must be registered in MSRailCrossing
472                         myControlledLinks.push_back(NBConnection(incoming, el.fromLane, el.toEdge, el.toLane, -1));
473                     } else if (incoming->getToNode()->getType() == NODETYPE_RAIL_SIGNAL
474                                && incoming->getToNode()->getDirection(incoming, el.toEdge) == LINKDIR_TURN) {
475                         // turnarounds stay uncontrolled at rail signal
476                     } else {
477                         myControlledLinks.push_back(NBConnection(incoming, el.fromLane, el.toEdge, el.toLane, tlIndex++));
478                     }
479                 }
480             }
481         }
482     }
483     if (myControlledLinks.size() > 0 && tlIndex == 0) {
484         WRITE_WARNING("The rail crossing '" + getID() + "' does not have any roads.");
485     }
486 }
487 
488 
489 bool
needsCont(const NBEdge * fromE,const NBEdge * toE,const NBEdge * otherFromE,const NBEdge * otherToE) const490 NBTrafficLightDefinition::needsCont(const NBEdge* fromE, const NBEdge* toE, const NBEdge* otherFromE, const NBEdge* otherToE) const {
491     if (!myNeedsContRelationReady) {
492         initNeedsContRelation();
493         assert(myNeedsContRelationReady);
494     }
495     return std::find(myNeedsContRelation.begin(), myNeedsContRelation.end(),
496                      StreamPair(fromE, toE, otherFromE, otherToE)) != myNeedsContRelation.end();
497 }
498 
499 
500 void
initNeedsContRelation() const501 NBTrafficLightDefinition::initNeedsContRelation() const {
502     if (!amInvalid()) {
503         NBOwnTLDef dummy(DummyID, myControlledNodes, 0, TLTYPE_STATIC);
504         dummy.initNeedsContRelation();
505         myNeedsContRelation = dummy.myNeedsContRelation;
506         for (std::vector<NBNode*>::const_iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
507             (*i)->removeTrafficLight(&dummy);
508         }
509     }
510     myNeedsContRelationReady = true;
511 }
512 
513 
514 bool
rightOnRedConflict(int index,int foeIndex) const515 NBTrafficLightDefinition::rightOnRedConflict(int index, int foeIndex) const {
516     if (!myRightOnRedConflictsReady) {
517         NBOwnTLDef dummy(DummyID, myControlledNodes, 0, TLTYPE_STATIC);
518         dummy.setParticipantsInformation();
519         NBTrafficLightLogic* tllDummy = dummy.computeLogicAndConts(0, true);
520         delete tllDummy;
521         myRightOnRedConflicts = dummy.myRightOnRedConflicts;
522         for (std::vector<NBNode*>::const_iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
523             (*i)->removeTrafficLight(&dummy);
524         }
525         myRightOnRedConflictsReady = true;
526         //std::cout << " rightOnRedConflicts tls=" << getID() << " pro=" << getProgramID() << "\n";
527         //for (RightOnRedConflicts::const_iterator it = myRightOnRedConflicts.begin(); it != myRightOnRedConflicts.end(); ++it) {
528         //    std::cout << "   " << it->first << ", " << it->second << "\n";
529         //}
530     }
531     return std::find(myRightOnRedConflicts.begin(), myRightOnRedConflicts.end(), std::make_pair(index, foeIndex)) != myRightOnRedConflicts.end();
532 }
533 
534 std::string
getDescription() const535 NBTrafficLightDefinition::getDescription() const {
536     return getID() + ':' + getProgramID() + '@' + toString(this);
537 }
538 
539 /****************************************************************************/
540 
541