Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2024 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 : /****************************************************************************/
14 : /// @file MSActuatedTrafficLightLogic.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Julia Ringel
17 : /// @author Jakob Erdmann
18 : /// @author Michael Behrisch
19 : /// @author Laura Bieker
20 : /// @date Sept 2002
21 : ///
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>
30 : #include <utils/common/FileHelpers.h>
31 : #include <utils/common/StringUtils.h>
32 : #include <utils/common/StringTokenizer.h>
33 : #include <microsim/output/MSInductLoop.h>
34 : #include <microsim/MSGlobals.h>
35 : #include <microsim/MSNet.h>
36 : #include <microsim/MSLane.h>
37 : #include <microsim/MSEdge.h>
38 : #include <microsim/MSJunctionLogic.h>
39 : #include <netload/NLDetectorBuilder.h>
40 : #include "MSActuatedTrafficLightLogic.h"
41 :
42 : //#define DEBUG_DETECTORS
43 : //#define DEBUG_PHASE_SELECTION
44 : #define DEBUG_COND (getID()=="C")
45 :
46 : // ===========================================================================
47 : // static members
48 : // ===========================================================================
49 : const std::vector<std::string> MSActuatedTrafficLightLogic::OPERATOR_PRECEDENCE({
50 : "**", "^", "*", "/", "+", "-", "%",
51 : "=", "==", "!=", "<", ">", "<=", ">=",
52 : "and", "&&", "or", "||",
53 : });
54 :
55 : // ===========================================================================
56 : // parameter defaults definitions
57 : // ===========================================================================
58 : #define DEFAULT_MAX_GAP "3.0"
59 : #define DEFAULT_PASSING_TIME "1.9"
60 : #define DEFAULT_DETECTOR_GAP "2.0"
61 : #define DEFAULT_INACTIVE_THRESHOLD "180"
62 : #define DEFAULT_CURRENT_PRIORITY 10
63 :
64 : #define DEFAULT_LENGTH_WITH_GAP 7.5
65 : #define DEFAULT_BIKE_LENGTH_WITH_GAP (getDefaultVehicleLength(SVC_BICYCLE) + 0.5)
66 :
67 : #define NO_DETECTOR "NO_DETECTOR"
68 :
69 : // ===========================================================================
70 : // method definitions
71 : // ===========================================================================
72 590 : MSActuatedTrafficLightLogic::MSActuatedTrafficLightLogic(MSTLLogicControl& tlcontrol,
73 : const std::string& id, const std::string& programID,
74 : const SUMOTime offset,
75 : const Phases& phases,
76 : int step, SUMOTime delay,
77 : const Parameterised::Map& parameter,
78 : const std::string& basePath,
79 : const ConditionMap& conditions,
80 : const AssignmentMap& assignments,
81 590 : const FunctionMap& functions) :
82 : MSSimpleTrafficLightLogic(tlcontrol, id, programID, offset, TrafficLightType::ACTUATED, phases, step, delay, parameter),
83 590 : myHasMultiTarget(false),
84 590 : myLastTrySwitchTime(0),
85 : myConditions(conditions),
86 590 : myAssignments(assignments),
87 : myFunctions(functions),
88 590 : myTraCISwitch(false),
89 1770 : myDetectorPrefix(id + "_" + programID + "_") {
90 590 : myMaxGap = StringUtils::toDouble(getParameter("max-gap", DEFAULT_MAX_GAP));
91 590 : myJamThreshold = StringUtils::toDouble(getParameter("jam-threshold", OptionsCont::getOptions().getValueString("tls.actuated.jam-threshold")));
92 590 : myPassingTime = StringUtils::toDouble(getParameter("passing-time", DEFAULT_PASSING_TIME));
93 590 : myDetectorGap = StringUtils::toDouble(getParameter("detector-gap", DEFAULT_DETECTOR_GAP));
94 1180 : myInactiveThreshold = string2time(getParameter("inactive-threshold", DEFAULT_INACTIVE_THRESHOLD));
95 590 : myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
96 1180 : myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
97 590 : myFreq = TIME2STEPS(StringUtils::toDouble(getParameter("freq", "300")));
98 1180 : myVehicleTypes = getParameter("vTypes", "");
99 :
100 1180 : if (hasParameter("hide-conditions")) {
101 8 : std::vector<std::string> hidden = StringTokenizer(getParameter("hide-conditions", "")).getVector();
102 4 : std::set<std::string> hiddenSet(hidden.begin(), hidden.end());
103 28 : for (auto item : myConditions) {
104 : if (hiddenSet.count(item.first) == 0) {
105 : myListedConditions.insert(item.first);
106 : }
107 24 : }
108 4 : } else {
109 1172 : const bool showAll = getParameter("show-conditions", "") == "";
110 1172 : std::vector<std::string> shown = StringTokenizer(getParameter("show-conditions", "")).getVector();
111 586 : std::set<std::string> shownSet(shown.begin(), shown.end());
112 654 : for (auto item : myConditions) {
113 68 : if (showAll || shownSet.count(item.first) != 0) {
114 : myListedConditions.insert(item.first);
115 : }
116 68 : }
117 586 : }
118 1180 : if (hasParameter("extra-detectors")) {
119 2 : const std::string extraIDs = getParameter("extra-detectors", "");
120 4 : for (std::string customID : StringTokenizer(extraIDs).getVector()) {
121 : try {
122 2 : myExtraLoops.push_back(retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(customID, extraIDs, true));
123 1 : } catch (ProcessError&) {
124 1 : myExtraE2.push_back(retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(customID, extraIDs, true));
125 1 : }
126 1 : }
127 : }
128 0 : myStack.push_back(std::map<std::string, double>());
129 590 : }
130 :
131 :
132 2945 : MSActuatedTrafficLightLogic::~MSActuatedTrafficLightLogic() { }
133 :
134 : void
135 590 : MSActuatedTrafficLightLogic::init(NLDetectorBuilder& nb) {
136 590 : MSTrafficLightLogic::init(nb);
137 590 : initAttributeOverride();
138 590 : initSwitchingRules();
139 590 : if (myLanes.size() == 0) {
140 : // must be an older network
141 0 : WRITE_WARNINGF(TL("Traffic light '%' does not control any links"), getID());
142 : }
143 : bool warn = true; // warn only once
144 590 : const int numLinks = (int)myLinks.size();
145 :
146 : // Detector position should be computed based on road speed. If the position
147 : // is quite far away and the minDur is short this may cause the following
148 : // problems:
149 : //
150 : // 1) high flow failure:
151 : // In a standing queue, no vehicle touches the detector.
152 : // By the time the queue advances, the detector gap has been exceeded and the phase terminates prematurely
153 : //
154 : // 2) low flow failure
155 : // The standing queue is fully between stop line and detector and there are no further vehicles.
156 : // The minDur is too short to let all vehicles pass
157 : //
158 : // Problem 2) is not so critical because there is less potential for
159 : // jamming in a low-flow situation. In contrast, problem 1) should be
160 : // avoided as it has big jamming potential. We compute an upper bound for the
161 : // detector distance to avoid it
162 :
163 :
164 : // change values for setting the loops and lanestate-detectors, here
165 : //SUMOTime inductLoopInterval = 1; //
166 : // build the induct loops
167 : std::map<const MSLane*, MSInductLoop*> laneInductLoopMap;
168 : std::map<MSInductLoop*, int> inductLoopInfoMap; // retrieve junction entry lane in case loops are placed further upstream (and other properties)
169 590 : int detEdgeIndex = -1;
170 590 : int detLaneIndex = 0;
171 1180 : const double detDefaultLength = StringUtils::toDouble(getParameter("detector-length",
172 1770 : OptionsCont::getOptions().getValueString("tls.actuated.detector-length")));
173 : MSEdge* prevDetEdge = nullptr;
174 10441 : for (LaneVector& lanes : myLanes) {
175 19668 : for (MSLane* lane : lanes) {
176 9817 : const std::string customID = getParameter(lane->getID());
177 11035 : if (noVehicles(lane->getPermissions()) && customID == "") {
178 : // do not build detectors on green verges or sidewalks
179 1218 : continue;
180 : }
181 8599 : if (laneInductLoopMap.find(lane) != laneInductLoopMap.end()) {
182 : // only build one detector per lane
183 3865 : continue;
184 : }
185 4734 : const SUMOTime minDur = getMinimumMinDuration(lane);
186 5328 : if (minDur == std::numeric_limits<SUMOTime>::max() && customID == "") {
187 : // only build detector if this lane is relevant for an actuated phase
188 592 : continue;
189 : }
190 4142 : double length = lane->getLength();
191 : double ilpos;
192 : double inductLoopPosition;
193 4142 : MSInductLoop* loop = nullptr;
194 4142 : if (&lane->getEdge() != prevDetEdge) {
195 2197 : detEdgeIndex++;
196 2197 : detLaneIndex = 0;
197 : prevDetEdge = &lane->getEdge();
198 : } else {
199 1945 : detLaneIndex++;
200 : }
201 4142 : const bool isBikeLane = (lane->getPermissions() & ~SVC_PEDESTRIAN) == SVC_BICYCLE;
202 4142 : const double defaultLength = isBikeLane ? DEFAULT_BIKE_LENGTH_WITH_GAP : DEFAULT_LENGTH_WITH_GAP;
203 4142 : if (customID == "") {
204 3816 : const double speed = isBikeLane ? DEFAULT_BICYCLE_SPEED : lane->getSpeedLimit();
205 7632 : inductLoopPosition = MIN2(
206 3816 : myDetectorGap * speed,
207 3816 : (STEPS2TIME(minDur) / myPassingTime + 0.5) * defaultLength);
208 :
209 : // check whether the lane is long enough
210 3816 : ilpos = length - inductLoopPosition;
211 3816 : MSLane* placementLane = lane;
212 4779 : while (ilpos < 0 && placementLane->getIncomingLanes().size() == 1
213 5083 : && placementLane->getIncomingLanes().front().viaLink->getCorrespondingEntryLink()->getTLLogic() == nullptr) {
214 585 : placementLane = placementLane->getLogicalPredecessorLane();
215 585 : ilpos += placementLane->getLength();
216 : }
217 3816 : if (ilpos < 0) {
218 : ilpos = 0;
219 : }
220 : // Build the induct loop and set it into the container
221 3816 : const double detLength = getDouble("detector-length:" + lane->getID(), detDefaultLength);
222 7632 : std::string id = myDetectorPrefix + "D" + toString(detEdgeIndex) + "." + toString(detLaneIndex);
223 3816 : loop = static_cast<MSInductLoop*>(nb.createInductLoop(id, placementLane, ilpos, detLength, "", myVehicleTypes, "", (int)PersonMode::NONE, myShowDetectors));
224 3816 : MSNet::getInstance()->getDetectorControl().add(SUMO_TAG_INDUCTION_LOOP, loop, myFile, myFreq);
225 326 : } else if (customID == NO_DETECTOR) {
226 240 : continue;
227 : } else {
228 172 : loop = dynamic_cast<MSInductLoop*>(MSNet::getInstance()->getDetectorControl().getTypedDetectors(SUMO_TAG_INDUCTION_LOOP).get(customID));
229 86 : if (loop == nullptr) {
230 0 : throw ProcessError(TLF("Unknown inductionLoop '%' given as custom detector for actuated tlLogic '%', program '%.", customID, getID(), getProgramID()));
231 : }
232 : ilpos = loop->getPosition();
233 86 : inductLoopPosition = length - ilpos;
234 : }
235 3902 : const double maxGap = getDouble("max-gap:" + lane->getID(), myMaxGap);
236 3902 : const double jamThreshold = getDouble("jam-threshold:" + lane->getID(), myJamThreshold);
237 3902 : laneInductLoopMap[lane] = loop;
238 3902 : inductLoopInfoMap[loop] = (int)myInductLoops.size();
239 3902 : myInductLoops.push_back(InductLoopInfo(loop, lane, (int)myPhases.size(), maxGap, jamThreshold));
240 :
241 3902 : 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 0 : 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 : // check1e : the conflict is only with bikes/pedestrians (i.e. for a right turn, also left turn with no oncoming traffic)
255 : // check1f : the conflict is only with a link from the same edge
256 : // (Under these conditions we assume that the minor link is unimportant and traffic is mostly for the major link)
257 : //
258 : // check1c: when the edge has only one lane, we treat greenMinor as green as there would be no actuation otherwise
259 : // check1d: for turnarounds 1b is sufficient and we do not require 1a
260 : //
261 : // 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
262 : //
263 : // if a jamThreshold is specificed for the loop, all checks are ignored
264 :
265 : // also assign loops to link index for validation:
266 : // check if all links from actuated phases (minDur != maxDur) have an inductionloop in at least one phase
267 : const SVCPermissions motorized = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
268 : std::map<int, std::set<MSInductLoop*> > linkToLoops;
269 : std::set<int> actuatedLinks;
270 :
271 590 : std::vector<bool> neverMajor(numLinks, true);
272 4690 : for (const MSPhaseDefinition* phase : myPhases) {
273 : const std::string& state = phase->getState();
274 83738 : for (int i = 0; i < numLinks; i++) {
275 79638 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
276 : neverMajor[i] = false;
277 : }
278 : }
279 : }
280 590 : std::vector<bool> oneLane(numLinks, false);
281 590 : std::vector<bool> turnaround(numLinks, true);
282 10441 : for (int i = 0; i < numLinks; i++) {
283 16761 : for (MSLane* lane : getLanesAt(i)) {
284 : // only count motorized vehicle lanes
285 : int numMotorized = 0;
286 34774 : for (MSLane* l : lane->getEdge().getLanes()) {
287 24969 : if ((l->getPermissions() & motorized) != 0) {
288 16169 : numMotorized++;
289 : }
290 : }
291 9805 : if (numMotorized == 1) {
292 : oneLane[i] = true;
293 2895 : break;
294 : }
295 : }
296 10981 : for (MSLink* link : getLinksAt(i)) {
297 9668 : if (!link->isTurnaround()) {
298 : turnaround[i] = false;
299 8538 : break;
300 : }
301 : }
302 : }
303 :
304 :
305 4690 : for (const MSPhaseDefinition* phase : myPhases) {
306 4100 : const int phaseIndex = (int)myInductLoopsForPhase.size();
307 : std::set<MSInductLoop*> loops;
308 : if (phase->isActuated()) {
309 : const std::string& state = phase->getState();
310 : // collect indices of all green links for the phase
311 : std::set<int> greenLinks;
312 : // green links that could jam
313 : std::set<int> greenLinksPermissive;
314 : // collect green links for each induction loops (in this phase)
315 : std::map<MSInductLoop*, std::set<int> > loopLinks;
316 :
317 30630 : for (int i = 0; i < numLinks; i++) {
318 29115 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
319 : greenLinks.insert(i);
320 : actuatedLinks.insert(i);
321 21318 : } else if (state[i] == LINKSTATE_TL_GREEN_MINOR) {
322 901 : if (((neverMajor[i] || turnaround[i]) // check1a, 1d
323 3145 : && hasMajor(state, getLanesAt(i))) // check1b
324 1228 : || oneLane[i] // check1c
325 4916 : || weakConflict(i, state)) { // check1e, check1f
326 : greenLinks.insert(i);
327 2848 : if (!turnaround[i]) {
328 : actuatedLinks.insert(i);
329 : }
330 : } else {
331 : greenLinksPermissive.insert(i);
332 : }
333 : }
334 : #ifdef DEBUG_DETECTORS
335 : if (DEBUG_COND) {
336 : std::cout << " phase=" << phaseIndex << " i=" << i << " state=" << state[i] << " green=" << greenLinks.count(i) << " oneLane=" << oneLane[i]
337 : << " turn=" << turnaround[i] << " loopLanes=";
338 : for (MSLane* lane : getLanesAt(i)) {
339 : if (laneInductLoopMap.count(lane) != 0) {
340 : std::cout << lane->getID() << " ";
341 : }
342 : }
343 : std::cout << "\n";
344 : }
345 : #endif
346 87255 : for (MSLane* lane : getLanesAt(i)) {
347 : if (laneInductLoopMap.count(lane) != 0) {
348 23846 : loopLinks[laneInductLoopMap[lane]].insert(i);
349 : }
350 : }
351 : }
352 13714 : for (auto& item : loopLinks) {
353 12199 : MSInductLoop* loop = item.first;
354 12199 : const InductLoopInfo& info = myInductLoops[inductLoopInfoMap[loop]];
355 12199 : const MSLane* loopLane = info.lane;
356 : bool usable = true;
357 : bool foundUsable = false;
358 : // check1
359 35599 : for (int j : item.second) {
360 15383 : if (greenLinks.count(j) == 0 && (info.jamThreshold <= 0 || greenLinksPermissive.count(j) == 0)) {
361 : usable = false;
362 : #ifdef DEBUG_DETECTORS
363 : if (DEBUG_COND) {
364 : std::cout << " phase=" << phaseIndex << " check1: loopLane=" << loopLane->getID() << " notGreen=" << j << " oneLane[j]=" << oneLane[j] << "\n";
365 : }
366 : #endif
367 : } else {
368 : foundUsable = true;
369 : }
370 : }
371 12199 : if (!usable && foundUsable && info.jamThreshold > 0) {
372 : // permit green even when the same lane has green and red links (if we have jamDetection)
373 : usable = true;
374 : }
375 : // check2 (skip if we have jam detection)
376 12199 : if (usable && info.jamThreshold <= 0) {
377 9473 : for (MSLink* link : loopLane->getLinkCont()) {
378 6216 : if (link->isTurnaround()) {
379 627 : continue;
380 : }
381 5589 : const MSLane* next = link->getLane();
382 : if (laneInductLoopMap.count(next) != 0) {
383 187 : MSInductLoop* nextLoop = laneInductLoopMap[next];
384 409 : for (int j : loopLinks[nextLoop]) {
385 : if (greenLinks.count(j) == 0) {
386 : usable = false;
387 : #ifdef DEBUG_DETECTORS
388 : if (DEBUG_COND) std::cout << " phase=" << phaseIndex << " check2: loopLane=" << loopLane->getID()
389 : << " nextLane=" << next->getID() << " nextLink=" << j << " nextState=" << state[j] << "\n";
390 : #endif
391 : break;
392 : }
393 : }
394 : }
395 : }
396 : }
397 :
398 12199 : if (usable) {
399 4173 : loops.insert(item.first);
400 : #ifdef DEBUG_DETECTORS
401 : if (DEBUG_COND) {
402 : std::cout << " phase=" << phaseIndex << " usableLoops=" << item.first->getID() << " links=" << joinToString(item.second, " ") << "\n";
403 : }
404 : #endif
405 12217 : for (int j : item.second) {
406 8044 : linkToLoops[j].insert(item.first);
407 : }
408 : }
409 : }
410 1515 : if (loops.size() == 0) {
411 21 : WRITE_WARNINGF(TL("At actuated tlLogic '%', actuated phase % has no controlling detector."), getID(), toString(phaseIndex));
412 : }
413 : }
414 : #ifdef DEBUG_DETECTORS
415 : if (DEBUG_COND) {
416 : std::cout << " phase=" << phaseIndex << " loops=" << joinNamedToString(loops, " ") << "\n";
417 : }
418 : if (DEBUG_COND) {
419 : std::cout << " linkToLoops:\n";
420 : for (auto item : linkToLoops) {
421 : std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
422 : }
423 : }
424 : #endif
425 : std::vector<InductLoopInfo*> loopInfos;
426 4100 : myInductLoopsForPhase.push_back(loopInfos);
427 8273 : for (MSInductLoop* loop : loops) {
428 43705 : for (InductLoopInfo& loopInfo : myInductLoops) {
429 39532 : if (loopInfo.loop == loop) {
430 4173 : myInductLoopsForPhase.back().push_back(&loopInfo);
431 4173 : loopInfo.servedPhase[phaseIndex] = true;
432 : }
433 : }
434 : }
435 : }
436 : #ifdef DEBUG_DETECTORS
437 : if (DEBUG_COND) {
438 : std::cout << "final linkToLoops:\n";
439 : for (auto item : linkToLoops) {
440 : std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
441 : }
442 : }
443 : #endif
444 : std::vector<int> warnLinks;
445 8850 : for (int i : actuatedLinks) {
446 9814 : if (linkToLoops[i].size() == 0 && myLinks[i].size() > 0
447 9733 : && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
448 636 : if (getParameter(myLinks[i].front()->getLaneBefore()->getID()) != NO_DETECTOR) {
449 78 : warnLinks.push_back(i);
450 : }
451 : }
452 : }
453 590 : if (warnLinks.size() > 0) {
454 72 : WRITE_WARNINGF(TL("At actuated tlLogic '%', linkIndex % has no controlling detector."), getID(), joinToString(warnLinks, ","));
455 : }
456 : // parse maximum green times for each link (optional)
457 2209 : for (const auto& kv : getParametersMap()) {
458 3238 : if (StringUtils::startsWith(kv.first, "linkMaxDur:")) {
459 1 : int link = StringUtils::toInt(kv.first.substr(11));
460 1 : if (link < 0 || link >= myNumLinks) {
461 0 : WRITE_ERRORF(TL("Invalid link '%' given as linkMaxDur parameter for actuated tlLogic '%', program '%."), kv.first.substr(11), getID(), getProgramID());
462 0 : continue;
463 : }
464 1 : if (myLinkMaxGreenTimes.empty()) {
465 2 : myLinkMaxGreenTimes = std::vector<SUMOTime>(myNumLinks, std::numeric_limits<SUMOTime>::max());
466 : }
467 1 : myLinkMaxGreenTimes[link] = string2time(kv.second);
468 3236 : } else if (StringUtils::startsWith(kv.first, "linkMinDur:")) {
469 9 : int link = StringUtils::toInt(kv.first.substr(11));
470 9 : if (link < 0 || link >= myNumLinks) {
471 0 : WRITE_ERRORF(TL("Invalid link '%' given as linkMinDur parameter for actuated tlLogic '%', program '%."), kv.first.substr(11), getID(), getProgramID());
472 0 : continue;
473 : }
474 9 : if (myLinkMinGreenTimes.empty()) {
475 4 : myLinkMinGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
476 : }
477 9 : myLinkMinGreenTimes[link] = string2time(kv.second);
478 : }
479 : }
480 590 : if (myLinkMaxGreenTimes.size() > 0 || myLinkMinGreenTimes.size() > 0 || mySwitchingRules.size() > 0) {
481 590 : myLinkGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
482 1180 : myLinkRedTimes = std::vector<SUMOTime>(myNumLinks, 0);
483 : }
484 : //std::cout << SIMTIME << " linkMaxGreenTimes=" << toString(myLinkMaxGreenTimes) << "\n";
485 590 : }
486 :
487 :
488 : bool
489 1144 : MSActuatedTrafficLightLogic::weakConflict(int tlIndex, const std::string& state) const {
490 1316 : for (MSLink* link : getLinksAt(tlIndex)) {
491 : int linkIndex = link->getIndex();
492 : const MSJunction* junction = link->getJunction();
493 15525 : for (int i = 0; i < (int)myLinks.size(); i++) {
494 15353 : if (i == tlIndex) {
495 627 : continue;
496 : }
497 14726 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
498 12926 : for (MSLink* foe : getLinksAt(i)) {
499 : // junction logic is based on junction link index rather than tl index
500 : int foeIndex = foe->getIndex();
501 : const MSJunction* junction2 = foe->getJunction();
502 6623 : if (junction == junction2) {
503 5153 : const MSJunctionLogic* logic = junction->getLogic();
504 : //std::cout << " greenLink=" << i << " isFoe=" << logic->getFoesFor(linkIndex).test(foeIndex) << "\n";
505 5153 : if (logic->getFoesFor(linkIndex).test(foeIndex)
506 1348 : && (foe->getPermissions() & ~SVC_WEAK) != 0 // check1e
507 6148 : && &foe->getLaneBefore()->getEdge() != &link->getLaneBefore()->getEdge()) { // check1f
508 : //std::cout << " strongConflict " << tlIndex << " in phase " << state << " with link " << foe->getTLIndex() << "\n";
509 : return false;
510 : }
511 : }
512 : }
513 :
514 : }
515 : }
516 : }
517 : //std::cout << " weakConflict " << tlIndex << " in phase " << state << "\n";
518 : return true;
519 : }
520 :
521 :
522 : SUMOTime
523 300780 : MSActuatedTrafficLightLogic::getMinDur(int step) const {
524 300780 : step = step < 0 ? myStep : step;
525 300780 : const MSPhaseDefinition* p = myPhases[step];
526 300780 : return p->minDuration != MSPhaseDefinition::OVERRIDE_DURATION
527 300780 : ? p->minDuration
528 303332 : : TIME2STEPS(evalExpression(myConditions.find("minDur:" + toString(step))->second));
529 : }
530 :
531 : SUMOTime
532 107526 : MSActuatedTrafficLightLogic::getMaxDur(int step) const {
533 107526 : step = step < 0 ? myStep : step;
534 107526 : const MSPhaseDefinition* p = myPhases[step];
535 107526 : return p->maxDuration != MSPhaseDefinition::OVERRIDE_DURATION
536 107526 : ? p->maxDuration
537 110030 : : TIME2STEPS(evalExpression(myConditions.find("maxDur:" + toString(step))->second));
538 : }
539 :
540 : SUMOTime
541 233631 : MSActuatedTrafficLightLogic::getEarliestEnd(int step) const {
542 233631 : step = step < 0 ? myStep : step;
543 233631 : const MSPhaseDefinition* p = myPhases[step];
544 233631 : return p->earliestEnd != MSPhaseDefinition::OVERRIDE_DURATION
545 233631 : ? p->earliestEnd
546 236135 : : TIME2STEPS(evalExpression(myConditions.find("earliestEnd:" + toString(step))->second));
547 : }
548 :
549 : SUMOTime
550 262148 : MSActuatedTrafficLightLogic::getLatestEnd(int step) const {
551 262148 : step = step < 0 ? myStep : step;
552 262148 : const MSPhaseDefinition* p = myPhases[step];
553 262148 : return p->latestEnd != MSPhaseDefinition::OVERRIDE_DURATION
554 262148 : ? p->latestEnd
555 264652 : : TIME2STEPS(evalExpression(myConditions.find("latestEnd:" + toString(step))->second));
556 : }
557 :
558 :
559 : void
560 590 : MSActuatedTrafficLightLogic::initAttributeOverride() {
561 : const SUMOTime ovrd = MSPhaseDefinition::OVERRIDE_DURATION;
562 4690 : for (int i = 0; i < (int)myPhases.size(); i++) {
563 4100 : MSPhaseDefinition* phase = myPhases[i];
564 8200 : const std::string errorSuffix = "' for overriding attribute in phase " + toString(i) + " of tlLogic '" + getID() + "' in program '" + getProgramID() + "'.";
565 4100 : if (phase->minDuration == ovrd) {
566 4 : const std::string cond = "minDur:" + toString(i);
567 : if (myConditions.count(cond) == 0) {
568 0 : throw ProcessError("Missing condition '" + cond + errorSuffix);
569 : }
570 : }
571 4100 : if (phase->maxDuration == ovrd) {
572 4 : const std::string cond = "maxDur:" + toString(i);
573 : if (myConditions.count(cond) == 0) {
574 0 : throw ProcessError("Missing condition '" + cond + errorSuffix);
575 : }
576 : }
577 4100 : if (phase->earliestEnd == ovrd) {
578 4 : const std::string cond = "earliestEnd:" + toString(i);
579 : if (myConditions.count(cond) == 0) {
580 0 : throw ProcessError("Missing condition '" + cond + errorSuffix);
581 : }
582 : }
583 4100 : if (phase->latestEnd == ovrd) {
584 4 : const std::string cond = "latestEnd:" + toString(i);
585 : if (myConditions.count(cond) == 0) {
586 0 : throw ProcessError("Missing condition '" + cond + errorSuffix);
587 : }
588 : }
589 : }
590 590 : }
591 :
592 :
593 : void
594 590 : MSActuatedTrafficLightLogic::initSwitchingRules() {
595 4690 : for (int i = 0; i < (int)myPhases.size(); i++) {
596 4100 : SwitchingRules sr;
597 4100 : MSPhaseDefinition* phase = myPhases[i];
598 4100 : std::vector<int> nextPhases = phase->nextPhases;
599 4100 : if (nextPhases.size() == 0) {
600 3967 : nextPhases.push_back((i + 1) % (int)myPhases.size());
601 133 : } else if (nextPhases.size() > 1) {
602 30 : myHasMultiTarget = true;
603 : }
604 8265 : for (int next : nextPhases) {
605 4165 : if (next >= 0 && next < (int)myPhases.size()) {
606 4165 : const MSPhaseDefinition* nextPhase = myPhases[next];
607 4165 : if (nextPhase->earlyTarget != "" || nextPhase->finalTarget != "") {
608 57 : sr.enabled = true;
609 : // simplifies later code
610 57 : phase->nextPhases = nextPhases;
611 : }
612 : }
613 : }
614 4100 : mySwitchingRules.push_back(sr);
615 : }
616 590 : }
617 :
618 :
619 : SUMOTime
620 4734 : MSActuatedTrafficLightLogic::getMinimumMinDuration(MSLane* lane) const {
621 : SUMOTime result = std::numeric_limits<SUMOTime>::max();
622 40688 : for (int pI = 0; pI < (int)myPhases.size(); pI++) {
623 35954 : const MSPhaseDefinition* phase = myPhases[pI];
624 : const std::string& state = phase->getState();
625 888504 : for (int i = 0; i < (int)state.size(); i++) {
626 852550 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
627 411037 : for (MSLane* cand : getLanesAt(i)) {
628 204008 : if (lane == cand) {
629 : if (phase->isActuated()) {
630 10868 : result = MIN2(result, getMinDur(pI));
631 : }
632 : }
633 : }
634 : }
635 : }
636 : }
637 4734 : return result;
638 : }
639 :
640 : bool
641 3145 : MSActuatedTrafficLightLogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
642 44444 : for (int i = 0; i < (int)state.size(); i++) {
643 43843 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
644 19623 : for (MSLane* cand : getLanesAt(i)) {
645 17798 : for (MSLane* lane : lanes) {
646 9863 : if (lane == cand) {
647 : return true;
648 : }
649 : }
650 : }
651 : }
652 : }
653 : return false;
654 : }
655 :
656 :
657 : // ------------ Switching and setting current rows
658 : void
659 590 : MSActuatedTrafficLightLogic::activateProgram() {
660 590 : MSTrafficLightLogic::activateProgram();
661 590 : for (InductLoopInfo& loopInfo : myInductLoops) {
662 0 : loopInfo.loop->setVisible(myShowDetectors);
663 : }
664 590 : }
665 :
666 :
667 : void
668 16 : MSActuatedTrafficLightLogic::deactivateProgram() {
669 16 : MSTrafficLightLogic::deactivateProgram();
670 32 : for (InductLoopInfo& loopInfo : myInductLoops) {
671 16 : loopInfo.loop->setVisible(false);
672 : }
673 16 : }
674 :
675 : void
676 29 : MSActuatedTrafficLightLogic::changeStepAndDuration(MSTLLogicControl& tlcontrol,
677 : SUMOTime simStep, int step, SUMOTime stepDuration) {
678 : // do not change timing if the phase changes
679 29 : if (step >= 0 && step != myStep) {
680 5 : myStep = step;
681 5 : myPhases[myStep]->myLastSwitch = MSNet::getInstance()->getCurrentTimeStep();
682 5 : setTrafficLightSignals(simStep);
683 5 : tlcontrol.get(getID()).executeOnSwitchActions();
684 24 : } else if (step < 0) {
685 : // TraCI requested new timing
686 15 : mySwitchCommand->deschedule(this);
687 15 : mySwitchCommand = new SwitchCommand(tlcontrol, this, stepDuration + simStep);
688 15 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(
689 15 : mySwitchCommand, stepDuration + simStep);
690 15 : myTraCISwitch = true;
691 : }
692 29 : }
693 :
694 :
695 : void
696 2 : MSActuatedTrafficLightLogic::loadState(MSTLLogicControl& tlcontrol, SUMOTime t, int step, SUMOTime spentDuration) {
697 2 : const SUMOTime lastSwitch = t - spentDuration;
698 2 : myStep = step;
699 2 : myPhases[myStep]->myLastSwitch = lastSwitch;
700 2 : const SUMOTime nextSwitch = t + getPhase(step).minDuration - spentDuration;
701 2 : mySwitchCommand->deschedule(this);
702 2 : mySwitchCommand = new SwitchCommand(tlcontrol, this, nextSwitch);
703 2 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(mySwitchCommand, nextSwitch);
704 2 : setTrafficLightSignals(lastSwitch);
705 2 : tlcontrol.get(getID()).executeOnSwitchActions();
706 2 : }
707 :
708 :
709 : SUMOTime
710 288559 : MSActuatedTrafficLightLogic::trySwitch() {
711 : // checks if the actual phase should be continued
712 : // @note any vehicles which arrived during the previous phases which are now waiting between the detector and the stop line are not
713 : // considere here. RiLSA recommends to set minDuration in a way that lets all vehicles pass the detector
714 288559 : SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
715 577118 : executeAssignments(myAssignments, myConditions);
716 :
717 288559 : if (myLinkGreenTimes.size() > 0) {
718 : // constraints exist, record green time durations for each link
719 288559 : const std::string& state = getCurrentPhaseDef().getState();
720 288559 : SUMOTime lastDuration = SIMSTEP - myLastTrySwitchTime;
721 4877867 : for (int i = 0; i < myNumLinks; i++) {
722 4589308 : if (state[i] == 'G' || state[i] == 'g') {
723 1562474 : myLinkGreenTimes[i] += lastDuration;
724 : } else {
725 3026834 : myLinkGreenTimes[i] = 0;
726 : }
727 4589308 : if (state[i] == 'r' || state[i] == 'u') {
728 2711561 : myLinkRedTimes[i] += lastDuration;
729 : } else {
730 1877747 : myLinkRedTimes[i] = 0;
731 : }
732 : }
733 : }
734 288559 : myLastTrySwitchTime = now;
735 : // decide the next phase
736 288559 : const bool multiTarget = myPhases[myStep]->nextPhases.size() > 1 && myPhases[myStep]->nextPhases.front() >= 0;
737 : const int origStep = myStep;
738 : int nextStep = myStep;
739 288559 : SUMOTime actDuration = now - myPhases[myStep]->myLastSwitch;
740 :
741 288559 : if (mySwitchingRules[myStep].enabled) {
742 12061 : const bool mustSwitch = MIN2(getMaxDur() - actDuration, getLatest()) <= 0;
743 12061 : nextStep = decideNextPhaseCustom(mustSwitch);
744 : } else {
745 : // default algorithm
746 276498 : const double detectionGap = gapControl();
747 : #ifdef DEBUG_PHASE_SELECTION
748 : if (DEBUG_COND) {
749 : std::cout << SIMTIME << " p=" << myStep
750 : << " trySwitch dGap=" << (detectionGap == std::numeric_limits<double>::max() ? "inf" : toString(detectionGap))
751 : << " multi=" << multiTarget << "\n";
752 : }
753 : #endif
754 276498 : if (detectionGap < std::numeric_limits<double>::max() && !multiTarget && !myTraCISwitch) {
755 93475 : return duration(detectionGap);
756 : }
757 183023 : if (multiTarget) {
758 1404 : nextStep = decideNextPhase();
759 : } else {
760 181619 : if (myPhases[myStep]->nextPhases.size() == 1 && myPhases[myStep]->nextPhases.front() >= 0) {
761 : nextStep = myPhases[myStep]->nextPhases.front();
762 : } else {
763 180537 : nextStep = (myStep + 1) % (int)myPhases.size();
764 : }
765 : }
766 : }
767 :
768 195084 : myTraCISwitch = false;
769 195084 : if (myLinkMinGreenTimes.size() > 0) {
770 165 : SUMOTime linkMinDur = getLinkMinDuration(getTarget(nextStep));
771 165 : if (linkMinDur > 0) {
772 : // for multiTarget, the current phase must be extended but if another
773 : // targer is chosen, earlier switching than linkMinDur is possible
774 53 : return multiTarget ? TIME2STEPS(1) : linkMinDur;
775 : }
776 : }
777 195040 : myStep = nextStep;
778 : assert(myStep <= (int)myPhases.size());
779 : assert(myStep >= 0);
780 : //stores the time the phase started
781 195040 : const SUMOTime prevStart = myPhases[myStep]->myLastSwitch;
782 195040 : if (myStep != origStep) {
783 166535 : myPhases[origStep]->myLastEnd = now;
784 166535 : myPhases[myStep]->myLastSwitch = now;
785 : actDuration = 0;
786 : }
787 : // activate coloring
788 195040 : if ((myShowDetectors || myHasMultiTarget) && getCurrentPhaseDef().isGreenPhase()) {
789 25950 : for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
790 : //std::cout << SIMTIME << " p=" << myStep << " loopinfo=" << loopInfo->loop->getID() << " set lastGreen=" << STEPS2TIME(now) << "\n";
791 : if (loopInfo->isJammed()) {
792 36 : loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
793 : } else {
794 14216 : loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
795 : }
796 14252 : loopInfo->lastGreenTime = now;
797 : }
798 : }
799 : // set the next event
800 : #ifdef DEBUG_PHASE_SELECTION
801 : if (DEBUG_COND) {
802 : std::cout << SIMTIME << " tl=" << getID() << " p=" << myStep
803 : << " nextTryMinDur=" << STEPS2TIME(getMinDur() - actDuration)
804 : << " nextTryEarliest=" << STEPS2TIME(getEarliest(prevStart)) << "\n";
805 : }
806 : #endif
807 195040 : SUMOTime minRetry = myStep != origStep ? 0 : TIME2STEPS(1);
808 195040 : return MAX3(minRetry, getMinDur() - actDuration, getEarliest(prevStart));
809 : }
810 :
811 :
812 : // ------------ "actuated" algorithm methods
813 : SUMOTime
814 93475 : MSActuatedTrafficLightLogic::duration(const double detectionGap) const {
815 : assert(getCurrentPhaseDef().isGreenPhase());
816 : assert((int)myPhases.size() > myStep);
817 93475 : const SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
818 : // ensure that minimum duration is kept
819 93475 : SUMOTime newDuration = getMinDur() - actDuration;
820 : // try to let the last detected vehicle pass the intersection (duration must be positive)
821 93475 : newDuration = MAX3(newDuration, TIME2STEPS(myDetectorGap - detectionGap), SUMOTime(1));
822 : // cut the decimal places to ensure that phases always have integer duration
823 93475 : if (newDuration % 1000 != 0) {
824 63073 : const SUMOTime totalDur = newDuration + actDuration;
825 63073 : newDuration = (totalDur / 1000 + 1) * 1000 - actDuration;
826 : }
827 : // ensure that the maximum duration is not exceeded
828 93475 : newDuration = MIN3(newDuration, getMaxDur() - actDuration, getLatest());
829 93475 : return newDuration;
830 : }
831 :
832 :
833 : double
834 276498 : MSActuatedTrafficLightLogic::gapControl() {
835 : //intergreen times should not be lengthend
836 : assert((int)myPhases.size() > myStep);
837 : double result = std::numeric_limits<double>::max();
838 276498 : if (MSGlobals::gUseMesoSim) {
839 : return result;
840 : }
841 : // switch off active colors
842 276498 : if (myShowDetectors) {
843 110573 : for (InductLoopInfo& loopInfo : myInductLoops) {
844 98073 : if (loopInfo.lastGreenTime < loopInfo.loop->getLastDetectionTime()) {
845 22097 : loopInfo.loop->setSpecialColor(&RGBColor::RED);
846 : } else {
847 75976 : loopInfo.loop->setSpecialColor(nullptr);
848 : }
849 : }
850 : }
851 276498 : if (!getCurrentPhaseDef().isGreenPhase()) {
852 : return result; // end current phase
853 : }
854 :
855 : // Checks, if the maxDuration is kept. No phase should last longer than maxDuration.
856 202369 : SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
857 202369 : if (actDuration >= getCurrentPhaseDef().maxDuration || maxLinkDurationReached() || getLatest() == 0) {
858 : #ifdef DEBUG_PHASE_SELECTION
859 : if (DEBUG_COND) {
860 : std::cout << SIMTIME << " actDuration=" << STEPS2TIME(actDuration) << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
861 : << " maxLinkDurationReached=" << maxLinkDurationReached() << " latest=" << STEPS2TIME(getLatest()) << "\n";
862 : }
863 : #endif
864 51997 : return result; // end current phase
865 : }
866 :
867 : // now the gapcontrol starts
868 544254 : for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
869 393882 : MSInductLoop* loop = loopInfo->loop;
870 : if (loopInfo->isJammed()) {
871 194 : loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
872 : } else {
873 393688 : loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
874 : }
875 393882 : const double actualGap = loop->getTimeSinceLastDetection();
876 393882 : if (actualGap < loopInfo->maxGap && !loopInfo->isJammed()) {
877 : result = MIN2(result, actualGap);
878 : }
879 : }
880 : return result;
881 : }
882 :
883 : int
884 1404 : MSActuatedTrafficLightLogic::decideNextPhase() {
885 1404 : const auto& cands = myPhases[myStep]->nextPhases;
886 : // decide by priority
887 : // first target is the default when there is no traffic
888 : // @note: to keep the current phase, even when there is no traffic, it must be added to 'next' explicitly
889 1404 : int result = cands.front();
890 : int maxPrio = 0;
891 1404 : SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
892 1404 : const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && !maxLinkDurationReached() && getLatest() > 0;
893 : if (canExtend) {
894 : // consider keeping the current phase until maxDur is reached
895 : // (only when there is still traffic in that phase)
896 1136 : int currentPrio = getPhasePriority(myStep);
897 : #ifdef DEBUG_PHASE_SELECTION
898 : std::cout << SIMTIME << " p=" << myStep << " loops=" << myInductLoopsForPhase[myStep].size() << " currentPrio=" << currentPrio << "\n";
899 : #endif
900 1136 : if (currentPrio > maxPrio) {
901 846 : result = myStep;
902 : maxPrio = currentPrio;
903 : }
904 : }
905 5538 : for (int step : cands) {
906 4134 : int target = getTarget(step);
907 4134 : int prio = getPhasePriority(target);
908 : #ifdef DEBUG_PHASE_SELECTION
909 : if (DEBUG_COND) {
910 : std::cout << SIMTIME << " p=" << myStep << " step=" << step << " target=" << target << " loops=" << myInductLoopsForPhase[target].size() << " prio=" << prio << "\n";
911 : }
912 : #endif
913 4134 : if (prio > maxPrio && canExtendLinkGreen(target)) {
914 : maxPrio = prio;
915 : result = step;
916 : }
917 : }
918 : // prevent starvation in phases that are not direct targets
919 9753 : for (const InductLoopInfo& loopInfo : myInductLoops) {
920 8367 : int prio = getDetectorPriority(loopInfo);
921 8367 : if (prio > maxPrio) {
922 18 : result = cands.front();
923 18 : if (result == myStep) {
924 0 : WRITE_WARNING("At actuated tlLogic '" + getID()
925 : + "', starvation at e1Detector '" + loopInfo.loop->getID()
926 : + "' which cannot be reached from the default phase " + toString(myStep) + ".");
927 : }
928 : // use default phase to reach other phases
929 : #ifdef DEBUG_PHASE_SELECTION
930 : if (DEBUG_COND) {
931 : std::cout << SIMTIME << " p=" << myStep << " loop=" << loopInfo.loop->getID() << " prio=" << prio << " next=" << result << "\n";
932 : }
933 : #endif
934 : break;
935 : }
936 : }
937 1404 : return result;
938 : }
939 :
940 :
941 : int
942 4299 : MSActuatedTrafficLightLogic::getTarget(int step) {
943 4299 : int origStep = step;
944 : // if step is a transition, find the upcoming green phase
945 7644 : while (!myPhases[step]->isGreenPhase()) {
946 3345 : if (myPhases[step]->nextPhases.size() > 0 && myPhases[step]->nextPhases.front() >= 0) {
947 2051 : if (myPhases[step]->nextPhases.size() > 1) {
948 0 : WRITE_WARNINGF(TL("At actuated tlLogic '%', transition phase % should not have multiple next phases"), getID(), toString(step));
949 : }
950 2051 : step = myPhases[step]->nextPhases.front();
951 : } else {
952 1294 : step = (step + 1) % (int)myPhases.size();
953 : }
954 3345 : if (step == origStep) {
955 0 : WRITE_WARNING("At actuated tlLogic '" + getID() + "', infinite transition loop from phase " + toString(origStep));
956 0 : return 0;
957 : }
958 : }
959 4299 : return step;
960 : }
961 :
962 : int
963 18556 : MSActuatedTrafficLightLogic::getDetectorPriority(const InductLoopInfo& loopInfo) const {
964 18556 : MSInductLoop* loop = loopInfo.loop;
965 18556 : const double actualGap = loop->getTimeSinceLastDetection();
966 18556 : if ((actualGap < loopInfo.maxGap && !loopInfo.isJammed())
967 18556 : || loopInfo.lastGreenTime < loop->getLastDetectionTime()) {
968 5143 : SUMOTime inactiveTime = MSNet::getInstance()->getCurrentTimeStep() - loopInfo.lastGreenTime;
969 : // @note. Inactive time could also be tracked regardless of current activity (to increase robustness in case of detection failure
970 5143 : if (inactiveTime > myInactiveThreshold) {
971 : #ifdef DEBUG_PHASE_SELECTION
972 : if (DEBUG_COND) {
973 : std::cout << " loop=" << loop->getID() << " gap=" << loop->getTimeSinceLastDetection() << " lastGreen=" << STEPS2TIME(loopInfo.lastGreenTime)
974 : << " lastDetection=" << STEPS2TIME(loop->getLastDetectionTime()) << " inactive=" << STEPS2TIME(inactiveTime) << "\n";
975 : }
976 : #endif
977 12 : return (int)STEPS2TIME(inactiveTime);
978 : } else {
979 : // give bonus to detectors that are currently served (if that phase can stil be extended)
980 5131 : if (loopInfo.servedPhase[myStep]) {
981 2483 : SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
982 2483 : const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && getLatest() > 0;
983 : #ifdef DEBUG_PHASE_SELECTION
984 : if (DEBUG_COND) {
985 : std::cout << " loop=" << loop->getID()
986 : << " actDuration=" << STEPS2TIME(actDuration)
987 : << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
988 : << " getLatest=" << STEPS2TIME(getLatest())
989 : << " canExtend=" << canExtend
990 : << "\n";
991 : }
992 : #endif
993 : if (canExtend) {
994 : return DEFAULT_CURRENT_PRIORITY;
995 : } else {
996 25 : return 0;
997 : }
998 : }
999 : return 1;
1000 : }
1001 : }
1002 : return 0;
1003 : }
1004 :
1005 : int
1006 5270 : MSActuatedTrafficLightLogic::getPhasePriority(int step) const {
1007 : int result = 0;
1008 15459 : for (const InductLoopInfo* loopInfo : myInductLoopsForPhase[step]) {
1009 10189 : result += getDetectorPriority(*loopInfo);
1010 : }
1011 5270 : return result;
1012 : }
1013 :
1014 :
1015 : void
1016 0 : MSActuatedTrafficLightLogic::setShowDetectors(bool show) {
1017 0 : myShowDetectors = show;
1018 0 : for (InductLoopInfo& loopInfo : myInductLoops) {
1019 0 : loopInfo.loop->setVisible(myShowDetectors);
1020 : }
1021 0 : }
1022 :
1023 :
1024 : bool
1025 152332 : MSActuatedTrafficLightLogic::maxLinkDurationReached() {
1026 152332 : if (myLinkMaxGreenTimes.empty()) {
1027 : return false;
1028 : }
1029 777 : for (int i = 0; i < myNumLinks; i++) {
1030 718 : if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i]) {
1031 : //std::cout << SIMTIME << " maxLinkDurationReached i=" << i << "\n";
1032 : return true;
1033 : }
1034 : }
1035 : return false;
1036 : }
1037 :
1038 : bool
1039 86 : MSActuatedTrafficLightLogic::canExtendLinkGreen(int target) {
1040 86 : if (myLinkMaxGreenTimes.empty()) {
1041 : return true;
1042 : }
1043 2 : const std::string& targetState = myPhases[target]->getState();
1044 26 : for (int i = 0; i < myNumLinks; i++) {
1045 24 : if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i] && (
1046 0 : targetState[i] == 'G' || targetState[i] == 'g')) {
1047 : //std::cout << SIMTIME << " cannotExtendLinkGreen target=" << target << " i=" << i << "\n";
1048 : return false;
1049 : }
1050 : }
1051 : return true;
1052 : }
1053 :
1054 : SUMOTime
1055 165 : MSActuatedTrafficLightLogic::getLinkMinDuration(int target) const {
1056 : SUMOTime result = 0;
1057 165 : if (target != myStep && myLinkMinGreenTimes.size() > 0) {
1058 165 : const std::string& state = myPhases[myStep]->getState();
1059 165 : const std::string& targetState = myPhases[target]->getState();
1060 2517 : for (int i = 0; i < myNumLinks; i++) {
1061 2352 : if (myLinkGreenTimes[i] < myLinkMinGreenTimes[i]
1062 604 : && (state[i] == 'G' || state[i] == 'g')
1063 2443 : && !(targetState[i] == 'G' || targetState[i] == 'g')) {
1064 67 : result = MAX2(result, myLinkMinGreenTimes[i] - myLinkGreenTimes[i]);
1065 : //std::cout << SIMTIME << " getLinkMinDuration myStep=" << myStep << " target=" << target << " i=" << i
1066 : // << " greenTime=" << STEPS2TIME(myLinkGreenTimes[i]) << " min=" << STEPS2TIME(myLinkMinGreenTimes[i]) << " result=" << STEPS2TIME(result) << "\n";
1067 : }
1068 : }
1069 : }
1070 165 : return result;
1071 : }
1072 :
1073 : int
1074 12061 : MSActuatedTrafficLightLogic::decideNextPhaseCustom(bool mustSwitch) {
1075 30071 : for (int next : getCurrentPhaseDef().nextPhases) {
1076 18383 : const MSPhaseDefinition* phase = myPhases[next];
1077 18383 : const std::string& condition = mustSwitch ? phase->finalTarget : phase->earlyTarget;
1078 : //std::cout << SIMTIME << " mustSwitch=" << mustSwitch << " condition=" << condition << "\n";
1079 18383 : if (condition != "" && evalExpression(condition)) {
1080 : return next;
1081 : }
1082 : }
1083 11688 : return mustSwitch ? getCurrentPhaseDef().nextPhases.back() : myStep;
1084 : }
1085 :
1086 :
1087 : double
1088 279782 : MSActuatedTrafficLightLogic::evalExpression(const std::string& condition) const {
1089 279782 : const size_t bracketOpen = condition.find('(');
1090 279782 : if (bracketOpen != std::string::npos) {
1091 : // find matching closing bracket
1092 : size_t bracketClose = std::string::npos;
1093 : int open = 1;
1094 259264 : for (size_t i = bracketOpen + 1; i < condition.size(); i++) {
1095 259264 : if (condition[i] == '(') {
1096 1866 : open++;
1097 257398 : } else if (condition[i] == ')') {
1098 21348 : open--;
1099 21348 : if (open == 0) {
1100 : bracketClose = i;
1101 : break;
1102 : }
1103 : }
1104 : }
1105 19482 : if (bracketClose == std::string::npos) {
1106 0 : throw ProcessError(TLF("Unmatched parentheses in condition %'", condition));
1107 : }
1108 : std::string cond2 = condition;
1109 19482 : const std::string inBracket = condition.substr(bracketOpen + 1, bracketClose - bracketOpen - 1);
1110 19482 : double bracketVal = evalExpression(inBracket);
1111 19482 : cond2.replace(bracketOpen, bracketClose - bracketOpen + 1, toString(bracketVal));
1112 : try {
1113 19482 : return evalExpression(cond2);
1114 0 : } catch (ProcessError& e) {
1115 0 : throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1116 0 : }
1117 : }
1118 560537 : std::vector<std::string> tokens = StringTokenizer(condition).getVector();
1119 : //std::cout << SIMTIME << " tokens(" << tokens.size() << ")=" << toString(tokens) << "\n";
1120 260300 : if (tokens.size() == 0) {
1121 0 : throw ProcessError(TLF("Invalid empty condition '%'", condition));
1122 260300 : } else if (tokens.size() == 1) {
1123 : try {
1124 163446 : return evalAtomicExpression(tokens[0]);
1125 0 : } catch (ProcessError& e) {
1126 0 : throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1127 0 : }
1128 96854 : } else if (tokens.size() == 2) {
1129 1244 : if (tokens[0] == "not") {
1130 : try {
1131 1244 : return evalAtomicExpression(tokens[1]) == 0. ? 1. : 0.;
1132 0 : } catch (ProcessError& e) {
1133 0 : throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1134 0 : }
1135 : } else {
1136 0 : throw ProcessError(TLF("Unsupported condition '%'", condition));
1137 : }
1138 95610 : } else if (tokens.size() == 3) {
1139 : // infix expression
1140 64962 : const double a = evalAtomicExpression(tokens[0]);
1141 64962 : const double b = evalAtomicExpression(tokens[2]);
1142 : const std::string& o = tokens[1];
1143 : //std::cout << SIMTIME << " o=" << o << " a=" << a << " b=" << b << "\n";
1144 : try {
1145 64962 : return evalTernaryExpression(a, o, b, condition);
1146 0 : } catch (ProcessError& e) {
1147 0 : throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1148 0 : }
1149 : } else {
1150 30648 : const int iEnd = (int)tokens.size() - 1;
1151 354202 : for (const std::string& o : OPERATOR_PRECEDENCE) {
1152 1637044 : for (int i = 1; i < iEnd; i++) {
1153 1313490 : if (tokens[i] == o) {
1154 : try {
1155 30648 : const double val = evalTernaryExpression(
1156 30648 : evalAtomicExpression(tokens[i - 1]), o,
1157 61296 : evalAtomicExpression(tokens[i + 1]), condition);
1158 30648 : std::vector<std::string> newTokens(tokens.begin(), tokens.begin() + (i - 1));
1159 61296 : newTokens.push_back(toString(val));
1160 30648 : newTokens.insert(newTokens.end(), tokens.begin() + (i + 2), tokens.end());
1161 30648 : return evalExpression(toString(newTokens));
1162 30648 : } catch (ProcessError& e) {
1163 0 : throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1164 0 : }
1165 : }
1166 : }
1167 : }
1168 0 : throw ProcessError("Parsing expressions with " + toString(tokens.size()) + " elements ('" + condition + "') is not supported");
1169 : }
1170 : return true;
1171 260300 : }
1172 :
1173 : double
1174 95610 : MSActuatedTrafficLightLogic::evalTernaryExpression(double a, const std::string& o, double b, const std::string& condition) const {
1175 185352 : if (o == "=" || o == "==") {
1176 19352 : return (double)(a == b);
1177 85114 : } else if (o == "<") {
1178 0 : return (double)(a < b);
1179 85114 : } else if (o == ">") {
1180 36816 : return (double)(a > b);
1181 56572 : } else if (o == "<=") {
1182 0 : return (double)(a <= b);
1183 56572 : } else if (o == ">=") {
1184 9156 : return (double)(a >= b);
1185 51944 : } else if (o == "!=") {
1186 4728 : return (double)(a != b);
1187 76405 : } else if (o == "or" || o == "||") {
1188 18227 : return (double)(a || b);
1189 40344 : } else if (o == "and" || o == "&&") {
1190 17834 : return (double)(a && b);
1191 11255 : } else if (o == "+") {
1192 6597 : return a + b;
1193 4658 : } else if (o == "-") {
1194 0 : return a - b;
1195 4658 : } else if (o == "*") {
1196 0 : return a * b;
1197 4658 : } else if (o == "/") {
1198 29 : if (b == 0) {
1199 87 : WRITE_ERRORF(TL("Division by 0 in condition '%'"), condition);
1200 29 : return 0;
1201 : }
1202 0 : return a / b;
1203 4629 : } else if (o == "%") {
1204 4628 : return fmod(a, b);
1205 1 : } else if (o == "**" || o == "^") {
1206 1 : return pow(a, b);
1207 : } else {
1208 0 : throw ProcessError("Unsupported operator '" + o + "' in condition '" + condition + "'");
1209 : }
1210 : }
1211 :
1212 : double
1213 4628 : MSActuatedTrafficLightLogic::evalCustomFunction(const std::string& fun, const std::string& arg) const {
1214 13884 : std::vector<std::string> args = StringTokenizer(arg, ",").getVector();
1215 : const Function& f = myFunctions.find(fun)->second;
1216 4628 : if ((int)args.size() != f.nArgs) {
1217 0 : throw ProcessError("Function '" + fun + "' requires " + toString(f.nArgs) + " arguments but " + toString(args.size()) + " were given");
1218 : }
1219 : std::vector<double> args2;
1220 11570 : for (auto a : args) {
1221 6942 : args2.push_back(evalExpression(a));
1222 : }
1223 4628 : myStack.push_back(myStack.back());
1224 4628 : myStack.back()["$0"] = 0;
1225 11570 : for (int i = 0; i < (int)args2.size(); i++) {
1226 6942 : myStack.back()["$" + toString(i + 1)] = args2[i];
1227 : }
1228 : try {
1229 : ConditionMap empty;
1230 4628 : executeAssignments(f.assignments, empty, myConditions);
1231 0 : } catch (ProcessError& e) {
1232 0 : throw ProcessError("Error when evaluating function '" + fun + "' with args '" + joinToString(args2, ",") + "' (" + e.what() + ")");
1233 0 : }
1234 4628 : double result = myStack.back()["$0"];
1235 : myStack.pop_back();
1236 4628 : return result;
1237 4628 : }
1238 :
1239 :
1240 : void
1241 293187 : MSActuatedTrafficLightLogic::executeAssignments(const AssignmentMap& assignments, ConditionMap& conditions, const ConditionMap& forbidden) const {
1242 344146 : for (const auto& assignment : assignments) {
1243 50959 : if (evalExpression(std::get<1>(assignment))) {
1244 : const std::string& id = std::get<0>(assignment);
1245 27972 : const double val = evalExpression(std::get<2>(assignment));
1246 : ConditionMap::iterator it = conditions.find(id);
1247 27972 : if (it != conditions.end()) {
1248 41622 : it->second = toString(val);
1249 7161 : } else if (forbidden.find(id) != forbidden.end()) {
1250 0 : throw ProcessError(TLF("Modifying global condition '%' is forbidden", id));
1251 : } else {
1252 7161 : myStack.back()[id] = val;
1253 : }
1254 : }
1255 : }
1256 293187 : }
1257 :
1258 :
1259 : double
1260 360683 : MSActuatedTrafficLightLogic::evalAtomicExpression(const std::string& expr) const {
1261 360683 : if (expr.size() == 0) {
1262 0 : throw ProcessError(TL("Invalid empty expression"));
1263 360683 : } else if (expr[0] == '!') {
1264 9388 : return evalAtomicExpression(expr.substr(1)) == 0. ? 1. : 0.;
1265 355989 : } else if (expr[0] == '-') {
1266 79 : return -evalAtomicExpression(expr.substr(1));
1267 : } else {
1268 : // check for 'operator:'
1269 355910 : const size_t pos = expr.find(':');
1270 355910 : if (pos == std::string::npos) {
1271 : auto it = myConditions.find(expr);
1272 317364 : if (it != myConditions.end()) {
1273 : // symbol lookup
1274 73858 : return evalExpression(it->second);
1275 : } else {
1276 : // look at stack
1277 : auto it2 = myStack.back().find(expr);
1278 243506 : if (it2 != myStack.back().end()) {
1279 16198 : return it2->second;
1280 : }
1281 : // must be a number
1282 227308 : return StringUtils::toDouble(expr);
1283 : }
1284 : } else {
1285 38546 : const std::string fun = expr.substr(0, pos);
1286 38546 : const std::string arg = expr.substr(pos + 1);
1287 38546 : if (fun == "z") {
1288 28013 : return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection();
1289 10533 : } else if (fun == "a") {
1290 : try {
1291 4760 : return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection() == 0;
1292 4154 : } catch (ProcessError&) {
1293 4154 : return retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(arg, expr, true)->getCurrentVehicleNumber();
1294 4154 : }
1295 10855 : } else if (fun == "g" || fun == "r") {
1296 : try {
1297 1109 : int linkIndex = StringUtils::toInt(arg);
1298 1109 : if (linkIndex >= 0 && linkIndex < myNumLinks) {
1299 1109 : const std::vector<SUMOTime>& times = fun == "g" ? myLinkGreenTimes : myLinkRedTimes;
1300 1109 : if (times.empty()) {
1301 : return 0;
1302 : }
1303 1097 : if (myLastTrySwitchTime < SIMSTEP) {
1304 : // times are only updated at the start of a phase where
1305 : // switching is possible (i.e. not during minDur).
1306 : // If somebody is looking at those values in the tracker
1307 : // this would be confusing
1308 288 : const LinkState ls = getCurrentPhaseDef().getSignalState(linkIndex);
1309 144 : if ((fun == "g" && (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR))
1310 516 : || (fun == "r" && (ls == LINKSTATE_TL_RED || ls == LINKSTATE_TL_REDYELLOW))) {
1311 138 : const SUMOTime currentGreen = SIMSTEP - myLastTrySwitchTime;
1312 138 : return STEPS2TIME(times[linkIndex] + currentGreen);
1313 : } else {
1314 : return 0;
1315 : }
1316 : } else {
1317 809 : return STEPS2TIME(times[linkIndex]);
1318 : }
1319 : }
1320 0 : } catch (NumberFormatException&) { }
1321 0 : throw ProcessError("Invalid link index '" + arg + "' in expression '" + expr + "'");
1322 4664 : } else if (fun == "c") {
1323 36 : return STEPS2TIME(getTimeInCycle());
1324 : } else {
1325 4628 : if (myFunctions.find(fun) == myFunctions.end()) {
1326 0 : throw ProcessError("Unsupported function '" + fun + "' in expression '" + expr + "'");
1327 : }
1328 4628 : return evalCustomFunction(fun, arg);
1329 : }
1330 : }
1331 : }
1332 : }
1333 :
1334 :
1335 : std::map<std::string, double>
1336 2835 : MSActuatedTrafficLightLogic::getDetectorStates() const {
1337 : std::map<std::string, double> result;
1338 33235 : for (auto li : myInductLoops) {
1339 58744 : result[li.loop->getID()] = li.loop->getOccupancy() > 0 ? 1 : 0;
1340 : }
1341 3671 : for (auto loop : myExtraLoops) {
1342 1626 : result[loop->getID()] = loop->getOccupancy() > 0 ? 1 : 0;
1343 : }
1344 3671 : for (auto loop : myExtraE2) {
1345 836 : result[loop->getID()] = loop->getCurrentVehicleNumber();
1346 : }
1347 2835 : return result;
1348 : }
1349 :
1350 : std::map<std::string, double>
1351 6778 : MSActuatedTrafficLightLogic::getConditions() const {
1352 : std::map<std::string, double> result;
1353 41003 : for (auto item : myConditions) {
1354 : if (myListedConditions.count(item.first) != 0) {
1355 : try {
1356 29497 : result[item.first] = evalExpression(item.second);
1357 0 : } catch (ProcessError& e) {
1358 0 : WRITE_ERRORF(TL("Error when retrieving conditions '%' for tlLogic '%' (%)"), item.first, getID(), e.what());
1359 0 : }
1360 : }
1361 34225 : }
1362 6778 : return result;
1363 : }
1364 :
1365 : const std::string
1366 21454 : MSActuatedTrafficLightLogic::getParameter(const std::string& key, const std::string defaultValue) const {
1367 42908 : if (StringUtils::startsWith(key, "condition.")) {
1368 3600 : const std::string cond = key.substr(10);
1369 : auto it = myConditions.find(cond);
1370 3600 : if (it != myConditions.end()) {
1371 3600 : return toString(evalExpression(it->second));
1372 : } else {
1373 0 : throw InvalidArgument("Unknown condition '" + cond + "' for actuated traffic light '" + getID() + "'");
1374 : }
1375 : } else {
1376 35708 : return MSSimpleTrafficLightLogic::getParameter(key, defaultValue);
1377 : }
1378 : }
1379 :
1380 : void
1381 24 : MSActuatedTrafficLightLogic::setParameter(const std::string& key, const std::string& value) {
1382 : // some pre-defined parameters can be updated at runtime
1383 96 : if (key == "detector-gap" || key == "passing-time" || key == "file" || key == "freq" || key == "vTypes"
1384 48 : || StringUtils::startsWith(key, "linkMaxDur")
1385 72 : || StringUtils::startsWith(key, "linkMinDur")) {
1386 0 : throw InvalidArgument(key + " cannot be changed dynamically for actuated traffic light '" + getID() + "'");
1387 24 : } else if (key == "max-gap") {
1388 5 : myMaxGap = StringUtils::toDouble(value);
1389 : // overwrite custom values
1390 45 : for (InductLoopInfo& loopInfo : myInductLoops) {
1391 40 : loopInfo.maxGap = myMaxGap;
1392 : }
1393 5 : Parameterised::setParameter(key, value);
1394 38 : } else if (StringUtils::startsWith(key, "max-gap:")) {
1395 0 : const std::string laneID = key.substr(8);
1396 0 : for (InductLoopInfo& loopInfo : myInductLoops) {
1397 0 : if (loopInfo.lane->getID() == laneID) {
1398 0 : loopInfo.maxGap = StringUtils::toDouble(value);
1399 0 : Parameterised::setParameter(key, value);
1400 : return;
1401 : }
1402 : }
1403 0 : throw InvalidArgument("Invalid lane '" + laneID + "' in key '" + key + "' for actuated traffic light '" + getID() + "'");
1404 19 : } else if (key == "jam-threshold") {
1405 2 : myJamThreshold = StringUtils::toDouble(value);
1406 : // overwrite custom values
1407 10 : for (InductLoopInfo& loopInfo : myInductLoops) {
1408 8 : loopInfo.jamThreshold = myJamThreshold;
1409 : }
1410 2 : Parameterised::setParameter(key, value);
1411 34 : } else if (StringUtils::startsWith(key, "jam-threshold:")) {
1412 1 : const std::string laneID = key.substr(14);
1413 3 : for (InductLoopInfo& loopInfo : myInductLoops) {
1414 3 : if (loopInfo.lane->getID() == laneID) {
1415 1 : loopInfo.jamThreshold = StringUtils::toDouble(value);
1416 1 : Parameterised::setParameter(key, value);
1417 : return;
1418 : }
1419 : }
1420 0 : throw InvalidArgument("Invalid lane '" + laneID + "' in key '" + key + "' for actuated traffic light '" + getID() + "'");
1421 16 : } else if (key == "show-detectors") {
1422 1 : myShowDetectors = StringUtils::toBool(value);
1423 1 : Parameterised::setParameter(key, value);
1424 5 : for (InductLoopInfo& loopInfo : myInductLoops) {
1425 4 : loopInfo.loop->setVisible(myShowDetectors);
1426 : }
1427 15 : } else if (key == "inactive-threshold") {
1428 0 : myInactiveThreshold = string2time(value);
1429 0 : Parameterised::setParameter(key, value);
1430 : } else {
1431 15 : MSSimpleTrafficLightLogic::setParameter(key, value);
1432 : }
1433 : }
1434 :
1435 :
1436 : /****************************************************************************/
|