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-2022 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>
34#include <microsim/MSGlobals.h>
35#include <microsim/MSNet.h>
36#include <microsim/MSLane.h>
37#include <microsim/MSEdge.h>
40
41//#define DEBUG_DETECTORS
42//#define DEBUG_PHASE_SELECTION
43#define DEBUG_COND (getID()=="C")
44
45// ===========================================================================
46// static members
47// ===========================================================================
48const std::vector<std::string> MSActuatedTrafficLightLogic::OPERATOR_PRECEDENCE({
49 "**", "^", "*", "/", "+", "-", "%",
50 "=", "==", "!=", "<", ">", "<=", ">=",
51 "and", "&&", "or", "||",
52});
53
54// ===========================================================================
55// parameter defaults definitions
56// ===========================================================================
57#define DEFAULT_MAX_GAP "3.0"
58#define DEFAULT_PASSING_TIME "1.9"
59#define DEFAULT_DETECTOR_GAP "2.0"
60#define DEFAULT_INACTIVE_THRESHOLD "180"
61#define DEFAULT_CURRENT_PRIORITY 10
62
63#define DEFAULT_LENGTH_WITH_GAP 7.5
64
65#define NO_DETECTOR "NO_DETECTOR"
66
67// ===========================================================================
68// method definitions
69// ===========================================================================
71 const std::string& id, const std::string& programID,
72 const SUMOTime offset,
73 const Phases& phases,
74 int step, SUMOTime delay,
75 const Parameterised::Map& parameter,
76 const std::string& basePath,
77 const ConditionMap& conditions,
78 const AssignmentMap& assignments,
79 const FunctionMap& functions) :
80 MSSimpleTrafficLightLogic(tlcontrol, id, programID, offset, TrafficLightType::ACTUATED, phases, step, delay, parameter),
81 myLastTrySwitchTime(0),
82 myConditions(conditions),
83 myAssignments(assignments),
84 myFunctions(functions),
85 myTraCISwitch(false),
86 myDetectorPrefix(id + "_" + programID + "_") {
88 myJamThreshold = StringUtils::toDouble(getParameter("jam-threshold", OptionsCont::getOptions().getValueString("tls.actuated.jam-threshold")));
92 myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
93 myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
95 myVehicleTypes = getParameter("vTypes", "");
96
97 if (knowsParameter("hide-conditions")) {
98 std::vector<std::string> hidden = StringTokenizer(getParameter("hide-conditions", "")).getVector();
99 std::set<std::string> hiddenSet(hidden.begin(), hidden.end());
100 for (auto item : myConditions) {
101 if (hiddenSet.count(item.first) == 0) {
102 myListedConditions.insert(item.first);
103 }
104 }
105 } else {
106 const bool showAll = getParameter("show-conditions", "") == "";
107 std::vector<std::string> shown = StringTokenizer(getParameter("show-conditions", "")).getVector();
108 std::set<std::string> shownSet(shown.begin(), shown.end());
109 for (auto item : myConditions) {
110 if (showAll || shownSet.count(item.first) != 0) {
111 myListedConditions.insert(item.first);
112 }
113 }
114 }
115 if (knowsParameter("extra-detectors")) {
116 const std::string extraIDs = getParameter("extra-detectors", "");
117 for (std::string customID : StringTokenizer(extraIDs).getVector()) {
118 try {
119 myExtraLoops.push_back(retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(customID, extraIDs, true));
120 } catch (ProcessError&) {
121 myExtraE2.push_back(retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(customID, extraIDs, true));
122 }
123 }
124 }
125 myStack.push_back(std::map<std::string, double>());
126}
127
128
130
131void
136 if (myLanes.size() == 0) {
137 // must be an older network
138 WRITE_WARNING("Traffic light '" + getID() + "' does not control any links");
139 }
140 bool warn = true; // warn only once
141 const int numLinks = (int)myLinks.size();
142
143 // Detector position should be computed based on road speed. If the position
144 // is quite far away and the minDur is short this may cause the following
145 // problems:
146 //
147 // 1) high flow failure:
148 // In a standing queue, no vehicle touches the detector.
149 // By the time the queue advances, the detector gap has been exceeded and the phase terminates prematurely
150 //
151 // 2) low flow failure
152 // The standing queue is fully between stop line and detector and there are no further vehicles.
153 // The minDur is too short to let all vehicles pass
154 //
155 // Problem 2) is not so critical because there is less potential for
156 // jamming in a low-flow situation. In contrast, problem 1) should be
157 // avoided as it has big jamming potential. We compute an upper bound for the
158 // detector distance to avoid it
159
160
161 // change values for setting the loops and lanestate-detectors, here
162 //SUMOTime inductLoopInterval = 1; //
163 // build the induct loops
164 std::map<const MSLane*, MSInductLoop*> laneInductLoopMap;
165 std::map<MSInductLoop*, int> inductLoopInfoMap; // retrieve junction entry lane in case loops are placed further upstream (and other properties)
166 int detEdgeIndex = -1;
167 int detLaneIndex = 0;
168 const double detDefaultLength = StringUtils::toDouble(getParameter("detector-length",
169 OptionsCont::getOptions().getValueString("tls.actuated.detector-length")));
170 MSEdge* prevDetEdge = nullptr;
171 for (LaneVector& lanes : myLanes) {
172 for (MSLane* lane : lanes) {
173 const std::string customID = getParameter(lane->getID());
174 if (noVehicles(lane->getPermissions()) && customID == "") {
175 // do not build detectors on green verges or sidewalks
176 continue;
177 }
178 if (laneInductLoopMap.find(lane) != laneInductLoopMap.end()) {
179 // only build one detector per lane
180 continue;
181 }
182 const SUMOTime minDur = getMinimumMinDuration(lane);
183 if (minDur == std::numeric_limits<SUMOTime>::max() && customID == "") {
184 // only build detector if this lane is relevant for an actuated phase
185 continue;
186 }
187 double length = lane->getLength();
188 double ilpos;
189 double inductLoopPosition;
190 MSInductLoop* loop = nullptr;
191 if (&lane->getEdge() != prevDetEdge) {
192 detEdgeIndex++;
193 detLaneIndex = 0;
194 prevDetEdge = &lane->getEdge();
195 } else {
196 detLaneIndex++;
197 }
198 if (customID == "") {
199 double speed = lane->getSpeedLimit();
200 inductLoopPosition = MIN2(
201 myDetectorGap * speed,
203
204 // check whether the lane is long enough
205 ilpos = length - inductLoopPosition;
206 MSLane* placementLane = lane;
207 while (ilpos < 0 && placementLane->getIncomingLanes().size() == 1
208 && placementLane->getIncomingLanes().front().viaLink->getCorrespondingEntryLink()->getTLLogic() == nullptr) {
209 placementLane = placementLane->getLogicalPredecessorLane();
210 ilpos += placementLane->getLength();
211 }
212 if (ilpos < 0) {
213 ilpos = 0;
214 }
215 // Build the induct loop and set it into the container
216 const double detLength = getDouble("detector-length:" + lane->getID(), detDefaultLength);
217 std::string id = myDetectorPrefix + "D" + toString(detEdgeIndex) + "." + toString(detLaneIndex);
218 loop = static_cast<MSInductLoop*>(nb.createInductLoop(id, placementLane, ilpos, detLength, myVehicleTypes, "", "", (int)PersonMode::NONE, myShowDetectors));
220 } else if (customID == NO_DETECTOR) {
221 continue;
222 } else {
224 if (loop == nullptr) {
225 WRITE_ERROR("Unknown inductionLoop '" + customID + "' given as custom detector for actuated tlLogic '" + getID() + "', program '" + getProgramID() + ".");
226 continue;
227 }
228 ilpos = loop->getPosition();
229 inductLoopPosition = length - ilpos;
230 }
231 const double maxGap = getDouble("max-gap:" + lane->getID(), myMaxGap);
232 const double jamThreshold = getDouble("jam-threshold:" + lane->getID(), myJamThreshold);
233 laneInductLoopMap[lane] = loop;
234 inductLoopInfoMap[loop] = (int)myInductLoops.size();
235 myInductLoops.push_back(InductLoopInfo(loop, lane, (int)myPhases.size(), maxGap, jamThreshold));
236
237 if (warn && floor(floor(inductLoopPosition / DEFAULT_LENGTH_WITH_GAP) * myPassingTime) > STEPS2TIME(minDur)) {
238 // warn if the minGap is insufficient to clear vehicles between stop line and detector
239 WRITE_WARNING("At actuated tlLogic '" + getID() + "', minDur " + time2string(minDur) + " is too short for a detector gap of " + toString(inductLoopPosition) + "m.");
240 warn = false;
241 }
242 }
243 }
244 // assign loops to phase index (myInductLoopsForPhase)
245 // 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
246 // greenMinor is ambiguous as vehicles may not be able to drive
247 // Under the following condition we allow actuation from minor link:
248 // check1a : the minor link is minor in all phases
249 // check1b : there is another major link from the same lane in the current phase
250 // (Under these conditions we assume that the minor link is unimportant and traffic is mostly for the major link)
251 //
252 // check1c: when the edge has only one lane, we treat greenMinor as green as there would be no actuation otherwise
253 // check1d: for turnarounds 1b is sufficient and we do not require 1a
254 //
255 // 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
256 //
257 // if a jamThreshold is specificed for the loop, all checks are ignored
258
259 // also assign loops to link index for validation:
260 // check if all links from actuated phases (minDur != maxDur) have an inductionloop in at least one phase
261 const SVCPermissions motorized = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
262 std::map<int, std::set<MSInductLoop*> > linkToLoops;
263 std::set<int> actuatedLinks;
264
265 std::vector<bool> neverMajor(numLinks, true);
266 for (const MSPhaseDefinition* phase : myPhases) {
267 const std::string& state = phase->getState();
268 for (int i = 0; i < numLinks; i++) {
269 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
270 neverMajor[i] = false;
271 }
272 }
273 }
274 std::vector<bool> oneLane(numLinks, false);
275 std::vector<bool> turnaround(numLinks, true);
276 for (int i = 0; i < numLinks; i++) {
277 for (MSLane* lane : getLanesAt(i)) {
278 // only count motorized vehicle lanes
279 int numMotorized = 0;
280 for (MSLane* l : lane->getEdge().getLanes()) {
281 if ((l->getPermissions() & motorized) != 0) {
282 numMotorized++;
283 }
284 }
285 if (numMotorized == 1) {
286 oneLane[i] = true;
287 break;
288 }
289 }
290 for (MSLink* link : getLinksAt(i)) {
291 if (!link->isTurnaround()) {
292 turnaround[i] = false;
293 break;
294 }
295 }
296 }
297
298
299 for (const MSPhaseDefinition* phase : myPhases) {
300 const int phaseIndex = (int)myInductLoopsForPhase.size();
301 std::set<MSInductLoop*> loops;
302 if (phase->isActuated()) {
303 const std::string& state = phase->getState();
304 // collect indices of all green links for the phase
305 std::set<int> greenLinks;
306 // green links that could jam
307 std::set<int> greenLinksPermissive;
308 // collect green links for each induction loops (in this phase)
309 std::map<MSInductLoop*, std::set<int> > loopLinks;
310
311 for (int i = 0; i < numLinks; i++) {
312 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
313 greenLinks.insert(i);
314 actuatedLinks.insert(i);
315 } else if (state[i] == LINKSTATE_TL_GREEN_MINOR) {
316 if (((neverMajor[i] || turnaround[i]) // check1a, 1d
317 && hasMajor(state, getLanesAt(i))) // check1b
318 || oneLane[i]) { // check1c
319 greenLinks.insert(i);
320 if (!turnaround[i]) {
321 actuatedLinks.insert(i);
322 }
323 } else {
324 greenLinksPermissive.insert(i);
325 }
326 }
327#ifdef DEBUG_DETECTORS
328 if (DEBUG_COND) {
329 std::cout << " phase=" << phaseIndex << " i=" << i << " state=" << state[i] << " green=" << greenLinks.count(i) << " oneLane=" << oneLane[i]
330 << " turn=" << turnaround[i] << " loopLanes=";
331 for (MSLane* lane : getLanesAt(i)) {
332 if (laneInductLoopMap.count(lane) != 0) {
333 std::cout << lane->getID() << " ";
334 }
335 }
336 std::cout << "\n";
337 }
338#endif
339 for (MSLane* lane : getLanesAt(i)) {
340 if (laneInductLoopMap.count(lane) != 0) {
341 loopLinks[laneInductLoopMap[lane]].insert(i);
342 }
343 }
344 }
345 for (auto& item : loopLinks) {
346 MSInductLoop* loop = item.first;
347 const InductLoopInfo& info = myInductLoops[inductLoopInfoMap[loop]];
348 const MSLane* loopLane = info.lane;
349 bool usable = true;
350 bool foundUsable = false;
351 // check1
352 for (int j : item.second) {
353 if (greenLinks.count(j) == 0 && (info.jamThreshold <= 0 || greenLinksPermissive.count(j) == 0)) {
354 usable = false;
355#ifdef DEBUG_DETECTORS
356 if (DEBUG_COND) {
357 std::cout << " phase=" << phaseIndex << " check1: loopLane=" << loopLane->getID() << " notGreen=" << j << " oneLane[j]=" << oneLane[j] << "\n";
358 }
359#endif
360 } else {
361 foundUsable = true;
362 }
363 }
364 if (!usable && foundUsable && info.jamThreshold > 0) {
365 // permit green even when the same lane has green and red links (if we have jamDetection)
366 usable = true;
367 }
368 // check2 (skip if we have jam detection)
369 if (usable && info.jamThreshold <= 0) {
370 for (MSLink* link : loopLane->getLinkCont()) {
371 if (link->isTurnaround()) {
372 continue;
373 }
374 const MSLane* next = link->getLane();
375 if (laneInductLoopMap.count(next) != 0) {
376 MSInductLoop* nextLoop = laneInductLoopMap[next];
377 for (int j : loopLinks[nextLoop]) {
378 if (greenLinks.count(j) == 0) {
379 usable = false;
380#ifdef DEBUG_DETECTORS
381 if (DEBUG_COND) std::cout << " phase=" << phaseIndex << " check2: loopLane=" << loopLane->getID()
382 << " nextLane=" << next->getID() << " nextLink=" << j << " nextState=" << state[j] << "\n";
383#endif
384 break;
385 }
386 }
387 }
388 }
389 }
390
391 if (usable) {
392 loops.insert(item.first);
393#ifdef DEBUG_DETECTORS
394 if (DEBUG_COND) {
395 std::cout << " phase=" << phaseIndex << " usableLoops=" << item.first->getID() << " links=" << joinToString(item.second, " ") << "\n";
396 }
397#endif
398 for (int j : item.second) {
399 linkToLoops[j].insert(item.first);
400 }
401 }
402 }
403 if (loops.size() == 0) {
404 WRITE_WARNINGF(TL("At actuated tlLogic '%', actuated phase % has no controlling detector."), getID(), toString(phaseIndex));
405 }
406 }
407#ifdef DEBUG_DETECTORS
408 if (DEBUG_COND) {
409 std::cout << " phase=" << phaseIndex << " loops=" << joinNamedToString(loops, " ") << "\n";
410 }
411 if (DEBUG_COND) {
412 std::cout << " linkToLoops:\n";
413 for (auto item : linkToLoops) {
414 std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
415 }
416 }
417#endif
418 std::vector<InductLoopInfo*> loopInfos;
419 myInductLoopsForPhase.push_back(loopInfos);
420 for (MSInductLoop* loop : loops) {
421 for (InductLoopInfo& loopInfo : myInductLoops) {
422 if (loopInfo.loop == loop) {
423 myInductLoopsForPhase.back().push_back(&loopInfo);
424 loopInfo.servedPhase[phaseIndex] = true;
425 }
426 }
427 }
428 }
429#ifdef DEBUG_DETECTORS
430 if (DEBUG_COND) {
431 std::cout << "final linkToLoops:\n";
432 for (auto item : linkToLoops) {
433 std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
434 }
435 }
436#endif
437 for (int i : actuatedLinks) {
438 if (linkToLoops[i].size() == 0 && myLinks[i].size() > 0
439 && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
440 if (getParameter(myLinks[i].front()->getLaneBefore()->getID()) != NO_DETECTOR) {
441 WRITE_WARNINGF(TL("At actuated tlLogic '%', linkIndex % has no controlling detector."), getID(), toString(i));
442 }
443 }
444 }
445 // parse maximum green times for each link (optional)
446 for (const auto& kv : getParametersMap()) {
447 if (StringUtils::startsWith(kv.first, "linkMaxDur:")) {
448 int link = StringUtils::toInt(kv.first.substr(11));
449 if (link < 0 || link >= myNumLinks) {
450 WRITE_ERROR("Invalid link '" + kv.first.substr(11) + "' given as linkMaxDur parameter for actuated tlLogic '" + getID() + "', program '" + getProgramID() + ".");
451 continue;
452 }
453 if (myLinkMaxGreenTimes.empty()) {
454 myLinkMaxGreenTimes = std::vector<SUMOTime>(myNumLinks, std::numeric_limits<SUMOTime>::max());
455 }
456 myLinkMaxGreenTimes[link] = string2time(kv.second);
457 } else if (StringUtils::startsWith(kv.first, "linkMinDur:")) {
458 int link = StringUtils::toInt(kv.first.substr(11));
459 if (link < 0 || link >= myNumLinks) {
460 WRITE_ERROR("Invalid link '" + kv.first.substr(11) + "' given as linkMinDur parameter for actuated tlLogic '" + getID() + "', program '" + getProgramID() + ".");
461 continue;
462 }
463 if (myLinkMinGreenTimes.empty()) {
464 myLinkMinGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
465 }
466 myLinkMinGreenTimes[link] = string2time(kv.second);
467 }
468 }
469 if (myLinkMaxGreenTimes.size() > 0 || myLinkMinGreenTimes.size() > 0 || mySwitchingRules.size() > 0) {
470 myLinkGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
471 myLinkRedTimes = std::vector<SUMOTime>(myNumLinks, 0);
472 }
473 //std::cout << SIMTIME << " linkMaxGreenTimes=" << toString(myLinkMaxGreenTimes) << "\n";
474}
475
478 step = step < 0 ? myStep : step;
479 const MSPhaseDefinition* p = myPhases[step];
481 ? p->minDuration
482 : TIME2STEPS(evalExpression(myConditions.find("minDur:" + toString(step))->second));
483}
484
487 step = step < 0 ? myStep : step;
488 const MSPhaseDefinition* p = myPhases[step];
490 ? p->maxDuration
491 : TIME2STEPS(evalExpression(myConditions.find("maxDur:" + toString(step))->second));
492}
493
496 step = step < 0 ? myStep : step;
497 const MSPhaseDefinition* p = myPhases[step];
499 ? p->earliestEnd
500 : TIME2STEPS(evalExpression(myConditions.find("earliestEnd:" + toString(step))->second));
501}
502
505 step = step < 0 ? myStep : step;
506 const MSPhaseDefinition* p = myPhases[step];
508 ? p->latestEnd
509 : TIME2STEPS(evalExpression(myConditions.find("latestEnd:" + toString(step))->second));
510}
511
512
513void
516 for (int i = 0; i < (int)myPhases.size(); i++) {
517 MSPhaseDefinition* phase = myPhases[i];
518 const std::string errorSuffix = "' for overiding attribute in phase " + toString(i) + " of tlLogic '" + getID() + "' in program '" + getProgramID() + "'.";
519 if (phase->minDuration == ovrd) {
520 const std::string cond = "minDur:" + toString(i);
521 if (myConditions.count(cond) == 0) {
522 throw ProcessError("Missing condition '" + cond + errorSuffix);
523 }
524 }
525 if (phase->maxDuration == ovrd) {
526 const std::string cond = "maxDur:" + toString(i);
527 if (myConditions.count(cond) == 0) {
528 throw ProcessError("Missing condition '" + cond + errorSuffix);
529 }
530 }
531 if (phase->earliestEnd == ovrd) {
532 const std::string cond = "earliestEnd:" + toString(i);
533 if (myConditions.count(cond) == 0) {
534 throw ProcessError("Missing condition '" + cond + errorSuffix);
535 }
536 }
537 if (phase->latestEnd == ovrd) {
538 const std::string cond = "latestEnd:" + toString(i);
539 if (myConditions.count(cond) == 0) {
540 throw ProcessError("Missing condition '" + cond + errorSuffix);
541 }
542 }
543 }
544}
545
546
547void
549 for (int i = 0; i < (int)myPhases.size(); i++) {
551 MSPhaseDefinition* phase = myPhases[i];
552 std::vector<int> nextPhases = phase->nextPhases;
553 if (nextPhases.size() == 0) {
554 nextPhases.push_back((i + 1) % (int)myPhases.size());
555 }
556 for (int next : nextPhases) {
557 if (next >= 0 && next < (int)myPhases.size()) {
558 const MSPhaseDefinition* nextPhase = myPhases[next];
559 if (nextPhase->earlyTarget != "" || nextPhase->finalTarget != "") {
560 sr.enabled = true;
561 // simplifies later code
562 phase->nextPhases = nextPhases;
563 }
564 }
565 }
566 mySwitchingRules.push_back(sr);
567 }
568}
569
570
573 SUMOTime result = std::numeric_limits<SUMOTime>::max();
574 for (int pI = 0; pI < (int)myPhases.size(); pI++) {
575 const MSPhaseDefinition* phase = myPhases[pI];
576 const std::string& state = phase->getState();
577 for (int i = 0; i < (int)state.size(); i++) {
578 if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
579 for (MSLane* cand : getLanesAt(i)) {
580 if (lane == cand) {
581 if (phase->isActuated()) {
582 result = MIN2(result, getMinDur(pI));
583 }
584 }
585 }
586 }
587 }
588 }
589 return result;
590}
591
592bool
593MSActuatedTrafficLightLogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
594 for (int i = 0; i < (int)state.size(); i++) {
595 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
596 for (MSLane* cand : getLanesAt(i)) {
597 for (MSLane* lane : lanes) {
598 if (lane == cand) {
599 return true;
600 }
601 }
602 }
603 }
604 }
605 return false;
606}
607
608
609// ------------ Switching and setting current rows
610void
613 for (InductLoopInfo& loopInfo : myInductLoops) {
614 loopInfo.loop->setVisible(myShowDetectors);
615 }
616}
617
618
619void
622 for (InductLoopInfo& loopInfo : myInductLoops) {
623 loopInfo.loop->setVisible(false);
624 }
625}
626
627void
629 SUMOTime simStep, int step, SUMOTime stepDuration) {
630 // do not change timing if the phase changes
631 if (step >= 0 && step != myStep) {
632 myStep = step;
634 setTrafficLightSignals(simStep);
635 tlcontrol.get(getID()).executeOnSwitchActions();
636 } else if (step < 0) {
637 // TraCI requested new timing
639 mySwitchCommand = new SwitchCommand(tlcontrol, this, stepDuration + simStep);
641 mySwitchCommand, stepDuration + simStep);
642 myTraCISwitch = true;
643 }
644}
645
646
647void
649 const SUMOTime lastSwitch = t - spentDuration;
650 myStep = step;
651 myPhases[myStep]->myLastSwitch = lastSwitch;
652 const SUMOTime nextSwitch = t + getPhase(step).minDuration - spentDuration;
654 mySwitchCommand = new SwitchCommand(tlcontrol, this, nextSwitch);
656 setTrafficLightSignals(lastSwitch);
657 tlcontrol.get(getID()).executeOnSwitchActions();
658}
659
660
663 // checks if the actual phase should be continued
664 // @note any vehicles which arrived during the previous phases which are now waiting between the detector and the stop line are not
665 // considere here. RiLSA recommends to set minDuration in a way that lets all vehicles pass the detector
668
669 if (myLinkGreenTimes.size() > 0) {
670 // constraints exist, record green time durations for each link
671 const std::string& state = getCurrentPhaseDef().getState();
672 SUMOTime lastDuration = SIMSTEP - myLastTrySwitchTime;
673 for (int i = 0; i < myNumLinks; i++) {
674 if (state[i] == 'G' || state[i] == 'g') {
675 myLinkGreenTimes[i] += lastDuration;
676 } else {
677 myLinkGreenTimes[i] = 0;
678 }
679 if (state[i] == 'r' || state[i] == 'u') {
680 myLinkRedTimes[i] += lastDuration;
681 } else {
682 myLinkRedTimes[i] = 0;
683 }
684 }
685 }
687 // decide the next phase
688 const bool multiTarget = myPhases[myStep]->nextPhases.size() > 1 && myPhases[myStep]->nextPhases.front() >= 0;
689 const int origStep = myStep;
690 int nextStep = myStep;
691 SUMOTime actDuration = now - myPhases[myStep]->myLastSwitch;
692
693 if (mySwitchingRules[myStep].enabled) {
694 const bool mustSwitch = MIN2(getMaxDur() - actDuration, getLatest()) <= 0;
695 nextStep = decideNextPhaseCustom(mustSwitch);
696 } else {
697 // default algorithm
698 const double detectionGap = gapControl();
699#ifdef DEBUG_PHASE_SELECTION
700 if (DEBUG_COND) {
701 std::cout << SIMTIME << " p=" << myStep
702 << " trySwitch dGap=" << (detectionGap == std::numeric_limits<double>::max() ? "inf" : toString(detectionGap))
703 << " multi=" << multiTarget << "\n";
704 }
705#endif
706 if (detectionGap < std::numeric_limits<double>::max() && !multiTarget && !myTraCISwitch) {
707 return duration(detectionGap);
708 }
709 if (multiTarget) {
710 nextStep = decideNextPhase();
711 } else {
712 if (myPhases[myStep]->nextPhases.size() == 1 && myPhases[myStep]->nextPhases.front() >= 0) {
713 nextStep = myPhases[myStep]->nextPhases.front();
714 } else {
715 nextStep = (myStep + 1) % (int)myPhases.size();
716 }
717 }
718 }
719
720 myTraCISwitch = false;
721 if (myLinkMinGreenTimes.size() > 0) {
722 SUMOTime linkMinDur = getLinkMinDuration(getTarget(nextStep));
723 if (linkMinDur > 0) {
724 // for multiTarget, the current phase must be extended but if another
725 // targer is chosen, earlier switching than linkMinDur is possible
726 return multiTarget ? TIME2STEPS(1) : linkMinDur;
727 }
728 }
729 myStep = nextStep;
730 assert(myStep <= (int)myPhases.size());
731 assert(myStep >= 0);
732 //stores the time the phase started
733 const SUMOTime prevStart = myPhases[myStep]->myLastSwitch;
734 if (myStep != origStep) {
735 myPhases[origStep]->myLastEnd = now;
736 myPhases[myStep]->myLastSwitch = now;
737 actDuration = 0;
738 }
739 // activate coloring
740 if ((myShowDetectors || multiTarget) && getCurrentPhaseDef().isGreenPhase()) {
741 for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
742 //std::cout << SIMTIME << " p=" << myStep << " loopinfo=" << loopInfo->loop->getID() << " set lastGreen=" << STEPS2TIME(now) << "\n";
743 if (loopInfo->isJammed()) {
744 loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
745 } else {
746 loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
747 }
748 loopInfo->lastGreenTime = now;
749 }
750 }
751 // set the next event
752#ifdef DEBUG_PHASE_SELECTION
753 if (DEBUG_COND) {
754 std::cout << SIMTIME << " tl=" << getID() << " p=" << myStep
755 << " nextTryMinDur=" << STEPS2TIME(getMinDur() - actDuration)
756 << " nextTryEarliest=" << STEPS2TIME(getEarliest(prevStart)) << "\n";
757 }
758#endif
759 return MAX3(TIME2STEPS(1), getMinDur() - actDuration, getEarliest(prevStart));
760}
761
762
763// ------------ "actuated" algorithm methods
765MSActuatedTrafficLightLogic::duration(const double detectionGap) const {
766 assert(getCurrentPhaseDef().isGreenPhase());
767 assert((int)myPhases.size() > myStep);
768 const SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
769 // ensure that minimum duration is kept
770 SUMOTime newDuration = getMinDur() - actDuration;
771 // try to let the last detected vehicle pass the intersection (duration must be positive)
772 newDuration = MAX3(newDuration, TIME2STEPS(myDetectorGap - detectionGap), SUMOTime(1));
773 // cut the decimal places to ensure that phases always have integer duration
774 if (newDuration % 1000 != 0) {
775 const SUMOTime totalDur = newDuration + actDuration;
776 newDuration = (totalDur / 1000 + 1) * 1000 - actDuration;
777 }
778 // ensure that the maximum duration is not exceeded
779 newDuration = MIN3(newDuration, getMaxDur() - actDuration, getLatest());
780 return newDuration;
781}
782
783
784double
786 //intergreen times should not be lengthend
787 assert((int)myPhases.size() > myStep);
788 double result = std::numeric_limits<double>::max();
790 return result;
791 }
792 // switch off active colors
793 if (myShowDetectors) {
794 for (InductLoopInfo& loopInfo : myInductLoops) {
795 if (loopInfo.lastGreenTime < loopInfo.loop->getLastDetectionTime()) {
796 loopInfo.loop->setSpecialColor(&RGBColor::RED);
797 } else {
798 loopInfo.loop->setSpecialColor(nullptr);
799 }
800 }
801 }
802 if (!getCurrentPhaseDef().isGreenPhase()) {
803 return result; // end current phase
804 }
805
806 // Checks, if the maxDuration is kept. No phase should last longer than maxDuration.
807 SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
808 if (actDuration >= getCurrentPhaseDef().maxDuration || maxLinkDurationReached() || getLatest() == 0) {
809#ifdef DEBUG_PHASE_SELECTION
810 if (DEBUG_COND) {
811 std::cout << SIMTIME << " actDuration=" << STEPS2TIME(actDuration) << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
812 << " maxLinkDurationReached=" << maxLinkDurationReached() << " latest=" << STEPS2TIME(getLatest()) << "\n";
813 }
814#endif
815 return result; // end current phase
816 }
817
818 // now the gapcontrol starts
819 for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
820 MSInductLoop* loop = loopInfo->loop;
821 if (loopInfo->isJammed()) {
822 loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
823 } else {
824 loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
825 }
826 const double actualGap = loop->getTimeSinceLastDetection();
827 if (actualGap < loopInfo->maxGap && !loopInfo->isJammed()) {
828 result = MIN2(result, actualGap);
829 }
830 }
831 return result;
832}
833
834int
836 const auto& cands = myPhases[myStep]->nextPhases;
837 // decide by priority
838 // first target is the default when there is no traffic
839 // @note: to keep the current phase, even when there is no traffic, it must be added to 'next' explicitly
840 int result = cands.front();
841 int maxPrio = 0;
842 SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
843 const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && !maxLinkDurationReached() && getLatest() > 0;
844 if (canExtend) {
845 // consider keeping the current phase until maxDur is reached
846 // (only when there is still traffic in that phase)
847 int currentPrio = getPhasePriority(myStep);
848#ifdef DEBUG_PHASE_SELECTION
849 std::cout << SIMTIME << " p=" << myStep << " loops=" << myInductLoopsForPhase[myStep].size() << " currentPrio=" << currentPrio << "\n";
850#endif
851 if (currentPrio > maxPrio) {
852 result = myStep;
853 maxPrio = currentPrio;
854 }
855 }
856 for (int step : cands) {
857 int target = getTarget(step);
858 int prio = getPhasePriority(target);
859#ifdef DEBUG_PHASE_SELECTION
860 if (DEBUG_COND) {
861 std::cout << SIMTIME << " p=" << myStep << " step=" << step << " target=" << target << " loops=" << myInductLoopsForPhase[target].size() << " prio=" << prio << "\n";
862 }
863#endif
864 if (prio > maxPrio && canExtendLinkGreen(target)) {
865 maxPrio = prio;
866 result = step;
867 }
868 }
869 // prevent starvation in phases that are not direct targets
870 for (const InductLoopInfo& loopInfo : myInductLoops) {
871 int prio = getDetectorPriority(loopInfo);
872 if (prio > maxPrio) {
873 result = cands.front();
874 if (result == myStep) {
875 WRITE_WARNING("At actuated tlLogic '" + getID()
876 + "', starvation at e1Detector '" + loopInfo.loop->getID()
877 + "' which cannot be reached from the default phase " + toString(myStep) + ".");
878 }
879 // use default phase to reach other phases
880#ifdef DEBUG_PHASE_SELECTION
881 if (DEBUG_COND) {
882 std::cout << SIMTIME << " p=" << myStep << " loop=" << loopInfo.loop->getID() << " prio=" << prio << " next=" << result << "\n";
883 }
884#endif
885 break;
886 }
887 }
888 return result;
889}
890
891
892int
894 int origStep = step;
895 // if step is a transition, find the upcoming green phase
896 while (!myPhases[step]->isGreenPhase()) {
897 if (myPhases[step]->nextPhases.size() > 0 && myPhases[step]->nextPhases.front() >= 0) {
898 if (myPhases[step]->nextPhases.size() > 1) {
899 WRITE_WARNING("At actuated tlLogic '" + getID() + "', transition phase " + toString(step) + " should not have multiple next phases");
900 }
901 step = myPhases[step]->nextPhases.front();
902 } else {
903 step = (step + 1) % (int)myPhases.size();
904 }
905 if (step == origStep) {
906 WRITE_WARNING("At actuated tlLogic '" + getID() + "', infinite transition loop from phase " + toString(origStep));
907 return 0;
908 }
909 }
910 return step;
911}
912
913int
915 MSInductLoop* loop = loopInfo.loop;
916 const double actualGap = loop->getTimeSinceLastDetection();
917 if ((actualGap < loopInfo.maxGap && !loopInfo.isJammed())
918 || loopInfo.lastGreenTime < loop->getLastDetectionTime()) {
919 SUMOTime inactiveTime = MSNet::getInstance()->getCurrentTimeStep() - loopInfo.lastGreenTime;
920 // @note. Inactive time could also be tracked regardless of current activity (to increase robustness in case of detection failure
921 if (inactiveTime > myInactiveThreshold) {
922#ifdef DEBUG_PHASE_SELECTION
923 if (DEBUG_COND) {
924 std::cout << " loop=" << loop->getID() << " gap=" << loop->getTimeSinceLastDetection() << " lastGreen=" << STEPS2TIME(loopInfo.lastGreenTime)
925 << " lastDetection=" << STEPS2TIME(loop->getLastDetectionTime()) << " inactive=" << STEPS2TIME(inactiveTime) << "\n";
926 }
927#endif
928 return (int)STEPS2TIME(inactiveTime);
929 } else {
930 // give bonus to detectors that are currently served (if that phase can stil be extended)
931 if (loopInfo.servedPhase[myStep]) {
932 SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
933 const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && getLatest() > 0;
934#ifdef DEBUG_PHASE_SELECTION
935 if (DEBUG_COND) {
936 std::cout << " loop=" << loop->getID()
937 << " actDuration=" << STEPS2TIME(actDuration)
938 << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
939 << " getLatest=" << STEPS2TIME(getLatest())
940 << " canExtend=" << canExtend
941 << "\n";
942 }
943#endif
944 if (canExtend) {
946 } else {
947 return 0;
948 }
949 }
950 return 1;
951 }
952 }
953 return 0;
954}
955
956int
958 int result = 0;
959 for (const InductLoopInfo* loopInfo : myInductLoopsForPhase[step]) {
960 result += getDetectorPriority(*loopInfo);
961 }
962 return result;
963}
964
965
966void
968 myShowDetectors = show;
969 for (InductLoopInfo& loopInfo : myInductLoops) {
970 loopInfo.loop->setVisible(myShowDetectors);
971 }
972}
973
974
975bool
977 if (myLinkMaxGreenTimes.empty()) {
978 return false;
979 }
980 for (int i = 0; i < myNumLinks; i++) {
982 //std::cout << SIMTIME << " maxLinkDurationReached i=" << i << "\n";
983 return true;
984 }
985 }
986 return false;
987}
988
989bool
991 if (myLinkMaxGreenTimes.empty()) {
992 return true;
993 }
994 const std::string& targetState = myPhases[target]->getState();
995 for (int i = 0; i < myNumLinks; i++) {
996 if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i] && (
997 targetState[i] == 'G' || targetState[i] == 'g')) {
998 //std::cout << SIMTIME << " cannotExtendLinkGreen target=" << target << " i=" << i << "\n";
999 return false;
1000 }
1001 }
1002 return true;
1003}
1004
1007 SUMOTime result = 0;
1008 if (target != myStep && myLinkMinGreenTimes.size() > 0) {
1009 const std::string& state = myPhases[myStep]->getState();
1010 const std::string& targetState = myPhases[target]->getState();
1011 for (int i = 0; i < myNumLinks; i++) {
1013 && (state[i] == 'G' || state[i] == 'g')
1014 && !(targetState[i] == 'G' || targetState[i] == 'g')) {
1015 result = MAX2(result, myLinkMinGreenTimes[i] - myLinkGreenTimes[i]);
1016 //std::cout << SIMTIME << " getLinkMinDuration myStep=" << myStep << " target=" << target << " i=" << i
1017 // << " greenTime=" << STEPS2TIME(myLinkGreenTimes[i]) << " min=" << STEPS2TIME(myLinkMinGreenTimes[i]) << " result=" << STEPS2TIME(result) << "\n";
1018 }
1019 }
1020 }
1021 return result;
1022}
1023
1024int
1026 for (int next : getCurrentPhaseDef().nextPhases) {
1027 const MSPhaseDefinition* phase = myPhases[next];
1028 const std::string& condition = mustSwitch ? phase->finalTarget : phase->earlyTarget;
1029 //std::cout << SIMTIME << " mustSwitch=" << mustSwitch << " condition=" << condition << "\n";
1030 if (condition != "" && evalExpression(condition)) {
1031 return next;
1032 }
1033 }
1034 return mustSwitch ? getCurrentPhaseDef().nextPhases.back() : myStep;
1035}
1036
1037
1038double
1039MSActuatedTrafficLightLogic::evalExpression(const std::string& condition) const {
1040 const size_t bracketOpen = condition.find('(');
1041 if (bracketOpen != std::string::npos) {
1042 // find matching closing bracket
1043 size_t bracketClose = std::string::npos;
1044 int open = 1;
1045 for (size_t i = bracketOpen + 1; i < condition.size(); i++) {
1046 if (condition[i] == '(') {
1047 open++;
1048 } else if (condition[i] == ')') {
1049 open--;
1050 if (open == 0) {
1051 bracketClose = i;
1052 break;
1053 }
1054 }
1055 }
1056 if (bracketClose == std::string::npos) {
1057 throw ProcessError("Unmatched parentheses in condition " + condition + "'");
1058 }
1059 std::string cond2 = condition;
1060 const std::string inBracket = condition.substr(bracketOpen + 1, bracketClose - bracketOpen - 1);
1061 double bracketVal = evalExpression(inBracket);
1062 cond2.replace(bracketOpen, bracketClose - bracketOpen + 1, toString(bracketVal));
1063 try {
1064 return evalExpression(cond2);
1065 } catch (ProcessError& e) {
1066 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1067 }
1068 }
1069 std::vector<std::string> tokens = StringTokenizer(condition).getVector();
1070 //std::cout << SIMTIME << " tokens(" << tokens.size() << ")=" << toString(tokens) << "\n";
1071 if (tokens.size() == 0) {
1072 throw ProcessError("Invalid empty condition '" + condition + "'");
1073 } else if (tokens.size() == 1) {
1074 try {
1075 return evalAtomicExpression(tokens[0]);
1076 } catch (ProcessError& e) {
1077 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1078 }
1079 } else if (tokens.size() == 2) {
1080 if (tokens[0] == "not") {
1081 try {
1082 return evalAtomicExpression(tokens[1]) == 0. ? 1. : 0.;
1083 } catch (ProcessError& e) {
1084 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1085 }
1086 } else {
1087 throw ProcessError("Unsupported condition '" + condition + "'");
1088 }
1089 } else if (tokens.size() == 3) {
1090 // infix expression
1091 const double a = evalAtomicExpression(tokens[0]);
1092 const double b = evalAtomicExpression(tokens[2]);
1093 const std::string& o = tokens[1];
1094 //std::cout << SIMTIME << " o=" << o << " a=" << a << " b=" << b << "\n";
1095 try {
1096 return evalTernaryExpression(a, o, b, condition);
1097 } catch (ProcessError& e) {
1098 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1099 }
1100 } else {
1101 const int iEnd = (int)tokens.size() - 1;
1102 for (const std::string& o : OPERATOR_PRECEDENCE) {
1103 for (int i = 1; i < iEnd; i++) {
1104 if (tokens[i] == o) {
1105 try {
1106 const double val = evalTernaryExpression(
1107 evalAtomicExpression(tokens[i - 1]), o,
1108 evalAtomicExpression(tokens[i + 1]), condition);
1109 std::vector<std::string> newTokens(tokens.begin(), tokens.begin() + (i - 1));
1110 newTokens.push_back(toString(val));
1111 newTokens.insert(newTokens.end(), tokens.begin() + (i + 2), tokens.end());
1112 return evalExpression(toString(newTokens));
1113 } catch (ProcessError& e) {
1114 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1115 }
1116 }
1117 }
1118 }
1119 throw ProcessError("Parsing expressions with " + toString(tokens.size()) + " elements ('" + condition + "') is not supported");
1120 }
1121 return true;
1122}
1123
1124double
1125MSActuatedTrafficLightLogic::evalTernaryExpression(double a, const std::string& o, double b, const std::string& condition) const {
1126 if (o == "=" || o == "==") {
1127 return (double)(a == b);
1128 } else if (o == "<") {
1129 return (double)(a < b);
1130 } else if (o == ">") {
1131 return (double)(a > b);
1132 } else if (o == "<=") {
1133 return (double)(a <= b);
1134 } else if (o == ">=") {
1135 return (double)(a >= b);
1136 } else if (o == "!=") {
1137 return (double)(a != b);
1138 } else if (o == "or" || o == "||") {
1139 return (double)(a || b);
1140 } else if (o == "and" || o == "&&") {
1141 return (double)(a && b);
1142 } else if (o == "+") {
1143 return a + b;
1144 } else if (o == "-") {
1145 return a - b;
1146 } else if (o == "*") {
1147 return a * b;
1148 } else if (o == "/") {
1149 if (b == 0) {
1150 WRITE_ERROR("Division by 0 in condition '" + condition + "'");
1151 return 0;
1152 }
1153 return a / b;
1154 } else if (o == "%") {
1155 return fmod(a, b);
1156 } else if (o == "**" || o == "^") {
1157 return pow(a, b);
1158 } else {
1159 throw ProcessError("Unsupported operator '" + o + "' in condition '" + condition + "'");
1160 }
1161}
1162
1163double
1164MSActuatedTrafficLightLogic::evalCustomFunction(const std::string& fun, const std::string& arg) const {
1165 std::vector<std::string> args = StringTokenizer(arg, ",").getVector();
1166 const Function& f = myFunctions.find(fun)->second;
1167 if ((int)args.size() != f.nArgs) {
1168 throw ProcessError("Function '" + fun + "' requires " + toString(f.nArgs) + " arguments but " + toString(args.size()) + " were given");
1169 }
1170 std::vector<double> args2;
1171 for (auto a : args) {
1172 args2.push_back(evalExpression(a));
1173 }
1174 myStack.push_back(myStack.back());
1175 myStack.back()["$0"] = 0;
1176 for (int i = 0; i < (int)args2.size(); i++) {
1177 myStack.back()["$" + toString(i + 1)] = args2[i];
1178 }
1179 try {
1180 ConditionMap empty;
1182 } catch (ProcessError& e) {
1183 throw ProcessError("Error when evaluating function '" + fun + "' with args '" + joinToString(args2, ",") + "' (" + e.what() + ")");
1184 }
1185 double result = myStack.back()["$0"];
1186 myStack.pop_back();
1187 return result;
1188}
1189
1190
1191void
1192MSActuatedTrafficLightLogic::executeAssignments(const AssignmentMap& assignments, ConditionMap& conditions, const ConditionMap& forbidden) const {
1193 for (const auto& assignment : assignments) {
1194 if (evalExpression(std::get<1>(assignment))) {
1195 const std::string& id = std::get<0>(assignment);
1196 const double val = evalExpression(std::get<2>(assignment));
1197 ConditionMap::iterator it = conditions.find(id);
1198 if (it != conditions.end()) {
1199 it->second = toString(val);
1200 } else if (forbidden.find(id) != forbidden.end()) {
1201 throw ProcessError("Modifying global condition '" + id + "' is forbidden");
1202 } else {
1203 myStack.back()[id] = val;
1204 }
1205 }
1206 }
1207}
1208
1209
1210double
1212 if (expr.size() == 0) {
1213 throw ProcessError("Invalid empty expression");
1214 } else if (expr[0] == '!') {
1215 return evalAtomicExpression(expr.substr(1)) == 0. ? 1. : 0.;
1216 } else if (expr[0] == '-') {
1217 return -evalAtomicExpression(expr.substr(1));
1218 } else {
1219 // check for 'operator:'
1220 const size_t pos = expr.find(':');
1221 if (pos == std::string::npos) {
1222 auto it = myConditions.find(expr);
1223 if (it != myConditions.end()) {
1224 // symbol lookup
1225 return evalExpression(it->second);
1226 } else {
1227 // look at stack
1228 auto it2 = myStack.back().find(expr);
1229 if (it2 != myStack.back().end()) {
1230 return it2->second;
1231 }
1232 // must be a number
1233 return StringUtils::toDouble(expr);
1234 }
1235 } else {
1236 const std::string fun = expr.substr(0, pos);
1237 const std::string arg = expr.substr(pos + 1);
1238 if (fun == "z") {
1239 return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection();
1240 } else if (fun == "a") {
1241 try {
1242 return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection() == 0;
1243 } catch (ProcessError&) {
1244 return retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(arg, expr, true)->getCurrentVehicleNumber();
1245 }
1246 } else if (fun == "g" || fun == "r") {
1247 try {
1248 int linkIndex = StringUtils::toInt(arg);
1249 if (linkIndex >= 0 && linkIndex < myNumLinks) {
1250 const std::vector<SUMOTime>& times = fun == "g" ? myLinkGreenTimes : myLinkRedTimes;
1251 if (times.empty()) {
1252 return 0;
1253 }
1255 // times are only updated at the start of a phase where
1256 // switching is possible (i.e. not during minDur).
1257 // If somebody is looking at those values in the tracker
1258 // this would be confusing
1259 const LinkState ls = getCurrentPhaseDef().getSignalState(linkIndex);
1260 if ((fun == "g" && (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR))
1261 || (fun == "r" && (ls == LINKSTATE_TL_RED || ls == LINKSTATE_TL_REDYELLOW))) {
1262 const SUMOTime currentGreen = SIMSTEP - myLastTrySwitchTime;
1263 return STEPS2TIME(times[linkIndex] + currentGreen);
1264 } else {
1265 return 0;
1266 }
1267 } else {
1268 return STEPS2TIME(times[linkIndex]);
1269 }
1270 }
1271 } catch (NumberFormatException&) { }
1272 throw ProcessError("Invalid link index '" + arg + "' in expression '" + expr + "'");
1273 } else if (fun == "c") {
1274 return STEPS2TIME(getTimeInCycle());
1275 } else {
1276 if (myFunctions.find(fun) == myFunctions.end()) {
1277 throw ProcessError("Unsupported function '" + fun + "' in expression '" + expr + "'");
1278 }
1279 return evalCustomFunction(fun, arg);
1280 }
1281 }
1282 }
1283}
1284
1285
1286std::map<std::string, double>
1288 std::map<std::string, double> result;
1289 for (auto li : myInductLoops) {
1290 result[li.loop->getID()] = li.loop->getOccupancy() > 0 ? 1 : 0;
1291 }
1292 for (auto loop : myExtraLoops) {
1293 result[loop->getID()] = loop->getOccupancy() > 0 ? 1 : 0;
1294 }
1295 for (auto loop : myExtraE2) {
1296 result[loop->getID()] = loop->getCurrentVehicleNumber();
1297 }
1298 return result;
1299}
1300
1301std::map<std::string, double>
1303 std::map<std::string, double> result;
1304 for (auto item : myConditions) {
1305 if (myListedConditions.count(item.first) != 0) {
1306 try {
1307 result[item.first] = evalExpression(item.second);
1308 } catch (ProcessError& e) {
1309 WRITE_ERROR("Error when retrieving conditions '" + item.first + "' for tlLogic '" + getID() + "' (" + e.what() + ")");
1310 }
1311 }
1312 }
1313 return result;
1314}
1315
1316const std::string
1317MSActuatedTrafficLightLogic::getParameter(const std::string& key, const std::string defaultValue) const {
1318 if (StringUtils::startsWith(key, "condition.")) {
1319 const std::string cond = key.substr(10);
1320 auto it = myConditions.find(cond);
1321 if (it != myConditions.end()) {
1322 return toString(evalExpression(it->second));
1323 } else {
1324 throw InvalidArgument("Unknown condition '" + cond + "' for actuated traffic light '" + getID() + "'");
1325 }
1326 } else {
1327 return MSSimpleTrafficLightLogic::getParameter(key, defaultValue);
1328 }
1329}
1330
1331void
1332MSActuatedTrafficLightLogic::setParameter(const std::string& key, const std::string& value) {
1333 // some pre-defined parameters can be updated at runtime
1334 if (key == "detector-gap" || key == "passing-time" || key == "file" || key == "freq" || key == "vTypes"
1335 || StringUtils::startsWith(key, "linkMaxDur")
1336 || StringUtils::startsWith(key, "linkMinDur")) {
1337 throw InvalidArgument(key + " cannot be changed dynamically for actuated traffic light '" + getID() + "'");
1338 } else if (key == "max-gap") {
1340 // overwrite custom values
1341 for (InductLoopInfo& loopInfo : myInductLoops) {
1342 loopInfo.maxGap = myMaxGap;
1343 }
1344 Parameterised::setParameter(key, value);
1345 } else if (StringUtils::startsWith(key, "max-gap:")) {
1346 const std::string laneID = key.substr(8);
1347 for (InductLoopInfo& loopInfo : myInductLoops) {
1348 if (loopInfo.lane->getID() == laneID) {
1349 loopInfo.maxGap = StringUtils::toDouble(value);
1350 Parameterised::setParameter(key, value);
1351 return;
1352 }
1353 }
1354 throw InvalidArgument("Invalid lane '" + laneID + "' in key '" + key + "' for actuated traffic light '" + getID() + "'");
1355 } else if (key == "jam-threshold") {
1357 // overwrite custom values
1358 for (InductLoopInfo& loopInfo : myInductLoops) {
1359 loopInfo.jamThreshold = myJamThreshold;
1360 }
1361 Parameterised::setParameter(key, value);
1362 } else if (StringUtils::startsWith(key, "jam-threshold:")) {
1363 const std::string laneID = key.substr(14);
1364 for (InductLoopInfo& loopInfo : myInductLoops) {
1365 if (loopInfo.lane->getID() == laneID) {
1366 loopInfo.jamThreshold = StringUtils::toDouble(value);
1367 Parameterised::setParameter(key, value);
1368 return;
1369 }
1370 }
1371 throw InvalidArgument("Invalid lane '" + laneID + "' in key '" + key + "' for actuated traffic light '" + getID() + "'");
1372 } else if (key == "show-detectors") {
1374 Parameterised::setParameter(key, value);
1375 for (InductLoopInfo& loopInfo : myInductLoops) {
1376 loopInfo.loop->setVisible(myShowDetectors);
1377 }
1378 } else if (key == "inactive-threshold") {
1380 Parameterised::setParameter(key, value);
1381 } else {
1383 }
1384}
1385
1386
1387/****************************************************************************/
long long int SUMOTime
Definition: GUI.h:36
#define DEFAULT_DETECTOR_GAP
#define DEFAULT_MAX_GAP
#define DEFAULT_PASSING_TIME
#define DEFAULT_LENGTH_WITH_GAP
#define NO_DETECTOR
#define DEBUG_COND
#define DEFAULT_INACTIVE_THRESHOLD
#define DEFAULT_CURRENT_PRIORITY
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:266
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:274
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:265
#define TL(string)
Definition: MsgHandler.h:282
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:54
#define SIMSTEP
Definition: SUMOTime.h:60
#define SIMTIME
Definition: SUMOTime.h:61
#define TIME2STEPS(x)
Definition: SUMOTime.h:56
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
The right-of-way state of a link between two lanes used when constructing a NBTrafficLightLogic,...
@ LINKSTATE_TL_REDYELLOW
The link has red light (must brake) but indicates upcoming green.
@ LINKSTATE_TL_GREEN_MAJOR
The link has green light, may pass.
@ LINKSTATE_TL_RED
The link has red light (must brake)
@ LINKSTATE_TL_GREEN_MINOR
The link has green light, has to brake.
T MIN3(T a, T b, T c)
Definition: StdDefs.h:84
T MIN2(T a, T b)
Definition: StdDefs.h:71
T MAX2(T a, T b)
Definition: StdDefs.h:77
T MAX3(T a, T b, T c)
Definition: StdDefs.h:91
std::string joinNamedToString(const std::set< T *, C > &ns, const T_BETWEEN &between)
Definition: ToString.h:316
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition: ToString.h:282
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
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.
FunctionMap myFunctions
The loaded functions.
double myJamThreshold
The minimum continuous occupancy time to mark a detector as jammed.
double myMaxGap
The maximum gap to check in seconds.
const std::string getParameter(const std::string &key, const std::string defaultValue="") const override
try to get the value of the given parameter (including prefixed parameters)
std::vector< SwitchingRules > mySwitchingRules
std::vector< std::map< std::string, double > > myStack
The function call stack;.
double evalAtomicExpression(const std::string &expr) const
evaluate atomic expression
int getTarget(int step)
get the green phase following step
SUMOTime trySwitch() override
Switches to the next phase.
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
std::vector< const MSInductLoop * > myExtraLoops
extra loops for output/tracking
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
MSActuatedTrafficLightLogic(MSTLLogicControl &tlcontrol, const std::string &id, const std::string &programID, const SUMOTime offset, const MSSimpleTrafficLightLogic::Phases &phases, int step, SUMOTime delay, const Parameterised::Map &parameter, const std::string &basePath, const ConditionMap &conditions=ConditionMap(), const AssignmentMap &assignments=AssignmentMap(), const FunctionMap &functions=FunctionMap())
Constructor.
void loadState(MSTLLogicControl &tlcontrol, SUMOTime t, int step, SUMOTime spentDuration) override
restores the tls state
SUMOTime getMaxDur(int step=-1) const override
AssignmentMap myAssignments
The condition assignments.
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:...
std::vector< std::tuple< std::string, std::string, std::string > > AssignmentMap
std::set< std::string > myListedConditions
the conditions which shall be listed in GUITLLogicPhasesTrackerWindow
double evalExpression(const std::string &condition) const
evaluate custom switching condition
std::vector< SUMOTime > myLinkMinGreenTimes
minimum consecutive time that the given link must remain green
void changeStepAndDuration(MSTLLogicControl &tlcontrol, SUMOTime simStep, int step, SUMOTime stepDuration) override
Changes the current phase and her duration.
static const std::vector< std::string > OPERATOR_PRECEDENCE
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
SUMOTime getMinDur(int step=-1) const override
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 decideNextPhaseCustom(bool mustSwitch)
select among candidate phases based on detector states and custom switching rules
double evalTernaryExpression(double a, const std::string &o, double b, const std::string &condition) const
evaluate atomic expression
void executeAssignments(const AssignmentMap &assignments, ConditionMap &conditions, const ConditionMap &forbidden=ConditionMap()) const
execute assignemnts of the logic or a custom function
bool myTraCISwitch
whether the next switch time was requested via TraCI
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.
void activateProgram() override
called when switching programs
std::vector< InductLoopInfo > myInductLoops
bool maxLinkDurationReached()
whether the current phase cannot be continued due to linkMaxDur constraints
double evalCustomFunction(const std::string &fun, const std::string &arg) const
evaluate function expression
void initAttributeOverride()
initialize custom switching rules
std::map< std::string, double > getConditions() const override
return all named conditions defined for this traffic light
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< const MSE2Collector * > myExtraE2
SUMOTime getEarliestEnd(int step=-1) const override
std::map< std::string, Function > FunctionMap
std::map< std::string, double > getDetectorStates() const override
retrieve all detectors used by this program
void setParameter(const std::string &key, const std::string &value) override
Sets a parameter and updates internal constants.
std::vector< SUMOTime > myLinkGreenTimes
consecutive time that the given link index has been green
SUMOTime getLatestEnd(int step=-1) const override
std::string myFile
The output file for generated detectors.
ConditionMap myConditions
The custom switching conditions.
void init(NLDetectorBuilder &nb) override
Initialises the tls with information about incoming lanes.
int decideNextPhase()
select among 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 interval, SUMOTime begin=-1)
Adds a detector/output combination into the containers.
A road/street connecting two junctions.
Definition: MSEdge.h:77
const std::vector< MSLane * > & getLanes() const
Returns this edge's lanes.
Definition: MSEdge.h:168
double getSpeedLimit() const
Returns the speed limit of the edge @caution The speed limit of the first lane is retured; should pro...
Definition: MSEdge.cpp:1056
virtual void addEvent(Command *operation, SUMOTime execTimeStep=-1)
Adds an Event.
static bool gUseMesoSim
Definition: MSGlobals.h:103
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:99
virtual void setSpecialColor(const RGBColor *)
allows for special color in the gui version
Definition: MSInductLoop.h:320
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:84
const std::vector< IncomingLaneInfo > & getIncomingLanes() const
Definition: MSLane.h:879
double getLength() const
Returns the lane's length.
Definition: MSLane.h:575
MSLane * getLogicalPredecessorLane() const
get the most likely precedecessor lane (sorted using by_connections_to_sorter). The result is cached ...
Definition: MSLane.cpp:2866
MSEdge & getEdge() const
Returns the lane's edge.
Definition: MSLane.h:713
const std::vector< MSLink * > & getLinkCont() const
returns the container with all links !!!
Definition: MSLane.h:675
MSDetectorControl & getDetectorControl()
Returns the detector control.
Definition: MSNet.h:442
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:183
MSEventControl * getBeginOfTimestepEvents()
Returns the event control for events executed at the begin of a time step.
Definition: MSNet.h:472
SUMOTime getCurrentTimeStep() const
Returns the current simulation step.
Definition: MSNet.h:321
The definition of a single phase of a tls logic.
SUMOTime maxDuration
The maximum duration of the phase.
LinkState getSignalState(int pos) const
Returns the state of the tls signal at the given position.
bool isActuated() const
static const SUMOTime OVERRIDE_DURATION
SUMOTime latestEnd
The maximum time within the cycle for switching (for coordinated actuation)
SUMOTime minDuration
The minimum duration of the phase.
const std::string & getState() const
Returns the state within this phase.
std::vector< int > nextPhases
The index of the phase that suceeds this one (or -1)
std::string finalTarget
The condition expression for switching into this phase when the active phase must end.
std::string earlyTarget
The condition expression for an early switch into this phase.
SUMOTime earliestEnd
The minimum time within the cycle for switching (for coordinated actuation)
A fixed traffic light logic.
SUMOTime getLatest() const
the maximum duration for keeping the current phase when considering 'latestEnd'
Phases myPhases
The list of phases this logic uses.
const MSPhaseDefinition & getPhase(int givenstep) const override
Returns the definition of the phase from the given position within the plan.
SUMOTime getEarliest(SUMOTime prevStart) const
the minimum duration for keeping the current phase when considering 'earliestEnd'
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const override
gets a parameter
virtual void setParameter(const std::string &key, const std::string &value) override
Sets a parameter and updates internal constants.
const MSPhaseDefinition & getCurrentPhaseDef() const override
Returns the definition of the current phase.
A class that stores and controls tls and switching of their programs.
TLSLogicVariants & get(const std::string &id) const
Returns the variants of a named tls.
Class realising the switch between the traffic light phases.
void deschedule(MSTrafficLightLogic *tlLogic)
Marks this swicth as invalid (if the phase duration has changed, f.e.)
const LaneVector & getLanesAt(int i) const
Returns the list of lanes 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()
SUMOTime getTimeInCycle() const
return time within the current cycle
const std::string & getProgramID() const
Returns this tl-logic's id.
LaneVectorVector myLanes
The list of LaneVectors; each vector contains the incoming lanes that belong to the same link index.
SwitchCommand * mySwitchCommand
The current switch command.
int myNumLinks
number of controlled links
virtual void activateProgram()
called when switching programs
bool setTrafficLightSignals(SUMOTime t) const
Applies the current signal states to controlled links.
std::vector< MSPhaseDefinition * > Phases
Definition of a list of phases, being the junction logic.
const LinkVector & getLinksAt(int i) const
Returns the list of links that are controlled by the signals at the given position.
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.
Builds detectors for microsim.
virtual MSDetectorFileOutput * createInductLoop(const std::string &id, MSLane *lane, double pos, double length, const std::string name, const std::string &vTypes, const std::string &nextEdges, int detectPersons, bool show)
Creates an instance of an e1 detector using the given values.
const std::string & getID() const
Returns the id.
Definition: Named.h:74
T get(const std::string &id) const
Retrieves an item.
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:59
std::map< std::string, std::string > Map
parameters map
Definition: Parameterised.h:45
double getDouble(const std::string &key, const double defaultValue) const
Returns the value for a given key converted to a double.
const Parameterised::Map & getParametersMap() const
Returns the inner key/value map.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
bool knowsParameter(const std::string &key) const
Returns whether the parameter is known.
static const RGBColor ORANGE
Definition: RGBColor.h:191
static const RGBColor GREEN
Definition: RGBColor.h:186
static const RGBColor RED
named colors
Definition: RGBColor.h:185
std::vector< std::string > getVector()
return vector of strings
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