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