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    MSActuatedTrafficLightLogic.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Julia Ringel
13 /// @author  Jakob Erdmann
14 /// @author  Michael Behrisch
15 /// @author  Laura Bieker
16 /// @date    Sept 2002
17 /// @version $Id$
18 ///
19 // An actuated (adaptive) traffic light logic
20 /****************************************************************************/
21 
22 
23 // ===========================================================================
24 // included modules
25 // ===========================================================================
26 #include <config.h>
27 
28 #include <cassert>
29 #include <utility>
30 #include <vector>
31 #include <bitset>
32 #include <microsim/MSEventControl.h>
33 #include <microsim/output/MSDetectorControl.h>
34 #include <microsim/output/MSInductLoop.h>
35 #include <microsim/MSGlobals.h>
36 #include <microsim/MSNet.h>
37 #include "MSTrafficLightLogic.h"
38 #include "MSActuatedTrafficLightLogic.h"
39 #include <microsim/MSLane.h>
40 #include <microsim/MSEdge.h>
41 #include <netload/NLDetectorBuilder.h>
42 #include <utils/common/StringUtils.h>
43 
44 //#define DEBUG_DETECTORS
45 #define DEBUG_COND (getID()=="26121229")
46 
47 // ===========================================================================
48 // parameter defaults definitions
49 // ===========================================================================
50 #define DEFAULT_MAX_GAP "3.0"
51 #define DEFAULT_PASSING_TIME "1.9"
52 #define DEFAULT_DETECTOR_GAP "2.0"
53 
54 #define DEFAULT_LENGTH_WITH_GAP 7.5
55 
56 
57 // ===========================================================================
58 // method definitions
59 // ===========================================================================
MSActuatedTrafficLightLogic(MSTLLogicControl & tlcontrol,const std::string & id,const std::string & programID,const Phases & phases,int step,SUMOTime delay,const std::map<std::string,std::string> & parameter,const std::string & basePath)60 MSActuatedTrafficLightLogic::MSActuatedTrafficLightLogic(MSTLLogicControl& tlcontrol,
61         const std::string& id, const std::string& programID,
62         const Phases& phases,
63         int step, SUMOTime delay,
64         const std::map<std::string, std::string>& parameter,
65         const std::string& basePath) :
66     MSSimpleTrafficLightLogic(tlcontrol, id, programID, TLTYPE_ACTUATED, phases, step, delay, parameter) {
67 
68     myMaxGap = StringUtils::toDouble(getParameter("max-gap", DEFAULT_MAX_GAP));
69     myPassingTime = StringUtils::toDouble(getParameter("passing-time", DEFAULT_PASSING_TIME)); // passing-time seems obsolete... (Leo)
70     myDetectorGap = StringUtils::toDouble(getParameter("detector-gap", DEFAULT_DETECTOR_GAP));
71     myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
72     myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
73     myFreq = TIME2STEPS(StringUtils::toDouble(getParameter("freq", "300")));
74     myVehicleTypes = getParameter("vTypes", "");
75 }
76 
~MSActuatedTrafficLightLogic()77 MSActuatedTrafficLightLogic::~MSActuatedTrafficLightLogic() { }
78 
79 void
init(NLDetectorBuilder & nb)80 MSActuatedTrafficLightLogic::init(NLDetectorBuilder& nb) {
81     MSTrafficLightLogic::init(nb);
82     assert(myLanes.size() > 0);
83     bool warn = true; // warn only once
84     const int numLinks = (int)myLinks.size();
85 
86     // Detector position should be computed based on road speed. If the position
87     // is quite far away and the minDur is short this may cause the following
88     // problems:
89     //
90     // 1)  high flow failure:
91     // In a standing queue, no vehicle touches the detector.
92     // By the time the queue advances, the detector gap has been exceeded and the phase terminates prematurely
93     //
94     // 2) low flow failure
95     // The standing queue is fully between stop line and detector and there are no further vehicles.
96     // The minDur is too short to let all vehicles pass
97     //
98     // Problem 2) is not so critical because there is less potential for
99     // jamming in a low-flow situation. In contrast, problem 1) should be
100     // avoided as it has big jamming potential. We compute an upper bound for the
101     // detector disatnce to avoid it
102 
103 
104     // change values for setting the loops and lanestate-detectors, here
105     //SUMOTime inductLoopInterval = 1; //
106     LaneVectorVector::const_iterator i2;
107     LaneVector::const_iterator i;
108     // build the induct loops
109     std::map<const MSLane*, MSInductLoop*> laneInductLoopMap;
110     double maxDetectorGap = 0;
111     for (i2 = myLanes.begin(); i2 != myLanes.end(); ++i2) {
112         const LaneVector& lanes = *i2;
113         for (i = lanes.begin(); i != lanes.end(); i++) {
114             MSLane* lane = (*i);
115             if (noVehicles(lane->getPermissions())) {
116                 // do not build detectors on green verges or sidewalks
117                 continue;
118             }
119             if (laneInductLoopMap.find(lane) != laneInductLoopMap.end()) {
120                 // only build one detector per lane
121                 continue;
122             }
123             const SUMOTime minDur = getMinimumMinDuration(lane);
124             if (minDur == std::numeric_limits<SUMOTime>::max()) {
125                 // only build detector if this lane is relevant for an actuated phase
126                 continue;
127             }
128             double length = lane->getLength();
129             double speed = lane->getSpeedLimit();
130             double inductLoopPosition = MIN2(
131                                             myDetectorGap * speed,
132                                             MAX2(1.0, (STEPS2TIME(minDur) - 1)) * DEFAULT_LENGTH_WITH_GAP);
133 
134             // check whether the lane is long enough
135             double ilpos = length - inductLoopPosition;
136             if (ilpos < 0) {
137                 ilpos = 0;
138             }
139             // Build the induct loop and set it into the container
140             std::string id = "TLS" + myID + "_" + myProgramID + "_InductLoopOn_" + lane->getID();
141             MSInductLoop* loop = static_cast<MSInductLoop*>(nb.createInductLoop(id, lane, ilpos, myVehicleTypes, myShowDetectors));
142             laneInductLoopMap[lane] = loop;
143             myInductLoops.push_back(loop);
144             MSNet::getInstance()->getDetectorControl().add(SUMO_TAG_INDUCTION_LOOP, loop, myFile, myFreq);
145             maxDetectorGap = MAX2(maxDetectorGap, length - ilpos);
146 
147             if (warn && floor(floor(inductLoopPosition / DEFAULT_LENGTH_WITH_GAP) * myPassingTime) > STEPS2TIME(minDur)) {
148                 // warn if the minGap is insufficient to clear vehicles between stop line and detector
149                 WRITE_WARNING("At actuated tlLogic '" + getID() + "', minDur " + time2string(minDur) + " is too short for a detector gap of " + toString(inductLoopPosition) + "m.");
150                 warn = false;
151             }
152         }
153     }
154     // assign loops to phase index (myInductLoopsForPhase)
155     //  check1: loops may not be used for a phase if there are other connections from the same lane that may not drive in that phase
156     //            greenMinor is ambiguous as vehicles may not be able to drive
157     //            Under the following condition we allow actuation from minor link:
158     //              check1a : the minor link is minor in all phases
159     //              check1b : there is another major link from the same lane in the current phase
160     //            (Under these conditions we assume that the minor link is unimportant and traffic is mostly for the major link)
161     //
162     //              check1c: when the lane has only one edge, we treat greenMinor as green as there would be no actuation otherwise
163     //
164     //  check2: if there are two loops on subsequent lanes (joined tls) and the second one has a red link, the first loop may not be used
165 
166     // also assign loops to link index for validation:
167     // check if all links from actuated phases (minDur != maxDur) have an inductionloop in at least one phase
168     const SVCPermissions motorized = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
169     std::map<int, std::set<MSInductLoop*> > linkToLoops;
170     std::set<int> actuatedLinks;
171 
172     std::vector<bool> neverMajor(numLinks, true);
173     for (const MSPhaseDefinition* phase : myPhases) {
174         const std::string& state = phase->getState();
175         for (int i = 0; i < numLinks; i++)  {
176             if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
177                 neverMajor[i] = false;
178             }
179         }
180     }
181     std::vector<bool> oneLane(numLinks, false);
182     for (int i = 0; i < numLinks; i++)  {
183         for (MSLane* lane : getLanesAt(i)) {
184             // only count motorized vehicle lanes
185             int numMotorized = 0;
186             for (MSLane* l : lane->getEdge().getLanes()) {
187                 if ((l->getPermissions() & motorized) != 0) {
188                     numMotorized++;
189                 }
190             }
191             if (numMotorized == 1) {
192                 oneLane[i] = true;
193                 break;
194             }
195         }
196     }
197 
198 
199     for (const MSPhaseDefinition* phase : myPhases) {
200         std::set<MSInductLoop*> loops;
201         if (phase->minDuration != phase->maxDuration) {
202             // actuated phase
203             const std::string& state = phase->getState();
204             // collect indices of all green links for the phase
205             std::set<int> greenLinks;
206             // collect green links for each induction loops (in this phase)
207             std::map<MSInductLoop*, std::set<int> > loopLinks;
208 
209             for (int i = 0; i < numLinks; i++)  {
210                 if (state[i] == LINKSTATE_TL_GREEN_MAJOR
211                         || (state[i] == LINKSTATE_TL_GREEN_MINOR
212                             && ((neverMajor[i]  // check1a
213                                  && hasMajor(state, getLanesAt(i))) // check1b
214                                 || oneLane[i])) // check1c
215                    ) {
216                     greenLinks.insert(i);
217                     actuatedLinks.insert(i);
218                 }
219 #ifdef DEBUG_DETECTORS
220                 //if (DEBUG_COND) {
221                 //    std::cout << " phase=" << myInductLoopsForPhase.size() << " i=" << i << " state=" << state[i] << " green=" << greenLinks.count(i) << " oneLane=" << oneLane[i]
222                 //        << " loopLanes=";
223                 //    for (MSLane* lane: getLanesAt(i)) {
224                 //        if (laneInductLoopMap.count(lane) != 0) {
225                 //            std::cout << lane->getID() << " ";
226                 //        }
227                 //    }
228                 //    std::cout << "\n";
229                 //}
230 #endif
231                 for (MSLane* lane : getLanesAt(i)) {
232                     if (laneInductLoopMap.count(lane) != 0) {
233                         loopLinks[laneInductLoopMap[lane]].insert(i);
234                     }
235                 }
236             }
237             for (auto& item : loopLinks) {
238                 bool usable = true;
239                 // check1
240                 for (int j : item.second) {
241                     if (greenLinks.count(j) == 0) {
242                         usable = false;
243 #ifdef DEBUG_DETECTORS
244                         if (DEBUG_COND) {
245                             std::cout << " phase=" << myInductLoopsForPhase.size() << " check1: loopLane=" << item.first->getLane()->getID() << " notGreen=" << j << " oneLane[j]=" << oneLane[j] << "\n";
246                         }
247 #endif
248                         break;
249                     }
250                 }
251                 // check2
252                 if (usable) {
253                     const MSLane* loopLane = item.first->getLane();
254                     for (MSLink* link : loopLane->getLinkCont()) {
255                         const MSLane* next = link->getLane();
256                         if (laneInductLoopMap.count(next) != 0) {
257                             MSInductLoop* nextLoop = laneInductLoopMap[next];
258                             for (int j : loopLinks[nextLoop]) {
259                                 if (greenLinks.count(j) == 0) {
260                                     usable = false;
261 #ifdef DEBUG_DETECTORS
262                                     if (DEBUG_COND) std::cout << " phase=" << myInductLoopsForPhase.size() << " check2: loopLane=" << item.first->getLane()->getID()
263                                                                   << " nextLane=" << next->getID() << " nextLink=" << j << " nextState=" << state[j] << "\n";
264 #endif
265                                     break;
266                                 }
267                             }
268                         }
269                     }
270                 }
271 
272                 if (usable) {
273                     loops.insert(item.first);
274 #ifdef DEBUG_DETECTORS
275                     //if (DEBUG_COND) std::cout << " phase=" << myInductLoopsForPhase.size() << " usableLoops=" << item.first->getID() << " links=" << joinToString(item.second, " ") << "\n";
276 #endif
277                     for (int j : item.second) {
278                         linkToLoops[j].insert(item.first);
279                     }
280                 }
281             }
282             if (loops.size() == 0) {
283                 WRITE_WARNING("At actuated tlLogic '" + getID() + "', actuated phase " + toString(myInductLoopsForPhase.size()) + " has no controlling detector");
284             }
285         }
286 #ifdef DEBUG_DETECTORS
287         if (DEBUG_COND) {
288             std::cout << " phase=" << myInductLoopsForPhase.size() << " loops=" << joinNamedToString(loops, " ") << "\n";
289         }
290         //if (DEBUG_COND) {
291         //    std::cout << " linkToLoops:\n";
292         //    for (auto item : linkToLoops) {
293         //        std::cout << "   link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
294         //    }
295         //}
296 #endif
297         myInductLoopsForPhase.push_back(std::vector<MSInductLoop*>(loops.begin(), loops.end()));
298     }
299 #ifdef DEBUG_DETECTORS
300     //if (DEBUG_COND) {
301     //    std::cout << "final linkToLoops:\n";
302     //    for (auto item : linkToLoops) {
303     //        std::cout << "   link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
304     //    }
305     //}
306 #endif
307     for (int i : actuatedLinks) {
308         if (linkToLoops[i].size() == 0 && myLinks[i].size() > 0
309                 && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
310             WRITE_WARNING("At actuated tlLogic '" + getID() + "', linkIndex " + toString(i) + " has no controlling detector");
311         }
312     }
313 }
314 
315 
316 SUMOTime
getMinimumMinDuration(MSLane * lane) const317 MSActuatedTrafficLightLogic::getMinimumMinDuration(MSLane* lane) const {
318     SUMOTime result = std::numeric_limits<SUMOTime>::max();
319     for (const MSPhaseDefinition* phase : myPhases) {
320         const std::string& state = phase->getState();
321         for (int i = 0; i < (int)state.size(); i++)  {
322             if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
323                 for (MSLane* cand : getLanesAt(i)) {
324                     if (lane == cand) {
325                         if (phase->minDuration != phase->maxDuration) {
326                             result = MIN2(result, phase->minDuration);
327                         }
328                     }
329                 }
330             }
331         }
332     }
333     return result;
334 }
335 
336 bool
hasMajor(const std::string & state,const LaneVector & lanes) const337 MSActuatedTrafficLightLogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
338     for (int i = 0; i < (int)state.size(); i++) {
339         if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
340             for (MSLane* cand : getLanesAt(i)) {
341                 for (MSLane* lane : lanes) {
342                     if (lane == cand) {
343                         return true;
344                     }
345                 }
346             }
347         }
348     }
349     return false;
350 }
351 
352 
353 // ------------ Switching and setting current rows
354 SUMOTime
trySwitch()355 MSActuatedTrafficLightLogic::trySwitch() {
356     // checks if the actual phase should be continued
357     // @note any vehicles which arrived during the previous phases which are now waiting between the detector and the stop line are not
358     // considere here. RiLSA recommends to set minDuration in a way that lets all vehicles pass the detector
359     const double detectionGap = gapControl();
360     if (detectionGap < std::numeric_limits<double>::max()) {
361         return duration(detectionGap);
362     }
363     // increment the index to the current phase
364     myStep++;
365     assert(myStep <= (int)myPhases.size());
366     if (myStep == (int)myPhases.size()) {
367         myStep = 0;
368     }
369     //stores the time the phase started
370     myPhases[myStep]->myLastSwitch = MSNet::getInstance()->getCurrentTimeStep();
371     // activate coloring
372     if (myShowDetectors && getCurrentPhaseDef().isGreenPhase()) {
373         for (MSInductLoop* loop : myInductLoopsForPhase[myStep]) {
374             loop->setSpecialColor(&RGBColor::GREEN);
375         }
376     }
377 
378     // set the next event
379     return getCurrentPhaseDef().minDuration;
380 }
381 
382 
383 // ------------ "actuated" algorithm methods
384 SUMOTime
duration(const double detectionGap) const385 MSActuatedTrafficLightLogic::duration(const double detectionGap) const {
386     assert(getCurrentPhaseDef().isGreenPhase());
387     assert((int)myPhases.size() > myStep);
388     const SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
389     // ensure that minimum duration is kept
390     SUMOTime newDuration = getCurrentPhaseDef().minDuration - actDuration;
391     // try to let the last detected vehicle pass the intersection (duration must be positive)
392     newDuration = MAX3(newDuration, TIME2STEPS(myDetectorGap - detectionGap), SUMOTime(1));
393     // cut the decimal places to ensure that phases always have integer duration
394     if (newDuration % 1000 != 0) {
395         const SUMOTime totalDur = newDuration + actDuration;
396         newDuration = (totalDur / 1000 + 1) * 1000 - actDuration;
397     }
398     // ensure that the maximum duration is not exceeded
399     newDuration = MIN2(newDuration, getCurrentPhaseDef().maxDuration - actDuration);
400     return newDuration;
401 }
402 
403 
404 double
gapControl()405 MSActuatedTrafficLightLogic::gapControl() {
406     //intergreen times should not be lenghtend
407     assert((int)myPhases.size() > myStep);
408     double result = std::numeric_limits<double>::max();
409     if (MSGlobals::gUseMesoSim) {
410         return result;
411     }
412     // switch off active colors
413     if (myShowDetectors) {
414         for (MSInductLoop* loop : myInductLoops) {
415             loop->setSpecialColor(nullptr);
416         }
417     }
418     if (!getCurrentPhaseDef().isGreenPhase()) {
419         return result; // end current phase
420     }
421 
422     // Checks, if the maxDuration is kept. No phase should longer send than maxDuration.
423     SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
424     if (actDuration >= getCurrentPhaseDef().maxDuration) {
425         return result; // end current phase
426     }
427 
428     // now the gapcontrol starts
429     for (MSInductLoop* loop : myInductLoopsForPhase[myStep]) {
430         loop->setSpecialColor(&RGBColor::GREEN);
431         const double actualGap = loop->getTimeSinceLastDetection();
432         if (actualGap < myMaxGap) {
433             result = MIN2(result, actualGap);
434         }
435     }
436     return result;
437 }
438 
439 
440 
441 /****************************************************************************/
442 
443