Eclipse SUMO - Simulation of Urban MObility
MSActuatedTrafficLightLogic.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2020 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials are made available under the
5 // terms of the Eclipse Public License 2.0 which is available at
6 // https://www.eclipse.org/legal/epl-2.0/
7 // This Source Code may also be made available under the following Secondary
8 // Licenses when the conditions for such availability set forth in the Eclipse
9 // Public License 2.0 are satisfied: GNU General Public License, version 2
10 // or later which is available at
11 // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 /****************************************************************************/
22 // An actuated (adaptive) traffic light logic
23 /****************************************************************************/
24 #include <config.h>
25 
26 #include <cassert>
27 #include <utility>
28 #include <vector>
29 #include <bitset>
33 #include <microsim/MSGlobals.h>
34 #include <microsim/MSNet.h>
35 #include "MSTrafficLightLogic.h"
37 #include <microsim/MSLane.h>
38 #include <microsim/MSEdge.h>
41 
42 //#define DEBUG_DETECTORS
43 //#define DEBUG_PHASE_SELECTION
44 #define DEBUG_COND (getID()=="C")
45 
46 // ===========================================================================
47 // parameter defaults definitions
48 // ===========================================================================
49 #define DEFAULT_MAX_GAP "3.0"
50 #define DEFAULT_PASSING_TIME "1.9"
51 #define DEFAULT_DETECTOR_GAP "2.0"
52 #define DEFAULT_INACTIVE_THRESHOLD "180"
53 #define DEFAULT_CURRENT_PRIORITY 10
54 
55 #define DEFAULT_LENGTH_WITH_GAP 7.5
56 
57 
58 // ===========================================================================
59 // method definitions
60 // ===========================================================================
62  const std::string& id, const std::string& programID,
63  const Phases& phases,
64  int step, SUMOTime delay,
65  const std::map<std::string, std::string>& parameter,
66  const std::string& basePath) :
67  MSSimpleTrafficLightLogic(tlcontrol, id, programID, TrafficLightType::ACTUATED, phases, step, delay, parameter),
68  myLastTrySwitchTime(0) {
73  myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
74  myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
76  myVehicleTypes = getParameter("vTypes", "");
77 }
78 
80 
81 void
84  if (myLanes.size() == 0) {
85  // must be an older network
86  WRITE_WARNING("Traffic light '" + getID() + "' does not control any links");
87  }
88  bool warn = true; // warn only once
89  const int numLinks = (int)myLinks.size();
90 
91  // Detector position should be computed based on road speed. If the position
92  // is quite far away and the minDur is short this may cause the following
93  // problems:
94  //
95  // 1) high flow failure:
96  // In a standing queue, no vehicle touches the detector.
97  // By the time the queue advances, the detector gap has been exceeded and the phase terminates prematurely
98  //
99  // 2) low flow failure
100  // The standing queue is fully between stop line and detector and there are no further vehicles.
101  // The minDur is too short to let all vehicles pass
102  //
103  // Problem 2) is not so critical because there is less potential for
104  // jamming in a low-flow situation. In contrast, problem 1) should be
105  // avoided as it has big jamming potential. We compute an upper bound for the
106  // detector distance to avoid it
107 
108 
109  // change values for setting the loops and lanestate-detectors, here
110  //SUMOTime inductLoopInterval = 1; //
111  // build the induct loops
112  std::map<const MSLane*, MSInductLoop*> laneInductLoopMap;
113  std::map<MSInductLoop*, const MSLane*> inductLoopLaneMap; // in case loops are placed further upstream
114  double maxDetectorGap = 0;
115  for (LaneVector& lanes : myLanes) {
116  for (MSLane* lane : lanes) {
117  if (noVehicles(lane->getPermissions())) {
118  // do not build detectors on green verges or sidewalks
119  continue;
120  }
121  if (laneInductLoopMap.find(lane) != laneInductLoopMap.end()) {
122  // only build one detector per lane
123  continue;
124  }
125  const SUMOTime minDur = getMinimumMinDuration(lane);
126  if (minDur == std::numeric_limits<SUMOTime>::max()) {
127  // only build detector if this lane is relevant for an actuated phase
128  continue;
129  }
130  const std::string customID = getParameter(lane->getID());
131  double length = lane->getLength();
132  double ilpos;
133  double inductLoopPosition;
134  MSInductLoop* loop = nullptr;
135  if (customID == "") {
136  double speed = lane->getSpeedLimit();
137  inductLoopPosition = MIN2(
138  myDetectorGap * speed,
139  (STEPS2TIME(minDur) / myPassingTime + 0.5) * DEFAULT_LENGTH_WITH_GAP);
140 
141  // check whether the lane is long enough
142  ilpos = length - inductLoopPosition;
143  MSLane* placementLane = lane;
144  while (ilpos < 0 && placementLane->getIncomingLanes().size() == 1) {
145  placementLane = placementLane->getLogicalPredecessorLane();
146  ilpos += placementLane->getLength();
147  }
148  if (ilpos < 0) {
149  ilpos = 0;
150  }
151  // Build the induct loop and set it into the container
152  std::string id = "TLS" + myID + "_" + myProgramID + "_InductLoopOn_" + lane->getID();
153  loop = static_cast<MSInductLoop*>(nb.createInductLoop(id, placementLane, ilpos, myVehicleTypes, myShowDetectors));
155  } else {
157  if (loop == nullptr) {
158  WRITE_ERROR("Unknown inductionLoop '" + customID + "' given as custom detector for actuated tlLogic '" + getID() + "', program '" + getProgramID() + ".");
159  continue;
160  }
161  ilpos = loop->getPosition();
162  inductLoopPosition = length - ilpos;
163  }
164  laneInductLoopMap[lane] = loop;
165  inductLoopLaneMap[loop] = lane;
166  myInductLoops.push_back(InductLoopInfo(loop, (int)myPhases.size()));
167  maxDetectorGap = MAX2(maxDetectorGap, length - ilpos);
168 
169  if (warn && floor(floor(inductLoopPosition / DEFAULT_LENGTH_WITH_GAP) * myPassingTime) > STEPS2TIME(minDur)) {
170  // warn if the minGap is insufficient to clear vehicles between stop line and detector
171  WRITE_WARNING("At actuated tlLogic '" + getID() + "', minDur " + time2string(minDur) + " is too short for a detector gap of " + toString(inductLoopPosition) + "m.");
172  warn = false;
173  }
174  }
175  }
176  // assign loops to phase index (myInductLoopsForPhase)
177  // 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
178  // greenMinor is ambiguous as vehicles may not be able to drive
179  // Under the following condition we allow actuation from minor link:
180  // check1a : the minor link is minor in all phases
181  // check1b : there is another major link from the same lane in the current phase
182  // (Under these conditions we assume that the minor link is unimportant and traffic is mostly for the major link)
183  //
184  // check1c: when the lane has only one edge, we treat greenMinor as green as there would be no actuation otherwise
185  // check1d: for turnarounds 1b is sufficient and we do not require 1a
186  //
187  // 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
188 
189  // also assign loops to link index for validation:
190  // check if all links from actuated phases (minDur != maxDur) have an inductionloop in at least one phase
191  const SVCPermissions motorized = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
192  std::map<int, std::set<MSInductLoop*> > linkToLoops;
193  std::set<int> actuatedLinks;
194 
195  std::vector<bool> neverMajor(numLinks, true);
196  for (const MSPhaseDefinition* phase : myPhases) {
197  const std::string& state = phase->getState();
198  for (int i = 0; i < numLinks; i++) {
199  if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
200  neverMajor[i] = false;
201  }
202  }
203  }
204  std::vector<bool> oneLane(numLinks, false);
205  std::vector<bool> turnaround(numLinks, true);
206  for (int i = 0; i < numLinks; i++) {
207  for (MSLane* lane : getLanesAt(i)) {
208  // only count motorized vehicle lanes
209  int numMotorized = 0;
210  for (MSLane* l : lane->getEdge().getLanes()) {
211  if ((l->getPermissions() & motorized) != 0) {
212  numMotorized++;
213  }
214  }
215  if (numMotorized == 1) {
216  oneLane[i] = true;
217  break;
218  }
219  }
220  for (MSLink* link : getLinksAt(i)) {
221  if (!link->isTurnaround()) {
222  turnaround[i] = false;
223  break;
224  }
225  }
226  }
227 
228 
229  for (const MSPhaseDefinition* phase : myPhases) {
230  const int phaseIndex = (int)myInductLoopsForPhase.size();
231  std::set<MSInductLoop*> loops;
232  if (phase->minDuration != phase->maxDuration) {
233  // actuated phase
234  const std::string& state = phase->getState();
235  // collect indices of all green links for the phase
236  std::set<int> greenLinks;
237  // collect green links for each induction loops (in this phase)
238  std::map<MSInductLoop*, std::set<int> > loopLinks;
239 
240  for (int i = 0; i < numLinks; i++) {
241  if (state[i] == LINKSTATE_TL_GREEN_MAJOR
242  || (state[i] == LINKSTATE_TL_GREEN_MINOR
243  && (((neverMajor[i] || turnaround[i]) // check1a, 1d
244  && hasMajor(state, getLanesAt(i))) // check1b
245  || oneLane[i])) // check1c
246  ) {
247  greenLinks.insert(i);
248  if (state[i] == LINKSTATE_TL_GREEN_MAJOR || !turnaround[i]) {
249  actuatedLinks.insert(i);
250  }
251  }
252 #ifdef DEBUG_DETECTORS
253  if (DEBUG_COND) {
254  std::cout << " phase=" << phaseIndex << " i=" << i << " state=" << state[i] << " green=" << greenLinks.count(i) << " oneLane=" << oneLane[i]
255  << " turn=" << turnaround[i] << " loopLanes=";
256  for (MSLane* lane : getLanesAt(i)) {
257  if (laneInductLoopMap.count(lane) != 0) {
258  std::cout << lane->getID() << " ";
259  }
260  }
261  std::cout << "\n";
262  }
263 #endif
264  for (MSLane* lane : getLanesAt(i)) {
265  if (laneInductLoopMap.count(lane) != 0) {
266  loopLinks[laneInductLoopMap[lane]].insert(i);
267  }
268  }
269  }
270  for (auto& item : loopLinks) {
271  MSInductLoop* loop = item.first;
272  const MSLane* loopLane = inductLoopLaneMap[loop];
273  bool usable = true;
274  // check1
275  for (int j : item.second) {
276  if (greenLinks.count(j) == 0) {
277  usable = false;
278 #ifdef DEBUG_DETECTORS
279  if (DEBUG_COND) {
280  std::cout << " phase=" << phaseIndex << " check1: loopLane=" << loopLane->getID() << " notGreen=" << j << " oneLane[j]=" << oneLane[j] << "\n";
281  }
282 #endif
283  break;
284  }
285  }
286  // check2
287  if (usable) {
288  for (MSLink* link : loopLane->getLinkCont()) {
289  if (link->isTurnaround()) {
290  continue;
291  }
292  const MSLane* next = link->getLane();
293  if (laneInductLoopMap.count(next) != 0) {
294  MSInductLoop* nextLoop = laneInductLoopMap[next];
295  for (int j : loopLinks[nextLoop]) {
296  if (greenLinks.count(j) == 0) {
297  usable = false;
298 #ifdef DEBUG_DETECTORS
299  if (DEBUG_COND) std::cout << " phase=" << phaseIndex << " check2: loopLane=" << loopLane->getID()
300  << " nextLane=" << next->getID() << " nextLink=" << j << " nextState=" << state[j] << "\n";
301 #endif
302  break;
303  }
304  }
305  }
306  }
307  }
308 
309  if (usable) {
310  loops.insert(item.first);
311 #ifdef DEBUG_DETECTORS
312  if (DEBUG_COND) {
313  std::cout << " phase=" << phaseIndex << " usableLoops=" << item.first->getID() << " links=" << joinToString(item.second, " ") << "\n";
314  }
315 #endif
316  for (int j : item.second) {
317  linkToLoops[j].insert(item.first);
318  }
319  }
320  }
321  if (loops.size() == 0) {
322  WRITE_WARNINGF("At actuated tlLogic '%', actuated phase % has no controlling detector", getID(), toString(phaseIndex));
323  }
324  }
325 #ifdef DEBUG_DETECTORS
326  if (DEBUG_COND) {
327  std::cout << " phase=" << phaseIndex << " loops=" << joinNamedToString(loops, " ") << "\n";
328  }
329  if (DEBUG_COND) {
330  std::cout << " linkToLoops:\n";
331  for (auto item : linkToLoops) {
332  std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
333  }
334  }
335 #endif
336  std::vector<InductLoopInfo*> loopInfos;
337  myInductLoopsForPhase.push_back(loopInfos);
338  for (MSInductLoop* loop : loops) {
339  for (InductLoopInfo& loopInfo : myInductLoops) {
340  if (loopInfo.loop == loop) {
341  myInductLoopsForPhase.back().push_back(&loopInfo);
342  loopInfo.servedPhase[phaseIndex] = true;
343  }
344  }
345  }
346  }
347 #ifdef DEBUG_DETECTORS
348  if (DEBUG_COND) {
349  std::cout << "final linkToLoops:\n";
350  for (auto item : linkToLoops) {
351  std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
352  }
353  }
354 #endif
355  for (int i : actuatedLinks) {
356  if (linkToLoops[i].size() == 0 && myLinks[i].size() > 0
357  && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
358  WRITE_WARNINGF("At actuated tlLogic '%', linkIndex % has no controlling detector", getID(), toString(i));
359  }
360  }
361  // parse maximum green times for each link (optional)
362  for (const auto& kv : getParametersMap()) {
363  if (StringUtils::startsWith(kv.first, "linkMaxDur:")) {
364  int link = StringUtils::toInt(kv.first.substr(11));
365  if (link < 0 || link >= myNumLinks) {
366  WRITE_ERROR("Invalid link '" + kv.first.substr(11) + "' given as linkMaxDur parameter for actuated tlLogic '" + getID() + "', program '" + getProgramID() + ".");
367  continue;
368  }
369  if (myLinkMaxGreenTimes.empty()) {
370  myLinkMaxGreenTimes = std::vector<SUMOTime>(myNumLinks, std::numeric_limits<SUMOTime>::max());
371  }
372  myLinkMaxGreenTimes[link] = string2time(kv.second);
373  } else if (StringUtils::startsWith(kv.first, "linkMinDur:")) {
374  int link = StringUtils::toInt(kv.first.substr(11));
375  if (link < 0 || link >= myNumLinks) {
376  WRITE_ERROR("Invalid link '" + kv.first.substr(11) + "' given as linkMinDur parameter for actuated tlLogic '" + getID() + "', program '" + getProgramID() + ".");
377  continue;
378  }
379  if (myLinkMinGreenTimes.empty()) {
380  myLinkMinGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
381  }
382  myLinkMinGreenTimes[link] = string2time(kv.second);
383  }
384  }
385  if (myLinkMaxGreenTimes.size() > 0 || myLinkMinGreenTimes.size() > 0) {
386  myLinkGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
387  }
388  //std::cout << SIMTIME << " linkMaxGreenTimes=" << toString(myLinkMaxGreenTimes) << "\n";
389 }
390 
391 
392 SUMOTime
394  SUMOTime result = std::numeric_limits<SUMOTime>::max();
395  for (const MSPhaseDefinition* phase : myPhases) {
396  const std::string& state = phase->getState();
397  for (int i = 0; i < (int)state.size(); i++) {
398  if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
399  for (MSLane* cand : getLanesAt(i)) {
400  if (lane == cand) {
401  if (phase->minDuration != phase->maxDuration) {
402  result = MIN2(result, phase->minDuration);
403  }
404  }
405  }
406  }
407  }
408  }
409  return result;
410 }
411 
412 bool
413 MSActuatedTrafficLightLogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
414  for (int i = 0; i < (int)state.size(); i++) {
415  if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
416  for (MSLane* cand : getLanesAt(i)) {
417  for (MSLane* lane : lanes) {
418  if (lane == cand) {
419  return true;
420  }
421  }
422  }
423  }
424  }
425  return false;
426 }
427 
428 
429 // ------------ Switching and setting current rows
430 void
433  for (InductLoopInfo& loopInfo : myInductLoops) {
434  loopInfo.loop->setVisible(true);
435  }
436 }
437 
438 
439 void
442  for (InductLoopInfo& loopInfo : myInductLoops) {
443  loopInfo.loop->setVisible(false);
444  }
445 }
446 
447 SUMOTime
449  // checks if the actual phase should be continued
450  // @note any vehicles which arrived during the previous phases which are now waiting between the detector and the stop line are not
451  // considere here. RiLSA recommends to set minDuration in a way that lets all vehicles pass the detector
453  if (myLinkGreenTimes.size() > 0) {
454  // constraints exist, record green time durations for each link
455  const std::string& state = getCurrentPhaseDef().getState();
456  SUMOTime lastDuration = now - myLastTrySwitchTime;
457  for (int i = 0; i < myNumLinks; i++) {
458  if (state[i] == 'G' || state[i] == 'g') {
459  myLinkGreenTimes[i] += lastDuration;
460  } else {
461  myLinkGreenTimes[i] = 0;
462  }
463  }
464  //std::cout << SIMTIME << " greenTimes=" << toString(myLinkGreenTimes) << "\n";
465  }
466  myLastTrySwitchTime = now;
467  const double detectionGap = gapControl();
468  const bool multiTarget = myPhases[myStep]->nextPhases.size() > 1 && myPhases[myStep]->nextPhases.front() >= 0;
469 #ifdef DEBUG_PHASE_SELECTION
470  if (DEBUG_COND) {
471  std::cout << SIMTIME << " p=" << myStep << " trySwitch dGap=" << detectionGap << " multi=" << multiTarget << "\n";
472  }
473 #endif
474  if (detectionGap < std::numeric_limits<double>::max() && !multiTarget) {
475  return duration(detectionGap);
476  }
477  // decide the next phase
478  const int origStep = myStep;
479  int nextStep = myStep;
480  SUMOTime actDuration = now - myPhases[myStep]->myLastSwitch;
481  if (multiTarget) {
482  nextStep = decideNextPhase();
483  } else {
484  if (myPhases[myStep]->nextPhases.size() == 1 && myPhases[myStep]->nextPhases.front() >= 0) {
485  nextStep = myPhases[myStep]->nextPhases.front();
486  } else {
487  nextStep++;
488  }
489  }
490  if (nextStep == (int)myPhases.size()) {
491  nextStep = 0;
492  }
493  SUMOTime linkMinDur = getLinkMinDuration(getTarget(nextStep));
494  if (linkMinDur > 0) {
495  // for multiTarget, the current phase must be extended but if another
496  // targer is chosen, earlier switching than linkMinDur is possible
497  return multiTarget ? TIME2STEPS(1) : linkMinDur;
498  }
499  myStep = nextStep;
500  assert(myStep <= (int)myPhases.size());
501  assert(myStep >= 0);
502  //stores the time the phase started
503  if (myStep != origStep) {
504  myPhases[myStep]->myLastSwitch = now;
505  actDuration = 0;
506  }
507  // activate coloring
508  if ((myShowDetectors || multiTarget) && getCurrentPhaseDef().isGreenPhase()) {
509  for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
510  //std::cout << SIMTIME << " p=" << myStep << " loopinfo=" << loopInfo->loop->getID() << " set lastGreen=" << STEPS2TIME(now) << "\n";
511  loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
512  loopInfo->lastGreenTime = now;
513  }
514  }
515  // set the next event
516  return MAX2(TIME2STEPS(1), getCurrentPhaseDef().minDuration - actDuration);
517 }
518 
519 
520 // ------------ "actuated" algorithm methods
521 SUMOTime
522 MSActuatedTrafficLightLogic::duration(const double detectionGap) const {
523  assert(getCurrentPhaseDef().isGreenPhase());
524  assert((int)myPhases.size() > myStep);
525  const SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
526  // ensure that minimum duration is kept
527  SUMOTime newDuration = getCurrentPhaseDef().minDuration - actDuration;
528  // try to let the last detected vehicle pass the intersection (duration must be positive)
529  newDuration = MAX3(newDuration, TIME2STEPS(myDetectorGap - detectionGap), SUMOTime(1));
530  // cut the decimal places to ensure that phases always have integer duration
531  if (newDuration % 1000 != 0) {
532  const SUMOTime totalDur = newDuration + actDuration;
533  newDuration = (totalDur / 1000 + 1) * 1000 - actDuration;
534  }
535  // ensure that the maximum duration is not exceeded
536  newDuration = MIN2(newDuration, getCurrentPhaseDef().maxDuration - actDuration);
537  return newDuration;
538 }
539 
540 
541 double
543  //intergreen times should not be lengthend
544  assert((int)myPhases.size() > myStep);
545  double result = std::numeric_limits<double>::max();
547  return result;
548  }
549  // switch off active colors
550  if (myShowDetectors) {
551  for (InductLoopInfo& loopInfo : myInductLoops) {
552  if (loopInfo.lastGreenTime < loopInfo.loop->getLastDetectionTime()) {
553  loopInfo.loop->setSpecialColor(&RGBColor::RED);
554  } else {
555  loopInfo.loop->setSpecialColor(nullptr);
556  }
557  }
558  }
559  if (!getCurrentPhaseDef().isGreenPhase()) {
560  return result; // end current phase
561  }
562 
563  // Checks, if the maxDuration is kept. No phase should last longer than maxDuration.
564  SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
565  if (actDuration >= getCurrentPhaseDef().maxDuration || maxLinkDurationReached()) {
566  return result; // end current phase
567  }
568 
569  // now the gapcontrol starts
570  for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
571  MSInductLoop* loop = loopInfo->loop;
573  const double actualGap = loop->getTimeSinceLastDetection();
574  if (actualGap < myMaxGap) {
575  result = MIN2(result, actualGap);
576  }
577  }
578  return result;
579 }
580 
581 
582 int
584  const auto& cands = myPhases[myStep]->nextPhases;
585  // decide by priority
586  // first target is the default when thre is no traffic
587  // @note: the keep the current phase, even when there is no traffic, it must be added to 'next' explicitly
588  int result = cands.front();
589  int maxPrio = 0;
590  SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
591  const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && !maxLinkDurationReached();
592  if (canExtend) {
593  // consider keeping the current phase until maxDur is reached
594  // (only when there is still traffic in that phase)
595  int currentPrio = getPhasePriority(myStep);
596 #ifdef DEBUG_PHASE_SELECTION
597  std::cout << SIMTIME << " p=" << myStep << " loops=" << myInductLoopsForPhase[myStep].size() << " currentPrio=" << currentPrio << "\n";
598 #endif
599  if (currentPrio > maxPrio) {
600  result = myStep;
601  maxPrio = currentPrio;
602  }
603  }
604  for (int step : cands) {
605  int target = getTarget(step);
606  int prio = getPhasePriority(target);
607 #ifdef DEBUG_PHASE_SELECTION
608  if (DEBUG_COND) {
609  std::cout << SIMTIME << " p=" << myStep << " step=" << step << " target=" << target << " loops=" << myInductLoopsForPhase[target].size() << " prio=" << prio << "\n";
610  }
611 #endif
612  if (prio > maxPrio && canExtendLinkGreen(target)) {
613  maxPrio = prio;
614  result = step;
615  }
616  }
617  // prevent starvation in phases that are not direct targets
618  for (const InductLoopInfo& loopInfo : myInductLoops) {
619  int prio = getDetectorPriority(loopInfo);
620  if (prio > maxPrio) {
621  result = cands.front();
622  if (result == myStep) {
623  WRITE_WARNING("At actuated tlLogic '" + getID()
624  + "', starvation at e1Detector '" + loopInfo.loop->getID()
625  + "' which cannot be reached from the default phase " + toString(myStep) + ".");
626  }
627  // use default phase to reach other phases
628 #ifdef DEBUG_PHASE_SELECTION
629  if (DEBUG_COND) {
630  std::cout << SIMTIME << " p=" << myStep << " loop=" << loopInfo.loop->getID() << " prio=" << prio << " next=" << result << "\n";
631  }
632 #endif
633  break;
634  }
635  }
636  return result;
637 }
638 
639 
640 int
642  int origStep = step;
643  // if step is a transition, find the upcoming green phase
644  while (!myPhases[step]->isGreenPhase()) {
645  if (myPhases[step]->nextPhases.size() > 0 && myPhases[step]->nextPhases.front() >= 0) {
646  if (myPhases[step]->nextPhases.size() > 1) {
647  WRITE_WARNING("At actuated tlLogic '" + getID() + "', transition phase " + toString(step) + " should not have multiple next phases");
648  }
649  step = myPhases[step]->nextPhases.front();
650  } else {
651  step = (step + 1) % myPhases.size();
652  }
653  if (step == origStep) {
654  WRITE_WARNING("At actuated tlLogic '" + getID() + "', infinite transition loop from phase " + toString(origStep));
655  return 0;
656  }
657  }
658  return step;
659 }
660 
661 int
663  MSInductLoop* loop = loopInfo.loop;
664  const double actualGap = loop->getTimeSinceLastDetection();
665  if (actualGap < myMaxGap || loopInfo.lastGreenTime < loop->getLastDetectionTime()) {
666  SUMOTime inactiveTime = MSNet::getInstance()->getCurrentTimeStep() - loopInfo.lastGreenTime;
667  // @note. Inactive time could also be tracked regardless of current activity (to increase robustness in case of detection failure
668  if (inactiveTime > myInactiveThreshold) {
669 #ifdef DEBUG_PHASE_SELECTION
670  if (DEBUG_COND) {
671  std::cout << " loop=" << loop->getID() << " gap=" << loop->getTimeSinceLastDetection() << " lastGreen=" << STEPS2TIME(loopInfo.lastGreenTime)
672  << " lastDetection=" << STEPS2TIME(loop->getLastDetectionTime()) << " inactive=" << STEPS2TIME(inactiveTime) << "\n";
673  }
674 #endif
675  return (int)STEPS2TIME(inactiveTime);
676  } else {
677  // give bonus to detectors that are currently served (if that phase can stil be extended)
678  if (loopInfo.servedPhase[myStep]) {
679  SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
680  const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration;
681  if (canExtend) {
683  } else {
684  return 0;
685  }
686  }
687  return 1;
688  }
689  }
690  return 0;
691 }
692 
693 int
695  int result = 0;
696  for (const InductLoopInfo* loopInfo : myInductLoopsForPhase[step]) {
697  result += getDetectorPriority(*loopInfo);
698  }
699  return result;
700 }
701 
702 
703 void
705  myShowDetectors = show;
706  for (InductLoopInfo& loopInfo : myInductLoops) {
707  loopInfo.loop->setVisible(myShowDetectors);
708  }
709 }
710 
711 
712 bool
714  if (myLinkMaxGreenTimes.empty()) {
715  return false;
716  }
717  for (int i = 0; i < myNumLinks; i++) {
718  if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i]) {
719  //std::cout << SIMTIME << " maxLinkDurationReached i=" << i << "\n";
720  return true;
721  }
722  }
723  return false;
724 }
725 
726 bool
728  if (myLinkMaxGreenTimes.empty()) {
729  return true;
730  }
731  const std::string& targetState = myPhases[target]->getState();
732  for (int i = 0; i < myNumLinks; i++) {
733  if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i] && (
734  targetState[i] == 'G' || targetState[i] == 'g')) {
735  //std::cout << SIMTIME << " cannotExtendLinkGreen target=" << target << " i=" << i << "\n";
736  return false;
737  }
738  }
739  return true;
740 }
741 
742 SUMOTime
744  SUMOTime result = 0;
745  if (target != myStep && myLinkMinGreenTimes.size() > 0) {
746  const std::string& state = myPhases[myStep]->getState();
747  const std::string& targetState = myPhases[target]->getState();
748  for (int i = 0; i < myNumLinks; i++) {
750  && (state[i] == 'G' || state[i] == 'g')
751  && !(targetState[i] == 'G' || targetState[i] == 'g')) {
752  result = MAX2(result, myLinkMinGreenTimes[i] - myLinkGreenTimes[i]);
753  //std::cout << SIMTIME << " getLinkMinDuration myStep=" << myStep << " target=" << target << " i=" << i
754  // << " greenTime=" << STEPS2TIME(myLinkGreenTimes[i]) << " min=" << STEPS2TIME(myLinkMinGreenTimes[i]) << " result=" << STEPS2TIME(result) << "\n";
755  }
756  }
757  }
758  return result;
759 }
760 
761 
762 void
763 MSActuatedTrafficLightLogic::setParameter(const std::string& key, const std::string& value) {
764  // some pre-defined parameters can be updated at runtime
765  if (key == "detector-gap" || key == "passing-time" || key == "file" || key == "freq" || key == "vTypes"
766  || StringUtils::startsWith(key, "linkMaxDur")
767  || StringUtils::startsWith(key, "linkMinDur")) {
768  throw InvalidArgument(key + " cannot be changed dynamically for actuated traffic light '" + getID() + "'");
769  } else if (key == "max-gap") {
771  } else if (key == "show-detectors") {
773  } else if (key == "inactive-threshold") {
775  }
776  Parameterised::setParameter(key, value);
777 }
778 
779 
780 /****************************************************************************/
#define DEFAULT_DETECTOR_GAP
#define DEFAULT_MAX_GAP
#define DEFAULT_PASSING_TIME
#define DEFAULT_LENGTH_WITH_GAP
#define DEBUG_COND
#define DEFAULT_INACTIVE_THRESHOLD
#define DEFAULT_CURRENT_PRIORITY
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:277
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:284
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:276
std::string time2string(SUMOTime t)
convert SUMOTime to string
Definition: SUMOTime.cpp:68
SUMOTime string2time(const std::string &r)
convert string to SUMOTime
Definition: SUMOTime.cpp:45
#define STEPS2TIME(x)
Definition: SUMOTime.h:53
#define SIMTIME
Definition: SUMOTime.h:60
#define TIME2STEPS(x)
Definition: SUMOTime.h:55
long long int SUMOTime
Definition: SUMOTime.h:31
bool noVehicles(SVCPermissions permissions)
Returns whether an edge with the given permission forbids vehicles.
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_PEDESTRIAN
pedestrian
int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
TrafficLightType
@ SUMO_TAG_INDUCTION_LOOP
alternative tag for e1 detector
@ LINKSTATE_TL_GREEN_MAJOR
The link has green light, may pass.
@ LINKSTATE_TL_GREEN_MINOR
The link has green light, has to brake.
T MIN2(T a, T b)
Definition: StdDefs.h:73
T MAX2(T a, T b)
Definition: StdDefs.h:79
T MAX3(T a, T b, T c)
Definition: StdDefs.h:93
std::string joinNamedToString(const std::set< T *, C > &ns, const T_BETWEEN &between)
Definition: ToString.h:284
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition: ToString.h:250
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:44
static std::string checkForRelativity(const std::string &filename, const std::string &basePath)
Returns the path from a configuration so that it is accessable from the current working directory.
double myDetectorGap
The detector distance in seconds.
double myMaxGap
The maximum gap to check in seconds.
void activateProgram()
called when switching programs
int getTarget(int step)
get the green phase following step
void setParameter(const std::string &key, const std::string &value)
Sets a parameter and updates internal constants.
SUMOTime myLastTrySwitchTime
last time trySwitch was called
int getDetectorPriority(const InductLoopInfo &loopInfo) const
SUMOTime myFreq
The frequency for aggregating detector output.
SUMOTime getMinimumMinDuration(MSLane *lane) const
get the minimum min duration for all stretchable phases that affect the given lane
bool myShowDetectors
Whether the detectors shall be shown in the GUI.
std::vector< SUMOTime > myLinkMaxGreenTimes
maximum consecutive time that the given link may remain green
std::string myVehicleTypes
Whether detector output separates by vType.
double gapControl()
Return the minimum detection gap of all detectors if the current phase should be extended and double:...
void init(NLDetectorBuilder &nb)
Initialises the tls with information about incoming lanes.
std::vector< SUMOTime > myLinkMinGreenTimes
minimum consecutive time that the given link must remain green
SUMOTime trySwitch()
Switches to the next phase.
double myPassingTime
The passing time used in seconds.
SUMOTime getLinkMinDuration(int target) const
the minimum duratin for keeping the current phase due to linkMinDur constraints
MSActuatedTrafficLightLogic(MSTLLogicControl &tlcontrol, const std::string &id, const std::string &programID, const MSSimpleTrafficLightLogic::Phases &phases, int step, SUMOTime delay, const std::map< std::string, std::string > &parameter, const std::string &basePath)
Constructor.
bool canExtendLinkGreen(int target)
whether the target phase is acceptable in light of linkMaxDur constraints
InductLoopMap myInductLoopsForPhase
A map from phase to induction loops to be used for gap control.
int getPhasePriority(int step) const
count the number of active detectors for the given step
SUMOTime duration(const double detectionGap) const
Returns the minimum duration of the current phase.
std::vector< InductLoopInfo > myInductLoops
bool maxLinkDurationReached()
whether the current phase cannot be continued due to linkMaxDur constraints
bool hasMajor(const std::string &state, const LaneVector &lanes) const
return whether there is a major link from the given lane in the given phase
std::vector< SUMOTime > myLinkGreenTimes
consecutive time that the given link index has been green
std::string myFile
The output file for generated detectors.
int decideNextPhase()
select am candidate phases based on detector states
SUMOTime myInactiveThreshold
The time threshold to avoid starved phases.
const NamedObjectCont< MSDetectorFileOutput * > & getTypedDetectors(SumoXMLTag type) const
Returns the list of detectors of the given type.
void add(SumoXMLTag type, MSDetectorFileOutput *d, const std::string &device, SUMOTime splInterval, SUMOTime begin=-1)
Adds a detector/output combination into the containers.
const std::vector< MSLane * > & getLanes() const
Returns this edge's lanes.
Definition: MSEdge.h:166
static bool gUseMesoSim
Definition: MSGlobals.h:88
An unextended detector measuring at a fixed position on a fixed lane.
Definition: MSInductLoop.h:62
double getPosition() const
Returns the position of the detector on the lane.
Definition: MSInductLoop.h:93
virtual void setSpecialColor(const RGBColor *)
allows for special color in the gui version
Definition: MSInductLoop.h:288
double getTimeSinceLastDetection() const
Returns the time since the last vehicle left the detector.
SUMOTime getLastDetectionTime() const
return last time a vehicle was on the detector
Representation of a lane in the micro simulation.
Definition: MSLane.h:82
const std::vector< MSLink * > & getLinkCont() const
returns the container with all links !!!
Definition: MSLane.h:640
double getLength() const
Returns the lane's length.
Definition: MSLane.h:539
MSLane * getLogicalPredecessorLane() const
get the most likely precedecessor lane (sorted using by_connections_to_sorter). The result is cached ...
Definition: MSLane.cpp:2542
MSEdge & getEdge() const
Returns the lane's edge.
Definition: MSLane.h:673
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:171
MSDetectorControl & getDetectorControl()
Returns the detector control.
Definition: MSNet.h:434
SUMOTime getCurrentTimeStep() const
Returns the current simulation step.
Definition: MSNet.h:313
The definition of a single phase of a tls logic.
const std::string & getState() const
Returns the state within this phase.
SUMOTime maxDuration
The maximum duration of the phase.
SUMOTime minDuration
The minimum duration of the phase.
A fixed traffic light logic.
Phases myPhases
The list of phases this logic uses.
const MSPhaseDefinition & getCurrentPhaseDef() const
Returns the definition of the current phase.
A class that stores and controls tls and switching of their programs.
const LinkVector & getLinksAt(int i) const
Returns the list of links that are controlled by the signals at the given position.
std::vector< MSLane * > LaneVector
Definition of the list of arrival lanes subjected to this tls.
virtual void deactivateProgram()
const std::string myProgramID
The id of the logic.
LaneVectorVector myLanes
The list of LaneVectors; each vector contains the incoming lanes that belong to the same link index.
int myNumLinks
number of controlled links
virtual void activateProgram()
called when switching programs
const LaneVector & getLanesAt(int i) const
Returns the list of lanes that are controlled by the signals at the given position.
std::vector< MSPhaseDefinition * > Phases
Definition of a list of phases, being the junction logic.
LinkVectorVector myLinks
The list of LinkVectors; each vector contains the links that belong to the same link index.
virtual void init(NLDetectorBuilder &nb)
Initialises the tls with information about incoming lanes.
const std::string & getProgramID() const
Returns this tl-logic's id.
Builds detectors for microsim.
virtual MSDetectorFileOutput * createInductLoop(const std::string &id, MSLane *lane, double pos, const std::string &vTypes, bool show=true)
Creates an instance of an e1 detector using the given values.
std::string myID
The name of the object.
Definition: Named.h:124
const std::string & getID() const
Returns the id.
Definition: Named.h:73
T get(const std::string &id) const
Retrieves an item.
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:58
const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
const std::map< std::string, std::string > & getParametersMap() const
Returns the inner key/value map.
static const RGBColor GREEN
Definition: RGBColor.h:181
static const RGBColor RED
named colors
Definition: RGBColor.h:180
static double toDouble(const std::string &sData)
converts a string into the double value described by it by calling the char-type converter
static bool startsWith(const std::string &str, const std::string prefix)
Checks whether a given string starts with the prefix.
static int toInt(const std::string &sData)
converts a string into the integer value described by it by calling the char-type converter,...
static bool toBool(const std::string &sData)
converts a string into the bool value described by it by calling the char-type converter