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-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 WRITE_ERRORF(TL("Unknown inductionLoop '%' given as custom detector for actuated tlLogic '%', program '%."), customID, getID(), getProgramID());
230 continue;
231 }
232 ilpos = loop->getPosition();
233 inductLoopPosition = length - ilpos;
234 }
235 const double maxGap = getDouble("max-gap:" + lane->getID(), myMaxGap);
236 const double jamThreshold = getDouble("jam-threshold:" + lane->getID(), myJamThreshold);
237 laneInductLoopMap[lane] = loop;
238 inductLoopInfoMap[loop] = (int)myInductLoops.size();
239 myInductLoops.push_back(InductLoopInfo(loop, lane, (int)myPhases.size(), maxGap, jamThreshold));
240
241 if (warn && floor(floor(inductLoopPosition / defaultLength) * myPassingTime) > STEPS2TIME(minDur)) {
242 // warn if the minGap is insufficient to clear vehicles between stop line and detector
243 WRITE_WARNINGF(TL("At actuated tlLogic '%', minDur % is too short for a detector gap of %m."), getID(), time2string(minDur), toString(inductLoopPosition));
244 warn = false;
245 }
246 }
247 }
248 // assign loops to phase index (myInductLoopsForPhase)
249 // 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
250 // greenMinor is ambiguous as vehicles may not be able to drive
251 // Under the following condition we allow actuation from minor link:
252 // check1a : the minor link is minor in all phases
253 // check1b : there is another major link from the same lane in the current phase
254 // (Under these conditions we assume that the minor link is unimportant and traffic is mostly for the major link)
255 //
256 // check1c: when the edge has only one lane, we treat greenMinor as green as there would be no actuation otherwise
257 // check1d: for turnarounds 1b is sufficient and we do not require 1a
258 //
259 // 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
260 //
261 // if a jamThreshold is specificed for the loop, all checks are ignored
262
263 // also assign loops to link index for validation:
264 // check if all links from actuated phases (minDur != maxDur) have an inductionloop in at least one phase
265 const SVCPermissions motorized = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
266 std::map<int, std::set<MSInductLoop*> > linkToLoops;
267 std::set<int> actuatedLinks;
268
269 std::vector<bool> neverMajor(numLinks, true);
270 for (const MSPhaseDefinition* phase : myPhases) {
271 const std::string& state = phase->getState();
272 for (int i = 0; i < numLinks; i++) {
273 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
274 neverMajor[i] = false;
275 }
276 }
277 }
278 std::vector<bool> oneLane(numLinks, false);
279 std::vector<bool> turnaround(numLinks, true);
280 for (int i = 0; i < numLinks; i++) {
281 for (MSLane* lane : getLanesAt(i)) {
282 // only count motorized vehicle lanes
283 int numMotorized = 0;
284 for (MSLane* l : lane->getEdge().getLanes()) {
285 if ((l->getPermissions() & motorized) != 0) {
286 numMotorized++;
287 }
288 }
289 if (numMotorized == 1) {
290 oneLane[i] = true;
291 break;
292 }
293 }
294 for (MSLink* link : getLinksAt(i)) {
295 if (!link->isTurnaround()) {
296 turnaround[i] = false;
297 break;
298 }
299 }
300 }
301
302
303 for (const MSPhaseDefinition* phase : myPhases) {
304 const int phaseIndex = (int)myInductLoopsForPhase.size();
305 std::set<MSInductLoop*> loops;
306 if (phase->isActuated()) {
307 const std::string& state = phase->getState();
308 // collect indices of all green links for the phase
309 std::set<int> greenLinks;
310 // green links that could jam
311 std::set<int> greenLinksPermissive;
312 // collect green links for each induction loops (in this phase)
313 std::map<MSInductLoop*, std::set<int> > loopLinks;
314
315 for (int i = 0; i < numLinks; i++) {
316 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
317 greenLinks.insert(i);
318 actuatedLinks.insert(i);
319 } else if (state[i] == LINKSTATE_TL_GREEN_MINOR) {
320 if (((neverMajor[i] || turnaround[i]) // check1a, 1d
321 && hasMajor(state, getLanesAt(i))) // check1b
322 || oneLane[i]) { // check1c
323 greenLinks.insert(i);
324 if (!turnaround[i]) {
325 actuatedLinks.insert(i);
326 }
327 } else {
328 greenLinksPermissive.insert(i);
329 }
330 }
331#ifdef DEBUG_DETECTORS
332 if (DEBUG_COND) {
333 std::cout << " phase=" << phaseIndex << " i=" << i << " state=" << state[i] << " green=" << greenLinks.count(i) << " oneLane=" << oneLane[i]
334 << " turn=" << turnaround[i] << " loopLanes=";
335 for (MSLane* lane : getLanesAt(i)) {
336 if (laneInductLoopMap.count(lane) != 0) {
337 std::cout << lane->getID() << " ";
338 }
339 }
340 std::cout << "\n";
341 }
342#endif
343 for (MSLane* lane : getLanesAt(i)) {
344 if (laneInductLoopMap.count(lane) != 0) {
345 loopLinks[laneInductLoopMap[lane]].insert(i);
346 }
347 }
348 }
349 for (auto& item : loopLinks) {
350 MSInductLoop* loop = item.first;
351 const InductLoopInfo& info = myInductLoops[inductLoopInfoMap[loop]];
352 const MSLane* loopLane = info.lane;
353 bool usable = true;
354 bool foundUsable = false;
355 // check1
356 for (int j : item.second) {
357 if (greenLinks.count(j) == 0 && (info.jamThreshold <= 0 || greenLinksPermissive.count(j) == 0)) {
358 usable = false;
359#ifdef DEBUG_DETECTORS
360 if (DEBUG_COND) {
361 std::cout << " phase=" << phaseIndex << " check1: loopLane=" << loopLane->getID() << " notGreen=" << j << " oneLane[j]=" << oneLane[j] << "\n";
362 }
363#endif
364 } else {
365 foundUsable = true;
366 }
367 }
368 if (!usable && foundUsable && info.jamThreshold > 0) {
369 // permit green even when the same lane has green and red links (if we have jamDetection)
370 usable = true;
371 }
372 // check2 (skip if we have jam detection)
373 if (usable && info.jamThreshold <= 0) {
374 for (MSLink* link : loopLane->getLinkCont()) {
375 if (link->isTurnaround()) {
376 continue;
377 }
378 const MSLane* next = link->getLane();
379 if (laneInductLoopMap.count(next) != 0) {
380 MSInductLoop* nextLoop = laneInductLoopMap[next];
381 for (int j : loopLinks[nextLoop]) {
382 if (greenLinks.count(j) == 0) {
383 usable = false;
384#ifdef DEBUG_DETECTORS
385 if (DEBUG_COND) std::cout << " phase=" << phaseIndex << " check2: loopLane=" << loopLane->getID()
386 << " nextLane=" << next->getID() << " nextLink=" << j << " nextState=" << state[j] << "\n";
387#endif
388 break;
389 }
390 }
391 }
392 }
393 }
394
395 if (usable) {
396 loops.insert(item.first);
397#ifdef DEBUG_DETECTORS
398 if (DEBUG_COND) {
399 std::cout << " phase=" << phaseIndex << " usableLoops=" << item.first->getID() << " links=" << joinToString(item.second, " ") << "\n";
400 }
401#endif
402 for (int j : item.second) {
403 linkToLoops[j].insert(item.first);
404 }
405 }
406 }
407 if (loops.size() == 0) {
408 WRITE_WARNINGF(TL("At actuated tlLogic '%', actuated phase % has no controlling detector."), getID(), toString(phaseIndex));
409 }
410 }
411#ifdef DEBUG_DETECTORS
412 if (DEBUG_COND) {
413 std::cout << " phase=" << phaseIndex << " loops=" << joinNamedToString(loops, " ") << "\n";
414 }
415 if (DEBUG_COND) {
416 std::cout << " linkToLoops:\n";
417 for (auto item : linkToLoops) {
418 std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
419 }
420 }
421#endif
422 std::vector<InductLoopInfo*> loopInfos;
423 myInductLoopsForPhase.push_back(loopInfos);
424 for (MSInductLoop* loop : loops) {
425 for (InductLoopInfo& loopInfo : myInductLoops) {
426 if (loopInfo.loop == loop) {
427 myInductLoopsForPhase.back().push_back(&loopInfo);
428 loopInfo.servedPhase[phaseIndex] = true;
429 }
430 }
431 }
432 }
433#ifdef DEBUG_DETECTORS
434 if (DEBUG_COND) {
435 std::cout << "final linkToLoops:\n";
436 for (auto item : linkToLoops) {
437 std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
438 }
439 }
440#endif
441 for (int i : actuatedLinks) {
442 if (linkToLoops[i].size() == 0 && myLinks[i].size() > 0
443 && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
444 if (getParameter(myLinks[i].front()->getLaneBefore()->getID()) != NO_DETECTOR) {
445 WRITE_WARNINGF(TL("At actuated tlLogic '%', linkIndex % has no controlling detector."), getID(), toString(i));
446 }
447 }
448 }
449 // parse maximum green times for each link (optional)
450 for (const auto& kv : getParametersMap()) {
451 if (StringUtils::startsWith(kv.first, "linkMaxDur:")) {
452 int link = StringUtils::toInt(kv.first.substr(11));
453 if (link < 0 || link >= myNumLinks) {
454 WRITE_ERRORF(TL("Invalid link '%' given as linkMaxDur parameter for actuated tlLogic '%', program '%."), kv.first.substr(11), getID(), getProgramID());
455 continue;
456 }
457 if (myLinkMaxGreenTimes.empty()) {
458 myLinkMaxGreenTimes = std::vector<SUMOTime>(myNumLinks, std::numeric_limits<SUMOTime>::max());
459 }
460 myLinkMaxGreenTimes[link] = string2time(kv.second);
461 } else if (StringUtils::startsWith(kv.first, "linkMinDur:")) {
462 int link = StringUtils::toInt(kv.first.substr(11));
463 if (link < 0 || link >= myNumLinks) {
464 WRITE_ERRORF(TL("Invalid link '%' given as linkMinDur parameter for actuated tlLogic '%', program '%."), kv.first.substr(11), getID(), getProgramID());
465 continue;
466 }
467 if (myLinkMinGreenTimes.empty()) {
468 myLinkMinGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
469 }
470 myLinkMinGreenTimes[link] = string2time(kv.second);
471 }
472 }
473 if (myLinkMaxGreenTimes.size() > 0 || myLinkMinGreenTimes.size() > 0 || mySwitchingRules.size() > 0) {
474 myLinkGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
475 myLinkRedTimes = std::vector<SUMOTime>(myNumLinks, 0);
476 }
477 //std::cout << SIMTIME << " linkMaxGreenTimes=" << toString(myLinkMaxGreenTimes) << "\n";
478}
479
482 step = step < 0 ? myStep : step;
483 const MSPhaseDefinition* p = myPhases[step];
485 ? p->minDuration
486 : TIME2STEPS(evalExpression(myConditions.find("minDur:" + toString(step))->second));
487}
488
491 step = step < 0 ? myStep : step;
492 const MSPhaseDefinition* p = myPhases[step];
494 ? p->maxDuration
495 : TIME2STEPS(evalExpression(myConditions.find("maxDur:" + toString(step))->second));
496}
497
500 step = step < 0 ? myStep : step;
501 const MSPhaseDefinition* p = myPhases[step];
503 ? p->earliestEnd
504 : TIME2STEPS(evalExpression(myConditions.find("earliestEnd:" + toString(step))->second));
505}
506
509 step = step < 0 ? myStep : step;
510 const MSPhaseDefinition* p = myPhases[step];
512 ? p->latestEnd
513 : TIME2STEPS(evalExpression(myConditions.find("latestEnd:" + toString(step))->second));
514}
515
516
517void
520 for (int i = 0; i < (int)myPhases.size(); i++) {
521 MSPhaseDefinition* phase = myPhases[i];
522 const std::string errorSuffix = "' for overriding attribute in phase " + toString(i) + " of tlLogic '" + getID() + "' in program '" + getProgramID() + "'.";
523 if (phase->minDuration == ovrd) {
524 const std::string cond = "minDur:" + toString(i);
525 if (myConditions.count(cond) == 0) {
526 throw ProcessError("Missing condition '" + cond + errorSuffix);
527 }
528 }
529 if (phase->maxDuration == ovrd) {
530 const std::string cond = "maxDur:" + toString(i);
531 if (myConditions.count(cond) == 0) {
532 throw ProcessError("Missing condition '" + cond + errorSuffix);
533 }
534 }
535 if (phase->earliestEnd == ovrd) {
536 const std::string cond = "earliestEnd:" + toString(i);
537 if (myConditions.count(cond) == 0) {
538 throw ProcessError("Missing condition '" + cond + errorSuffix);
539 }
540 }
541 if (phase->latestEnd == ovrd) {
542 const std::string cond = "latestEnd:" + toString(i);
543 if (myConditions.count(cond) == 0) {
544 throw ProcessError("Missing condition '" + cond + errorSuffix);
545 }
546 }
547 }
548}
549
550
551void
553 for (int i = 0; i < (int)myPhases.size(); i++) {
555 MSPhaseDefinition* phase = myPhases[i];
556 std::vector<int> nextPhases = phase->nextPhases;
557 if (nextPhases.size() == 0) {
558 nextPhases.push_back((i + 1) % (int)myPhases.size());
559 } else if (nextPhases.size() > 1) {
560 myHasMultiTarget = true;
561 }
562 for (int next : nextPhases) {
563 if (next >= 0 && next < (int)myPhases.size()) {
564 const MSPhaseDefinition* nextPhase = myPhases[next];
565 if (nextPhase->earlyTarget != "" || nextPhase->finalTarget != "") {
566 sr.enabled = true;
567 // simplifies later code
568 phase->nextPhases = nextPhases;
569 }
570 }
571 }
572 mySwitchingRules.push_back(sr);
573 }
574}
575
576
579 SUMOTime result = std::numeric_limits<SUMOTime>::max();
580 for (int pI = 0; pI < (int)myPhases.size(); pI++) {
581 const MSPhaseDefinition* phase = myPhases[pI];
582 const std::string& state = phase->getState();
583 for (int i = 0; i < (int)state.size(); i++) {
584 if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
585 for (MSLane* cand : getLanesAt(i)) {
586 if (lane == cand) {
587 if (phase->isActuated()) {
588 result = MIN2(result, getMinDur(pI));
589 }
590 }
591 }
592 }
593 }
594 }
595 return result;
596}
597
598bool
599MSActuatedTrafficLightLogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
600 for (int i = 0; i < (int)state.size(); i++) {
601 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
602 for (MSLane* cand : getLanesAt(i)) {
603 for (MSLane* lane : lanes) {
604 if (lane == cand) {
605 return true;
606 }
607 }
608 }
609 }
610 }
611 return false;
612}
613
614
615// ------------ Switching and setting current rows
616void
619 for (InductLoopInfo& loopInfo : myInductLoops) {
620 loopInfo.loop->setVisible(myShowDetectors);
621 }
622}
623
624
625void
628 for (InductLoopInfo& loopInfo : myInductLoops) {
629 loopInfo.loop->setVisible(false);
630 }
631}
632
633void
635 SUMOTime simStep, int step, SUMOTime stepDuration) {
636 // do not change timing if the phase changes
637 if (step >= 0 && step != myStep) {
638 myStep = step;
640 setTrafficLightSignals(simStep);
641 tlcontrol.get(getID()).executeOnSwitchActions();
642 } else if (step < 0) {
643 // TraCI requested new timing
645 mySwitchCommand = new SwitchCommand(tlcontrol, this, stepDuration + simStep);
647 mySwitchCommand, stepDuration + simStep);
648 myTraCISwitch = true;
649 }
650}
651
652
653void
655 const SUMOTime lastSwitch = t - spentDuration;
656 myStep = step;
657 myPhases[myStep]->myLastSwitch = lastSwitch;
658 const SUMOTime nextSwitch = t + getPhase(step).minDuration - spentDuration;
660 mySwitchCommand = new SwitchCommand(tlcontrol, this, nextSwitch);
662 setTrafficLightSignals(lastSwitch);
663 tlcontrol.get(getID()).executeOnSwitchActions();
664}
665
666
669 // checks if the actual phase should be continued
670 // @note any vehicles which arrived during the previous phases which are now waiting between the detector and the stop line are not
671 // considere here. RiLSA recommends to set minDuration in a way that lets all vehicles pass the detector
674
675 if (myLinkGreenTimes.size() > 0) {
676 // constraints exist, record green time durations for each link
677 const std::string& state = getCurrentPhaseDef().getState();
678 SUMOTime lastDuration = SIMSTEP - myLastTrySwitchTime;
679 for (int i = 0; i < myNumLinks; i++) {
680 if (state[i] == 'G' || state[i] == 'g') {
681 myLinkGreenTimes[i] += lastDuration;
682 } else {
683 myLinkGreenTimes[i] = 0;
684 }
685 if (state[i] == 'r' || state[i] == 'u') {
686 myLinkRedTimes[i] += lastDuration;
687 } else {
688 myLinkRedTimes[i] = 0;
689 }
690 }
691 }
693 // decide the next phase
694 const bool multiTarget = myPhases[myStep]->nextPhases.size() > 1 && myPhases[myStep]->nextPhases.front() >= 0;
695 const int origStep = myStep;
696 int nextStep = myStep;
697 SUMOTime actDuration = now - myPhases[myStep]->myLastSwitch;
698
699 if (mySwitchingRules[myStep].enabled) {
700 const bool mustSwitch = MIN2(getMaxDur() - actDuration, getLatest()) <= 0;
701 nextStep = decideNextPhaseCustom(mustSwitch);
702 } else {
703 // default algorithm
704 const double detectionGap = gapControl();
705#ifdef DEBUG_PHASE_SELECTION
706 if (DEBUG_COND) {
707 std::cout << SIMTIME << " p=" << myStep
708 << " trySwitch dGap=" << (detectionGap == std::numeric_limits<double>::max() ? "inf" : toString(detectionGap))
709 << " multi=" << multiTarget << "\n";
710 }
711#endif
712 if (detectionGap < std::numeric_limits<double>::max() && !multiTarget && !myTraCISwitch) {
713 return duration(detectionGap);
714 }
715 if (multiTarget) {
716 nextStep = decideNextPhase();
717 } else {
718 if (myPhases[myStep]->nextPhases.size() == 1 && myPhases[myStep]->nextPhases.front() >= 0) {
719 nextStep = myPhases[myStep]->nextPhases.front();
720 } else {
721 nextStep = (myStep + 1) % (int)myPhases.size();
722 }
723 }
724 }
725
726 myTraCISwitch = false;
727 if (myLinkMinGreenTimes.size() > 0) {
728 SUMOTime linkMinDur = getLinkMinDuration(getTarget(nextStep));
729 if (linkMinDur > 0) {
730 // for multiTarget, the current phase must be extended but if another
731 // targer is chosen, earlier switching than linkMinDur is possible
732 return multiTarget ? TIME2STEPS(1) : linkMinDur;
733 }
734 }
735 myStep = nextStep;
736 assert(myStep <= (int)myPhases.size());
737 assert(myStep >= 0);
738 //stores the time the phase started
739 const SUMOTime prevStart = myPhases[myStep]->myLastSwitch;
740 if (myStep != origStep) {
741 myPhases[origStep]->myLastEnd = now;
742 myPhases[myStep]->myLastSwitch = now;
743 actDuration = 0;
744 }
745 // activate coloring
746 if ((myShowDetectors || myHasMultiTarget) && getCurrentPhaseDef().isGreenPhase()) {
747 for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
748 //std::cout << SIMTIME << " p=" << myStep << " loopinfo=" << loopInfo->loop->getID() << " set lastGreen=" << STEPS2TIME(now) << "\n";
749 if (loopInfo->isJammed()) {
750 loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
751 } else {
752 loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
753 }
754 loopInfo->lastGreenTime = now;
755 }
756 }
757 // set the next event
758#ifdef DEBUG_PHASE_SELECTION
759 if (DEBUG_COND) {
760 std::cout << SIMTIME << " tl=" << getID() << " p=" << myStep
761 << " nextTryMinDur=" << STEPS2TIME(getMinDur() - actDuration)
762 << " nextTryEarliest=" << STEPS2TIME(getEarliest(prevStart)) << "\n";
763 }
764#endif
765 SUMOTime minRetry = myStep != origStep ? 0 : TIME2STEPS(1);
766 return MAX3(minRetry, getMinDur() - actDuration, getEarliest(prevStart));
767}
768
769
770// ------------ "actuated" algorithm methods
772MSActuatedTrafficLightLogic::duration(const double detectionGap) const {
773 assert(getCurrentPhaseDef().isGreenPhase());
774 assert((int)myPhases.size() > myStep);
775 const SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
776 // ensure that minimum duration is kept
777 SUMOTime newDuration = getMinDur() - actDuration;
778 // try to let the last detected vehicle pass the intersection (duration must be positive)
779 newDuration = MAX3(newDuration, TIME2STEPS(myDetectorGap - detectionGap), SUMOTime(1));
780 // cut the decimal places to ensure that phases always have integer duration
781 if (newDuration % 1000 != 0) {
782 const SUMOTime totalDur = newDuration + actDuration;
783 newDuration = (totalDur / 1000 + 1) * 1000 - actDuration;
784 }
785 // ensure that the maximum duration is not exceeded
786 newDuration = MIN3(newDuration, getMaxDur() - actDuration, getLatest());
787 return newDuration;
788}
789
790
791double
793 //intergreen times should not be lengthend
794 assert((int)myPhases.size() > myStep);
795 double result = std::numeric_limits<double>::max();
797 return result;
798 }
799 // switch off active colors
800 if (myShowDetectors) {
801 for (InductLoopInfo& loopInfo : myInductLoops) {
802 if (loopInfo.lastGreenTime < loopInfo.loop->getLastDetectionTime()) {
803 loopInfo.loop->setSpecialColor(&RGBColor::RED);
804 } else {
805 loopInfo.loop->setSpecialColor(nullptr);
806 }
807 }
808 }
809 if (!getCurrentPhaseDef().isGreenPhase()) {
810 return result; // end current phase
811 }
812
813 // Checks, if the maxDuration is kept. No phase should last longer than maxDuration.
814 SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
815 if (actDuration >= getCurrentPhaseDef().maxDuration || maxLinkDurationReached() || getLatest() == 0) {
816#ifdef DEBUG_PHASE_SELECTION
817 if (DEBUG_COND) {
818 std::cout << SIMTIME << " actDuration=" << STEPS2TIME(actDuration) << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
819 << " maxLinkDurationReached=" << maxLinkDurationReached() << " latest=" << STEPS2TIME(getLatest()) << "\n";
820 }
821#endif
822 return result; // end current phase
823 }
824
825 // now the gapcontrol starts
826 for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
827 MSInductLoop* loop = loopInfo->loop;
828 if (loopInfo->isJammed()) {
829 loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
830 } else {
831 loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
832 }
833 const double actualGap = loop->getTimeSinceLastDetection();
834 if (actualGap < loopInfo->maxGap && !loopInfo->isJammed()) {
835 result = MIN2(result, actualGap);
836 }
837 }
838 return result;
839}
840
841int
843 const auto& cands = myPhases[myStep]->nextPhases;
844 // decide by priority
845 // first target is the default when there is no traffic
846 // @note: to keep the current phase, even when there is no traffic, it must be added to 'next' explicitly
847 int result = cands.front();
848 int maxPrio = 0;
849 SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
850 const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && !maxLinkDurationReached() && getLatest() > 0;
851 if (canExtend) {
852 // consider keeping the current phase until maxDur is reached
853 // (only when there is still traffic in that phase)
854 int currentPrio = getPhasePriority(myStep);
855#ifdef DEBUG_PHASE_SELECTION
856 std::cout << SIMTIME << " p=" << myStep << " loops=" << myInductLoopsForPhase[myStep].size() << " currentPrio=" << currentPrio << "\n";
857#endif
858 if (currentPrio > maxPrio) {
859 result = myStep;
860 maxPrio = currentPrio;
861 }
862 }
863 for (int step : cands) {
864 int target = getTarget(step);
865 int prio = getPhasePriority(target);
866#ifdef DEBUG_PHASE_SELECTION
867 if (DEBUG_COND) {
868 std::cout << SIMTIME << " p=" << myStep << " step=" << step << " target=" << target << " loops=" << myInductLoopsForPhase[target].size() << " prio=" << prio << "\n";
869 }
870#endif
871 if (prio > maxPrio && canExtendLinkGreen(target)) {
872 maxPrio = prio;
873 result = step;
874 }
875 }
876 // prevent starvation in phases that are not direct targets
877 for (const InductLoopInfo& loopInfo : myInductLoops) {
878 int prio = getDetectorPriority(loopInfo);
879 if (prio > maxPrio) {
880 result = cands.front();
881 if (result == myStep) {
882 WRITE_WARNING("At actuated tlLogic '" + getID()
883 + "', starvation at e1Detector '" + loopInfo.loop->getID()
884 + "' which cannot be reached from the default phase " + toString(myStep) + ".");
885 }
886 // use default phase to reach other phases
887#ifdef DEBUG_PHASE_SELECTION
888 if (DEBUG_COND) {
889 std::cout << SIMTIME << " p=" << myStep << " loop=" << loopInfo.loop->getID() << " prio=" << prio << " next=" << result << "\n";
890 }
891#endif
892 break;
893 }
894 }
895 return result;
896}
897
898
899int
901 int origStep = step;
902 // if step is a transition, find the upcoming green phase
903 while (!myPhases[step]->isGreenPhase()) {
904 if (myPhases[step]->nextPhases.size() > 0 && myPhases[step]->nextPhases.front() >= 0) {
905 if (myPhases[step]->nextPhases.size() > 1) {
906 WRITE_WARNINGF(TL("At actuated tlLogic '%', transition phase % should not have multiple next phases"), getID(), toString(step));
907 }
908 step = myPhases[step]->nextPhases.front();
909 } else {
910 step = (step + 1) % (int)myPhases.size();
911 }
912 if (step == origStep) {
913 WRITE_WARNING("At actuated tlLogic '" + getID() + "', infinite transition loop from phase " + toString(origStep));
914 return 0;
915 }
916 }
917 return step;
918}
919
920int
922 MSInductLoop* loop = loopInfo.loop;
923 const double actualGap = loop->getTimeSinceLastDetection();
924 if ((actualGap < loopInfo.maxGap && !loopInfo.isJammed())
925 || loopInfo.lastGreenTime < loop->getLastDetectionTime()) {
926 SUMOTime inactiveTime = MSNet::getInstance()->getCurrentTimeStep() - loopInfo.lastGreenTime;
927 // @note. Inactive time could also be tracked regardless of current activity (to increase robustness in case of detection failure
928 if (inactiveTime > myInactiveThreshold) {
929#ifdef DEBUG_PHASE_SELECTION
930 if (DEBUG_COND) {
931 std::cout << " loop=" << loop->getID() << " gap=" << loop->getTimeSinceLastDetection() << " lastGreen=" << STEPS2TIME(loopInfo.lastGreenTime)
932 << " lastDetection=" << STEPS2TIME(loop->getLastDetectionTime()) << " inactive=" << STEPS2TIME(inactiveTime) << "\n";
933 }
934#endif
935 return (int)STEPS2TIME(inactiveTime);
936 } else {
937 // give bonus to detectors that are currently served (if that phase can stil be extended)
938 if (loopInfo.servedPhase[myStep]) {
939 SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
940 const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && getLatest() > 0;
941#ifdef DEBUG_PHASE_SELECTION
942 if (DEBUG_COND) {
943 std::cout << " loop=" << loop->getID()
944 << " actDuration=" << STEPS2TIME(actDuration)
945 << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
946 << " getLatest=" << STEPS2TIME(getLatest())
947 << " canExtend=" << canExtend
948 << "\n";
949 }
950#endif
951 if (canExtend) {
953 } else {
954 return 0;
955 }
956 }
957 return 1;
958 }
959 }
960 return 0;
961}
962
963int
965 int result = 0;
966 for (const InductLoopInfo* loopInfo : myInductLoopsForPhase[step]) {
967 result += getDetectorPriority(*loopInfo);
968 }
969 return result;
970}
971
972
973void
975 myShowDetectors = show;
976 for (InductLoopInfo& loopInfo : myInductLoops) {
977 loopInfo.loop->setVisible(myShowDetectors);
978 }
979}
980
981
982bool
984 if (myLinkMaxGreenTimes.empty()) {
985 return false;
986 }
987 for (int i = 0; i < myNumLinks; i++) {
989 //std::cout << SIMTIME << " maxLinkDurationReached i=" << i << "\n";
990 return true;
991 }
992 }
993 return false;
994}
995
996bool
998 if (myLinkMaxGreenTimes.empty()) {
999 return true;
1000 }
1001 const std::string& targetState = myPhases[target]->getState();
1002 for (int i = 0; i < myNumLinks; i++) {
1003 if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i] && (
1004 targetState[i] == 'G' || targetState[i] == 'g')) {
1005 //std::cout << SIMTIME << " cannotExtendLinkGreen target=" << target << " i=" << i << "\n";
1006 return false;
1007 }
1008 }
1009 return true;
1010}
1011
1014 SUMOTime result = 0;
1015 if (target != myStep && myLinkMinGreenTimes.size() > 0) {
1016 const std::string& state = myPhases[myStep]->getState();
1017 const std::string& targetState = myPhases[target]->getState();
1018 for (int i = 0; i < myNumLinks; i++) {
1020 && (state[i] == 'G' || state[i] == 'g')
1021 && !(targetState[i] == 'G' || targetState[i] == 'g')) {
1022 result = MAX2(result, myLinkMinGreenTimes[i] - myLinkGreenTimes[i]);
1023 //std::cout << SIMTIME << " getLinkMinDuration myStep=" << myStep << " target=" << target << " i=" << i
1024 // << " greenTime=" << STEPS2TIME(myLinkGreenTimes[i]) << " min=" << STEPS2TIME(myLinkMinGreenTimes[i]) << " result=" << STEPS2TIME(result) << "\n";
1025 }
1026 }
1027 }
1028 return result;
1029}
1030
1031int
1033 for (int next : getCurrentPhaseDef().nextPhases) {
1034 const MSPhaseDefinition* phase = myPhases[next];
1035 const std::string& condition = mustSwitch ? phase->finalTarget : phase->earlyTarget;
1036 //std::cout << SIMTIME << " mustSwitch=" << mustSwitch << " condition=" << condition << "\n";
1037 if (condition != "" && evalExpression(condition)) {
1038 return next;
1039 }
1040 }
1041 return mustSwitch ? getCurrentPhaseDef().nextPhases.back() : myStep;
1042}
1043
1044
1045double
1046MSActuatedTrafficLightLogic::evalExpression(const std::string& condition) const {
1047 const size_t bracketOpen = condition.find('(');
1048 if (bracketOpen != std::string::npos) {
1049 // find matching closing bracket
1050 size_t bracketClose = std::string::npos;
1051 int open = 1;
1052 for (size_t i = bracketOpen + 1; i < condition.size(); i++) {
1053 if (condition[i] == '(') {
1054 open++;
1055 } else if (condition[i] == ')') {
1056 open--;
1057 if (open == 0) {
1058 bracketClose = i;
1059 break;
1060 }
1061 }
1062 }
1063 if (bracketClose == std::string::npos) {
1064 throw ProcessError(TLF("Unmatched parentheses in condition %'", condition));
1065 }
1066 std::string cond2 = condition;
1067 const std::string inBracket = condition.substr(bracketOpen + 1, bracketClose - bracketOpen - 1);
1068 double bracketVal = evalExpression(inBracket);
1069 cond2.replace(bracketOpen, bracketClose - bracketOpen + 1, toString(bracketVal));
1070 try {
1071 return evalExpression(cond2);
1072 } catch (ProcessError& e) {
1073 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1074 }
1075 }
1076 std::vector<std::string> tokens = StringTokenizer(condition).getVector();
1077 //std::cout << SIMTIME << " tokens(" << tokens.size() << ")=" << toString(tokens) << "\n";
1078 if (tokens.size() == 0) {
1079 throw ProcessError(TLF("Invalid empty condition '%'", condition));
1080 } else if (tokens.size() == 1) {
1081 try {
1082 return evalAtomicExpression(tokens[0]);
1083 } catch (ProcessError& e) {
1084 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1085 }
1086 } else if (tokens.size() == 2) {
1087 if (tokens[0] == "not") {
1088 try {
1089 return evalAtomicExpression(tokens[1]) == 0. ? 1. : 0.;
1090 } catch (ProcessError& e) {
1091 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1092 }
1093 } else {
1094 throw ProcessError(TLF("Unsupported condition '%'", condition));
1095 }
1096 } else if (tokens.size() == 3) {
1097 // infix expression
1098 const double a = evalAtomicExpression(tokens[0]);
1099 const double b = evalAtomicExpression(tokens[2]);
1100 const std::string& o = tokens[1];
1101 //std::cout << SIMTIME << " o=" << o << " a=" << a << " b=" << b << "\n";
1102 try {
1103 return evalTernaryExpression(a, o, b, condition);
1104 } catch (ProcessError& e) {
1105 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1106 }
1107 } else {
1108 const int iEnd = (int)tokens.size() - 1;
1109 for (const std::string& o : OPERATOR_PRECEDENCE) {
1110 for (int i = 1; i < iEnd; i++) {
1111 if (tokens[i] == o) {
1112 try {
1113 const double val = evalTernaryExpression(
1114 evalAtomicExpression(tokens[i - 1]), o,
1115 evalAtomicExpression(tokens[i + 1]), condition);
1116 std::vector<std::string> newTokens(tokens.begin(), tokens.begin() + (i - 1));
1117 newTokens.push_back(toString(val));
1118 newTokens.insert(newTokens.end(), tokens.begin() + (i + 2), tokens.end());
1119 return evalExpression(toString(newTokens));
1120 } catch (ProcessError& e) {
1121 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1122 }
1123 }
1124 }
1125 }
1126 throw ProcessError("Parsing expressions with " + toString(tokens.size()) + " elements ('" + condition + "') is not supported");
1127 }
1128 return true;
1129}
1130
1131double
1132MSActuatedTrafficLightLogic::evalTernaryExpression(double a, const std::string& o, double b, const std::string& condition) const {
1133 if (o == "=" || o == "==") {
1134 return (double)(a == b);
1135 } else if (o == "<") {
1136 return (double)(a < b);
1137 } else if (o == ">") {
1138 return (double)(a > b);
1139 } else if (o == "<=") {
1140 return (double)(a <= b);
1141 } else if (o == ">=") {
1142 return (double)(a >= b);
1143 } else if (o == "!=") {
1144 return (double)(a != b);
1145 } else if (o == "or" || o == "||") {
1146 return (double)(a || b);
1147 } else if (o == "and" || o == "&&") {
1148 return (double)(a && b);
1149 } else if (o == "+") {
1150 return a + b;
1151 } else if (o == "-") {
1152 return a - b;
1153 } else if (o == "*") {
1154 return a * b;
1155 } else if (o == "/") {
1156 if (b == 0) {
1157 WRITE_ERRORF(TL("Division by 0 in condition '%'"), condition);
1158 return 0;
1159 }
1160 return a / b;
1161 } else if (o == "%") {
1162 return fmod(a, b);
1163 } else if (o == "**" || o == "^") {
1164 return pow(a, b);
1165 } else {
1166 throw ProcessError("Unsupported operator '" + o + "' in condition '" + condition + "'");
1167 }
1168}
1169
1170double
1171MSActuatedTrafficLightLogic::evalCustomFunction(const std::string& fun, const std::string& arg) const {
1172 std::vector<std::string> args = StringTokenizer(arg, ",").getVector();
1173 const Function& f = myFunctions.find(fun)->second;
1174 if ((int)args.size() != f.nArgs) {
1175 throw ProcessError("Function '" + fun + "' requires " + toString(f.nArgs) + " arguments but " + toString(args.size()) + " were given");
1176 }
1177 std::vector<double> args2;
1178 for (auto a : args) {
1179 args2.push_back(evalExpression(a));
1180 }
1181 myStack.push_back(myStack.back());
1182 myStack.back()["$0"] = 0;
1183 for (int i = 0; i < (int)args2.size(); i++) {
1184 myStack.back()["$" + toString(i + 1)] = args2[i];
1185 }
1186 try {
1187 ConditionMap empty;
1189 } catch (ProcessError& e) {
1190 throw ProcessError("Error when evaluating function '" + fun + "' with args '" + joinToString(args2, ",") + "' (" + e.what() + ")");
1191 }
1192 double result = myStack.back()["$0"];
1193 myStack.pop_back();
1194 return result;
1195}
1196
1197
1198void
1199MSActuatedTrafficLightLogic::executeAssignments(const AssignmentMap& assignments, ConditionMap& conditions, const ConditionMap& forbidden) const {
1200 for (const auto& assignment : assignments) {
1201 if (evalExpression(std::get<1>(assignment))) {
1202 const std::string& id = std::get<0>(assignment);
1203 const double val = evalExpression(std::get<2>(assignment));
1204 ConditionMap::iterator it = conditions.find(id);
1205 if (it != conditions.end()) {
1206 it->second = toString(val);
1207 } else if (forbidden.find(id) != forbidden.end()) {
1208 throw ProcessError(TLF("Modifying global condition '%' is forbidden", id));
1209 } else {
1210 myStack.back()[id] = val;
1211 }
1212 }
1213 }
1214}
1215
1216
1217double
1219 if (expr.size() == 0) {
1220 throw ProcessError(TL("Invalid empty expression"));
1221 } else if (expr[0] == '!') {
1222 return evalAtomicExpression(expr.substr(1)) == 0. ? 1. : 0.;
1223 } else if (expr[0] == '-') {
1224 return -evalAtomicExpression(expr.substr(1));
1225 } else {
1226 // check for 'operator:'
1227 const size_t pos = expr.find(':');
1228 if (pos == std::string::npos) {
1229 auto it = myConditions.find(expr);
1230 if (it != myConditions.end()) {
1231 // symbol lookup
1232 return evalExpression(it->second);
1233 } else {
1234 // look at stack
1235 auto it2 = myStack.back().find(expr);
1236 if (it2 != myStack.back().end()) {
1237 return it2->second;
1238 }
1239 // must be a number
1240 return StringUtils::toDouble(expr);
1241 }
1242 } else {
1243 const std::string fun = expr.substr(0, pos);
1244 const std::string arg = expr.substr(pos + 1);
1245 if (fun == "z") {
1246 return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection();
1247 } else if (fun == "a") {
1248 try {
1249 return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection() == 0;
1250 } catch (ProcessError&) {
1251 return retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(arg, expr, true)->getCurrentVehicleNumber();
1252 }
1253 } else if (fun == "g" || fun == "r") {
1254 try {
1255 int linkIndex = StringUtils::toInt(arg);
1256 if (linkIndex >= 0 && linkIndex < myNumLinks) {
1257 const std::vector<SUMOTime>& times = fun == "g" ? myLinkGreenTimes : myLinkRedTimes;
1258 if (times.empty()) {
1259 return 0;
1260 }
1262 // times are only updated at the start of a phase where
1263 // switching is possible (i.e. not during minDur).
1264 // If somebody is looking at those values in the tracker
1265 // this would be confusing
1266 const LinkState ls = getCurrentPhaseDef().getSignalState(linkIndex);
1267 if ((fun == "g" && (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR))
1268 || (fun == "r" && (ls == LINKSTATE_TL_RED || ls == LINKSTATE_TL_REDYELLOW))) {
1269 const SUMOTime currentGreen = SIMSTEP - myLastTrySwitchTime;
1270 return STEPS2TIME(times[linkIndex] + currentGreen);
1271 } else {
1272 return 0;
1273 }
1274 } else {
1275 return STEPS2TIME(times[linkIndex]);
1276 }
1277 }
1278 } catch (NumberFormatException&) { }
1279 throw ProcessError("Invalid link index '" + arg + "' in expression '" + expr + "'");
1280 } else if (fun == "c") {
1281 return STEPS2TIME(getTimeInCycle());
1282 } else {
1283 if (myFunctions.find(fun) == myFunctions.end()) {
1284 throw ProcessError("Unsupported function '" + fun + "' in expression '" + expr + "'");
1285 }
1286 return evalCustomFunction(fun, arg);
1287 }
1288 }
1289 }
1290}
1291
1292
1293std::map<std::string, double>
1295 std::map<std::string, double> result;
1296 for (auto li : myInductLoops) {
1297 result[li.loop->getID()] = li.loop->getOccupancy() > 0 ? 1 : 0;
1298 }
1299 for (auto loop : myExtraLoops) {
1300 result[loop->getID()] = loop->getOccupancy() > 0 ? 1 : 0;
1301 }
1302 for (auto loop : myExtraE2) {
1303 result[loop->getID()] = loop->getCurrentVehicleNumber();
1304 }
1305 return result;
1306}
1307
1308std::map<std::string, double>
1310 std::map<std::string, double> result;
1311 for (auto item : myConditions) {
1312 if (myListedConditions.count(item.first) != 0) {
1313 try {
1314 result[item.first] = evalExpression(item.second);
1315 } catch (ProcessError& e) {
1316 WRITE_ERRORF(TL("Error when retrieving conditions '%' for tlLogic '%' (%)"), item.first, getID(), e.what());
1317 }
1318 }
1319 }
1320 return result;
1321}
1322
1323const std::string
1324MSActuatedTrafficLightLogic::getParameter(const std::string& key, const std::string defaultValue) const {
1325 if (StringUtils::startsWith(key, "condition.")) {
1326 const std::string cond = key.substr(10);
1327 auto it = myConditions.find(cond);
1328 if (it != myConditions.end()) {
1329 return toString(evalExpression(it->second));
1330 } else {
1331 throw InvalidArgument("Unknown condition '" + cond + "' for actuated traffic light '" + getID() + "'");
1332 }
1333 } else {
1334 return MSSimpleTrafficLightLogic::getParameter(key, defaultValue);
1335 }
1336}
1337
1338void
1339MSActuatedTrafficLightLogic::setParameter(const std::string& key, const std::string& value) {
1340 // some pre-defined parameters can be updated at runtime
1341 if (key == "detector-gap" || key == "passing-time" || key == "file" || key == "freq" || key == "vTypes"
1342 || StringUtils::startsWith(key, "linkMaxDur")
1343 || StringUtils::startsWith(key, "linkMinDur")) {
1344 throw InvalidArgument(key + " cannot be changed dynamically for actuated traffic light '" + getID() + "'");
1345 } else if (key == "max-gap") {
1347 // overwrite custom values
1348 for (InductLoopInfo& loopInfo : myInductLoops) {
1349 loopInfo.maxGap = myMaxGap;
1350 }
1351 Parameterised::setParameter(key, value);
1352 } else if (StringUtils::startsWith(key, "max-gap:")) {
1353 const std::string laneID = key.substr(8);
1354 for (InductLoopInfo& loopInfo : myInductLoops) {
1355 if (loopInfo.lane->getID() == laneID) {
1356 loopInfo.maxGap = StringUtils::toDouble(value);
1357 Parameterised::setParameter(key, value);
1358 return;
1359 }
1360 }
1361 throw InvalidArgument("Invalid lane '" + laneID + "' in key '" + key + "' for actuated traffic light '" + getID() + "'");
1362 } else if (key == "jam-threshold") {
1364 // overwrite custom values
1365 for (InductLoopInfo& loopInfo : myInductLoops) {
1366 loopInfo.jamThreshold = myJamThreshold;
1367 }
1368 Parameterised::setParameter(key, value);
1369 } else if (StringUtils::startsWith(key, "jam-threshold:")) {
1370 const std::string laneID = key.substr(14);
1371 for (InductLoopInfo& loopInfo : myInductLoops) {
1372 if (loopInfo.lane->getID() == laneID) {
1373 loopInfo.jamThreshold = StringUtils::toDouble(value);
1374 Parameterised::setParameter(key, value);
1375 return;
1376 }
1377 }
1378 throw InvalidArgument("Invalid lane '" + laneID + "' in key '" + key + "' for actuated traffic light '" + getID() + "'");
1379 } else if (key == "show-detectors") {
1381 Parameterised::setParameter(key, value);
1382 for (InductLoopInfo& loopInfo : myInductLoops) {
1383 loopInfo.loop->setVisible(myShowDetectors);
1384 }
1385 } else if (key == "inactive-threshold") {
1387 Parameterised::setParameter(key, value);
1388 } else {
1390 }
1391}
1392
1393
1394/****************************************************************************/
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 DEBUG_COND
#define DEFAULT_INACTIVE_THRESHOLD
#define DEFAULT_CURRENT_PRIORITY
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:268
#define WRITE_ERRORF(...)
Definition: MsgHandler.h:277
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:267
#define TL(string)
Definition: MsgHandler.h:284
#define TLF(string,...)
Definition: MsgHandler.h:285
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
const double DEFAULT_BICYCLE_SPEED
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: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
const std::vector< MSLane * > & getLanes() const
Returns this edge's lanes.
Definition: MSEdge.h:168
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:324
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:2908
MSEdge & getEdge() const
Returns the lane's edge.
Definition: MSLane.h:745
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:441
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:471
SUMOTime getCurrentTimeStep() const
Returns the current simulation step.
Definition: MSNet.h:320
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