Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2025 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_PHASE_SELECTION_CUSTOM
45 : #define DEBUG_COND (getID()=="C")
46 :
47 : // ===========================================================================
48 : // static members
49 : // ===========================================================================
50 : const std::vector<std::string> MSActuatedTrafficLightLogic::OPERATOR_PRECEDENCE({
51 : "**", "^", "*", "/", "+", "-", "%",
52 : "=", "==", "!=", "<", ">", "<=", ">=",
53 : "and", "&&", "or", "||",
54 : });
55 :
56 : // ===========================================================================
57 : // parameter defaults definitions
58 : // ===========================================================================
59 : #define DEFAULT_MAX_GAP "3.0"
60 : #define DEFAULT_PASSING_TIME "1.9"
61 : #define DEFAULT_DETECTOR_GAP "2.0"
62 : #define DEFAULT_INACTIVE_THRESHOLD "180"
63 : #define DEFAULT_CURRENT_PRIORITY 10
64 : #define DEFAULT_CROSSING_PRIORITY 100
65 :
66 : #define DEFAULT_LENGTH_WITH_GAP 7.5
67 : #define DEFAULT_BIKE_LENGTH_WITH_GAP (getDefaultVehicleLength(SVC_BICYCLE) + 0.5)
68 : #define DEFAULT_STATIC_MINDUR TIME2STEPS(0) // loop position for non-stretchable phases
69 :
70 : #define NO_DETECTOR "NO_DETECTOR"
71 : #define DEFAULT_CONDITION "DEFAULT"
72 :
73 : // ===========================================================================
74 : // method definitions
75 : // ===========================================================================
76 494 : MSActuatedTrafficLightLogic::MSActuatedTrafficLightLogic(MSTLLogicControl& tlcontrol,
77 : const std::string& id, const std::string& programID,
78 : const SUMOTime offset,
79 : const Phases& phases,
80 : int step, SUMOTime delay,
81 : const Parameterised::Map& parameter,
82 : const std::string& basePath,
83 : const ConditionMap& conditions,
84 : const AssignmentMap& assignments,
85 494 : const FunctionMap& functions) :
86 : MSSimpleTrafficLightLogic(tlcontrol, id, programID, offset, TrafficLightType::ACTUATED, phases, step, delay, parameter),
87 494 : myHasMultiTarget(false),
88 494 : myLastTrySwitchTime(0),
89 : myConditions(conditions),
90 494 : myAssignments(assignments),
91 : myFunctions(functions),
92 494 : myTraCISwitch(false),
93 1482 : myDetectorPrefix(id + "_" + programID + "_") {
94 988 : myMaxGap = StringUtils::toDouble(getParameter("max-gap", DEFAULT_MAX_GAP));
95 988 : myJamThreshold = StringUtils::toDouble(getParameter("jam-threshold", OptionsCont::getOptions().getValueString("tls.actuated.jam-threshold")));
96 988 : myPassingTime = StringUtils::toDouble(getParameter("passing-time", DEFAULT_PASSING_TIME));
97 988 : myDetectorGap = StringUtils::toDouble(getParameter("detector-gap", DEFAULT_DETECTOR_GAP));
98 988 : myInactiveThreshold = string2time(getParameter("inactive-threshold", DEFAULT_INACTIVE_THRESHOLD));
99 988 : myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
100 988 : myBuildAllDetectors = StringUtils::toBool(getParameter("build-all-detectors", "false"));
101 988 : myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
102 1482 : myFreq = TIME2STEPS(StringUtils::toDouble(getParameter("freq", "300")));
103 988 : myVehicleTypes = getParameter("vTypes", "");
104 :
105 988 : if (hasParameter("hide-conditions")) {
106 8 : std::vector<std::string> hidden = StringTokenizer(getParameter("hide-conditions", "")).getVector();
107 4 : std::set<std::string> hiddenSet(hidden.begin(), hidden.end());
108 28 : for (auto item : myConditions) {
109 : if (hiddenSet.count(item.first) == 0) {
110 : myListedConditions.insert(item.first);
111 : }
112 : }
113 4 : } else {
114 980 : const bool showAll = getParameter("show-conditions", "") == "";
115 980 : std::vector<std::string> shown = StringTokenizer(getParameter("show-conditions", "")).getVector();
116 490 : std::set<std::string> shownSet(shown.begin(), shown.end());
117 561 : for (auto item : myConditions) {
118 71 : if (showAll || shownSet.count(item.first) != 0) {
119 : myListedConditions.insert(item.first);
120 : }
121 : }
122 490 : }
123 988 : if (hasParameter("extra-detectors")) {
124 2 : const std::string extraIDs = getParameter("extra-detectors", "");
125 4 : for (std::string customID : StringTokenizer(extraIDs).getVector()) {
126 : try {
127 2 : myExtraLoops.push_back(retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(customID, extraIDs, true));
128 1 : } catch (ProcessError&) {
129 1 : myExtraE2.push_back(retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(customID, extraIDs, true));
130 1 : }
131 1 : }
132 : }
133 494 : myStack.push_back(std::map<std::string, double>());
134 494 : }
135 :
136 :
137 2465 : MSActuatedTrafficLightLogic::~MSActuatedTrafficLightLogic() { }
138 :
139 : void
140 494 : MSActuatedTrafficLightLogic::init(NLDetectorBuilder& nb) {
141 494 : MSTrafficLightLogic::init(nb);
142 494 : initAttributeOverride();
143 494 : initSwitchingRules();
144 3963 : for (int i = 0; i < (int)myPhases.size(); i++) {
145 3469 : initTargets(i);
146 : }
147 494 : if (myLanes.size() == 0) {
148 : // must be an older network
149 0 : WRITE_WARNINGF(TL("Traffic light '%' does not control any links"), getID());
150 : }
151 : bool warn = true; // warn only once
152 494 : const int numLinks = (int)myLinks.size();
153 :
154 : // Detector position should be computed based on road speed. If the position
155 : // is quite far away and the minDur is short this may cause the following
156 : // problems:
157 : //
158 : // 1) high flow failure:
159 : // In a standing queue, no vehicle touches the detector.
160 : // By the time the queue advances, the detector gap has been exceeded and the phase terminates prematurely
161 : //
162 : // 2) low flow failure
163 : // The standing queue is fully between stop line and detector and there are no further vehicles.
164 : // The minDur is too short to let all vehicles pass
165 : //
166 : // Problem 2) is not so critical because there is less potential for
167 : // jamming in a low-flow situation. In contrast, problem 1) should be
168 : // avoided as it has big jamming potential. We compute an upper bound for the
169 : // detector distance to avoid it
170 :
171 :
172 494 : std::set<int> multiNextTargets = getMultiNextTargets();
173 : // change values for setting the loops and lanestate-detectors, here
174 : //SUMOTime inductLoopInterval = 1; //
175 : // build the induct loops
176 : std::map<const MSLane*, MSInductLoop*> laneInductLoopMap;
177 : std::map<MSInductLoop*, int> inductLoopInfoMap; // retrieve junction entry lane in case loops are placed further upstream (and other properties)
178 494 : int detEdgeIndex = -1;
179 494 : int detLaneIndex = 0;
180 988 : const double detDefaultLength = StringUtils::toDouble(getParameter("detector-length",
181 988 : OptionsCont::getOptions().getValueString("tls.actuated.detector-length")));
182 : MSEdge* prevDetEdge = nullptr;
183 8218 : for (LaneVector& lanes : myLanes) {
184 15444 : for (MSLane* lane : lanes) {
185 7720 : const std::string customID = getParameter(lane->getID());
186 7720 : if (noVehicles(lane->getPermissions()) && customID == "") {
187 : // do not build detectors on green verges or sidewalks
188 804 : continue;
189 : }
190 6916 : if (laneInductLoopMap.find(lane) != laneInductLoopMap.end()) {
191 : // only build one detector per lane
192 3167 : continue;
193 : }
194 3749 : const SUMOTime minDur = getMinimumMinDuration(lane, multiNextTargets);
195 3749 : if (minDur == std::numeric_limits<SUMOTime>::max() && customID == "" && !myBuildAllDetectors) {
196 : // only build detector if this lane is relevant for an actuated phase
197 182 : continue;
198 : }
199 3567 : double length = lane->getLength();
200 : double ilpos;
201 : double inductLoopPosition;
202 3567 : MSInductLoop* loop = nullptr;
203 3567 : if (&lane->getEdge() != prevDetEdge) {
204 1930 : detEdgeIndex++;
205 1930 : detLaneIndex = 0;
206 : prevDetEdge = &lane->getEdge();
207 : } else {
208 1637 : detLaneIndex++;
209 : }
210 3567 : const bool isBikeLane = (lane->getPermissions() & ~SVC_PEDESTRIAN) == SVC_BICYCLE;
211 3567 : const double defaultLength = isBikeLane ? DEFAULT_BIKE_LENGTH_WITH_GAP : DEFAULT_LENGTH_WITH_GAP;
212 3567 : if (customID == "") {
213 3225 : const double speed = isBikeLane ? DEFAULT_BICYCLE_SPEED : lane->getSpeedLimit();
214 6450 : inductLoopPosition = MIN2(
215 3225 : myDetectorGap * speed,
216 3225 : (STEPS2TIME(minDur) / myPassingTime + 0.5) * defaultLength);
217 :
218 : // check whether the lane is long enough
219 3225 : ilpos = length - inductLoopPosition;
220 3225 : MSLane* placementLane = lane;
221 4390 : while (ilpos < 0 && placementLane->getIncomingLanes().size() == 1
222 4452 : && placementLane->getIncomingLanes().front().viaLink->getCorrespondingEntryLink()->getTLLogic() == nullptr) {
223 545 : placementLane = placementLane->getLogicalPredecessorLane();
224 545 : ilpos += placementLane->getLength();
225 : }
226 3225 : if (ilpos < 0) {
227 : ilpos = 0;
228 : }
229 : // Build the induct loop and set it into the container
230 3225 : const double detLength = getDouble("detector-length:" + lane->getID(), detDefaultLength);
231 9675 : std::string id = myDetectorPrefix + "D" + toString(detEdgeIndex) + "." + toString(detLaneIndex);
232 3225 : loop = static_cast<MSInductLoop*>(nb.createInductLoop(id, placementLane, ilpos, detLength, "", myVehicleTypes, "", (int)PersonMode::NONE, myShowDetectors));
233 3225 : MSNet::getInstance()->getDetectorControl().add(SUMO_TAG_INDUCTION_LOOP, loop, myFile, myFreq);
234 342 : } else if (customID == NO_DETECTOR) {
235 240 : continue;
236 : } else {
237 204 : loop = dynamic_cast<MSInductLoop*>(MSNet::getInstance()->getDetectorControl().getTypedDetectors(SUMO_TAG_INDUCTION_LOOP).get(customID));
238 102 : if (loop == nullptr) {
239 0 : throw ProcessError(TLF("Unknown inductionLoop '%' given as custom detector for actuated tlLogic '%', program '%.", customID, getID(), getProgramID()));
240 : }
241 : ilpos = loop->getPosition();
242 102 : inductLoopPosition = length - ilpos;
243 : }
244 3327 : const double maxGap = getDouble("max-gap:" + lane->getID(), myMaxGap);
245 3327 : const double jamThreshold = getDouble("jam-threshold:" + lane->getID(), myJamThreshold);
246 3327 : laneInductLoopMap[lane] = loop;
247 3327 : inductLoopInfoMap[loop] = (int)myInductLoops.size();
248 3327 : myInductLoops.push_back(InductLoopInfo(loop, lane, (int)myPhases.size(), maxGap, jamThreshold));
249 :
250 3327 : if (warn && floor(floor(inductLoopPosition / defaultLength) * myPassingTime) > STEPS2TIME(minDur)) {
251 : // warn if the minGap is insufficient to clear vehicles between stop line and detector
252 0 : WRITE_WARNINGF(TL("At actuated tlLogic '%', minDur % is too short for a detector gap of %m."), getID(), time2string(minDur), toString(inductLoopPosition));
253 : warn = false;
254 : }
255 : }
256 : }
257 : // assign loops to phase index (myInductLoopsForPhase)
258 : // 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
259 : // greenMinor is ambiguous as vehicles may not be able to drive
260 : // Under the following condition we allow actuation from minor link:
261 : // check1a : the minor link is minor in all phases
262 : // check1b : there is another major link from the same lane in the current phase
263 : // check1e : the conflict is only with bikes/pedestrians (i.e. for a right turn, also left turn with no oncoming traffic)
264 : // check1f : the conflict is only with a link from the same edge
265 : // (Under these conditions we assume that the minor link is unimportant and traffic is mostly for the major link)
266 : //
267 : // check1c: when the edge has only one lane, we treat greenMinor as green as there would be no actuation otherwise
268 : // check1d: for turnarounds 1b is sufficient and we do not require 1a
269 : //
270 : // 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
271 : //
272 : // if a jamThreshold is specificed for the loop, all checks are ignored
273 :
274 : // also assign loops to link index for validation:
275 : // check if all links from actuated phases (minDur != maxDur) have an inductionloop in at least one phase
276 : const SVCPermissions motorized = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
277 : std::map<int, std::set<MSInductLoop*> > linkToLoops;
278 : std::set<int> actuatedLinks;
279 :
280 494 : std::vector<bool> neverMajor(numLinks, true);
281 3963 : for (const MSPhaseDefinition* phase : myPhases) {
282 : const std::string& state = phase->getState();
283 67112 : for (int i = 0; i < numLinks; i++) {
284 63643 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
285 : neverMajor[i] = false;
286 : }
287 : }
288 : }
289 494 : std::vector<bool> oneLane(numLinks, false);
290 494 : std::vector<bool> turnaround(numLinks, true);
291 8218 : for (int i = 0; i < numLinks; i++) {
292 13091 : for (MSLane* lane : getLanesAt(i)) {
293 : // only count motorized vehicle lanes
294 : int numMotorized = 0;
295 27058 : for (MSLane* l : lane->getEdge().getLanes()) {
296 19356 : if ((l->getPermissions() & motorized) != 0) {
297 12751 : numMotorized++;
298 : }
299 : }
300 7702 : if (numMotorized == 1) {
301 : oneLane[i] = true;
302 2335 : break;
303 : }
304 : }
305 8457 : for (MSLink* link : getLinksAt(i)) {
306 7547 : if (!link->isTurnaround()) {
307 : turnaround[i] = false;
308 6814 : break;
309 : }
310 : }
311 : }
312 :
313 3963 : for (const MSPhaseDefinition* phase : myPhases) {
314 3469 : const int phaseIndex = (int)myInductLoopsForPhase.size();
315 : std::set<MSInductLoop*> loops;
316 : if (phase->isActuated() || multiNextTargets.count(phaseIndex) != 0) {
317 : const std::string& state = phase->getState();
318 : // collect indices of all green links for the phase
319 : std::set<int> greenLinks;
320 : // green links that could jam
321 : std::set<int> greenLinksPermissive;
322 : // collect green links for each induction loops (in this phase)
323 : std::map<MSInductLoop*, std::set<int> > loopLinks;
324 :
325 26856 : for (int i = 0; i < numLinks; i++) {
326 25430 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
327 : greenLinks.insert(i);
328 : if (phase->isActuated()) {
329 : actuatedLinks.insert(i);
330 : }
331 :
332 15236 : for (MSLink* link : getLinksAt(i)) {
333 7635 : if (link->getLane()->isCrossing()) {
334 2517 : while (myCrossingsForPhase.size() < myPhases.size()) {
335 1394 : myCrossingsForPhase.push_back(std::vector<const MSLink*>());
336 : }
337 1123 : myCrossingsForPhase[phaseIndex].push_back(link);
338 : }
339 : }
340 :
341 17829 : } else if (state[i] == LINKSTATE_TL_GREEN_MINOR) {
342 761 : if (((neverMajor[i] || turnaround[i]) // check1a, 1d
343 2599 : && hasMajor(state, getLanesAt(i))) // check1b
344 998 : || oneLane[i] // check1c
345 4132 : || weakConflict(i, state)) { // check1e, check1f
346 : greenLinks.insert(i);
347 2370 : if (!turnaround[i]) {
348 : if (phase->isActuated()) {
349 : actuatedLinks.insert(i);
350 : }
351 : }
352 : } else {
353 : greenLinksPermissive.insert(i);
354 : }
355 : }
356 : #ifdef DEBUG_DETECTORS
357 : if (DEBUG_COND) {
358 : std::cout << " phase=" << phaseIndex << " i=" << i << " state=" << state[i] << " green=" << greenLinks.count(i) << " oneLane=" << oneLane[i]
359 : << " turn=" << turnaround[i] << " loopLanes=";
360 : for (MSLane* lane : getLanesAt(i)) {
361 : if (laneInductLoopMap.count(lane) != 0) {
362 : std::cout << lane->getID() << " ";
363 : }
364 : }
365 : std::cout << "\n";
366 : }
367 : #endif
368 76350 : for (MSLane* lane : getLanesAt(i)) {
369 : if (laneInductLoopMap.count(lane) != 0) {
370 21517 : loopLinks[laneInductLoopMap[lane]].insert(i);
371 : }
372 : }
373 : }
374 12864 : for (auto& item : loopLinks) {
375 11438 : MSInductLoop* loop = item.first;
376 11438 : const InductLoopInfo& info = myInductLoops[inductLoopInfoMap[loop]];
377 11438 : const MSLane* loopLane = info.lane;
378 : bool usable = true;
379 : bool foundUsable = false;
380 : // check1
381 32409 : for (int j : item.second) {
382 13013 : if (greenLinks.count(j) == 0 && (info.jamThreshold <= 0 || greenLinksPermissive.count(j) == 0)) {
383 : usable = false;
384 : #ifdef DEBUG_DETECTORS
385 : if (DEBUG_COND) {
386 : std::cout << " phase=" << phaseIndex << " check1: loopLane=" << loopLane->getID() << " notGreen=" << j << " oneLane[j]=" << oneLane[j] << "\n";
387 : }
388 : #endif
389 : } else {
390 : foundUsable = true;
391 : }
392 : }
393 11438 : if (!usable && foundUsable && info.jamThreshold > 0) {
394 : // permit green even when the same lane has green and red links (if we have jamDetection)
395 : usable = true;
396 : }
397 : // check2 (skip if we have jam detection)
398 11438 : if (usable && info.jamThreshold <= 0) {
399 10612 : for (MSLink* link : loopLane->getLinkCont()) {
400 6749 : if (link->isTurnaround()) {
401 961 : continue;
402 : }
403 5788 : const MSLane* next = link->getLane();
404 : if (laneInductLoopMap.count(next) != 0) {
405 640 : MSInductLoop* nextLoop = laneInductLoopMap[next];
406 1310 : for (int j : loopLinks[nextLoop]) {
407 : if (greenLinks.count(j) == 0) {
408 : usable = false;
409 : #ifdef DEBUG_DETECTORS
410 : if (DEBUG_COND) std::cout << " phase=" << phaseIndex << " check2: loopLane=" << loopLane->getID()
411 : << " nextLane=" << next->getID() << " nextLink=" << j << " nextState=" << state[j] << "\n";
412 : #endif
413 : break;
414 : }
415 : }
416 : }
417 : }
418 : }
419 :
420 4333 : if (usable) {
421 4320 : loops.insert(item.first);
422 : #ifdef DEBUG_DETECTORS
423 : if (DEBUG_COND) {
424 : std::cout << " phase=" << phaseIndex << " usableLoops=" << item.first->getID() << " links=" << joinToString(item.second, " ") << "\n";
425 : }
426 : #endif
427 11908 : for (int j : item.second) {
428 7588 : linkToLoops[j].insert(item.first);
429 : }
430 : }
431 : }
432 1426 : if (loops.size() == 0 && phase->isActuated()) {
433 24 : WRITE_WARNINGF(TL("At actuated tlLogic '%', actuated phase % has no controlling detector."), getID(), toString(phaseIndex));
434 : }
435 : }
436 : #ifdef DEBUG_DETECTORS
437 : if (DEBUG_COND) {
438 : std::cout << " phase=" << phaseIndex << " loops=" << joinNamedToString(loops, " ") << "\n";
439 : }
440 : if (DEBUG_COND) {
441 : std::cout << " linkToLoops:\n";
442 : for (auto item : linkToLoops) {
443 : std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
444 : }
445 : }
446 : #endif
447 : std::vector<InductLoopInfo*> loopInfos;
448 3469 : myInductLoopsForPhase.push_back(loopInfos);
449 7789 : for (MSInductLoop* loop : loops) {
450 47139 : for (InductLoopInfo& loopInfo : myInductLoops) {
451 42819 : if (loopInfo.loop == loop) {
452 4320 : myInductLoopsForPhase.back().push_back(&loopInfo);
453 4320 : loopInfo.servedPhase[phaseIndex] = true;
454 : }
455 : }
456 : }
457 3469 : }
458 : #ifdef DEBUG_DETECTORS
459 : if (DEBUG_COND) {
460 : std::cout << "final linkToLoops:\n";
461 : for (auto item : linkToLoops) {
462 : std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
463 : }
464 : }
465 : #endif
466 : std::vector<int> warnLinks;
467 7281 : for (int i : actuatedLinks) {
468 7933 : if (linkToLoops[i].size() == 0 && myLinks[i].size() > 0
469 7858 : && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
470 610 : if (getParameter(myLinks[i].front()->getLaneBefore()->getID()) != NO_DETECTOR) {
471 65 : warnLinks.push_back(i);
472 : }
473 : }
474 : }
475 494 : if (warnLinks.size() > 0) {
476 57 : WRITE_WARNINGF(TL("At actuated tlLogic '%', linkIndex % has no controlling detector."), getID(), joinToString(warnLinks, ","));
477 : }
478 : // parse maximum green times for each link (optional)
479 1459 : for (const auto& kv : getParametersMap()) {
480 1930 : if (StringUtils::startsWith(kv.first, "linkMaxDur:")) {
481 1 : int link = StringUtils::toInt(kv.first.substr(11));
482 1 : if (link < 0 || link >= myNumLinks) {
483 0 : WRITE_ERRORF(TL("Invalid link '%' given as linkMaxDur parameter for actuated tlLogic '%', program '%."), kv.first.substr(11), getID(), getProgramID());
484 0 : continue;
485 : }
486 1 : if (myLinkMaxGreenTimes.empty()) {
487 1 : myLinkMaxGreenTimes = std::vector<SUMOTime>(myNumLinks, std::numeric_limits<SUMOTime>::max());
488 : }
489 1 : myLinkMaxGreenTimes[link] = string2time(kv.second);
490 1928 : } else if (StringUtils::startsWith(kv.first, "linkMinDur:")) {
491 9 : int link = StringUtils::toInt(kv.first.substr(11));
492 9 : if (link < 0 || link >= myNumLinks) {
493 0 : WRITE_ERRORF(TL("Invalid link '%' given as linkMinDur parameter for actuated tlLogic '%', program '%."), kv.first.substr(11), getID(), getProgramID());
494 0 : continue;
495 : }
496 9 : if (myLinkMinGreenTimes.empty()) {
497 2 : myLinkMinGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
498 : }
499 9 : myLinkMinGreenTimes[link] = string2time(kv.second);
500 : }
501 : }
502 494 : bool haveSwitchingRules = myConditions.size() > 0 || myAssignments.size() > 0 || myFunctions.size() > 0;
503 3803 : for (auto sr : mySwitchingRules) {
504 3339 : if (sr.enabled) {
505 : haveSwitchingRules = true;
506 : break;
507 : }
508 : }
509 494 : if (myLinkMaxGreenTimes.size() > 0 || myLinkMinGreenTimes.size() > 0 || haveSwitchingRules) {
510 34 : myLinkGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
511 34 : myLinkRedTimes = std::vector<SUMOTime>(myNumLinks, 0);
512 : }
513 : //std::cout << SIMTIME << " linkMaxGreenTimes=" << toString(myLinkMaxGreenTimes) << "\n";
514 988 : }
515 :
516 :
517 : bool
518 966 : MSActuatedTrafficLightLogic::weakConflict(int tlIndex, const std::string& state) const {
519 1088 : for (MSLink* link : getLinksAt(tlIndex)) {
520 : int linkIndex = link->getIndex();
521 : const MSJunction* junction = link->getJunction();
522 12227 : for (int i = 0; i < (int)myLinks.size(); i++) {
523 12105 : if (i == tlIndex) {
524 522 : continue;
525 : }
526 11583 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
527 10357 : for (MSLink* foe : getLinksAt(i)) {
528 : // junction logic is based on junction link index rather than tl index
529 : int foeIndex = foe->getIndex();
530 : const MSJunction* junction2 = foe->getJunction();
531 5293 : if (junction == junction2) {
532 4143 : const MSJunctionLogic* logic = junction->getLogic();
533 : //std::cout << " greenLink=" << i << " isFoe=" << logic->getFoesFor(linkIndex).test(foeIndex) << "\n";
534 4143 : if (logic->getFoesFor(linkIndex).test(foeIndex)
535 1134 : && (foe->getPermissions() & ~SVC_VULNERABLE) != 0 // check1e
536 4980 : && &foe->getLaneBefore()->getEdge() != &link->getLaneBefore()->getEdge()) { // check1f
537 : //std::cout << " strongConflict " << tlIndex << " in phase " << state << " with link " << foe->getTLIndex() << "\n";
538 : return false;
539 : }
540 : }
541 : }
542 :
543 : }
544 : }
545 : }
546 : //std::cout << " weakConflict " << tlIndex << " in phase " << state << "\n";
547 : return true;
548 : }
549 :
550 :
551 : SUMOTime
552 270619 : MSActuatedTrafficLightLogic::getMinDur(int step) const {
553 270619 : step = step < 0 ? myStep : step;
554 270619 : const MSPhaseDefinition* p = myPhases[step];
555 270619 : return p->minDuration != MSPhaseDefinition::OVERRIDE_DURATION
556 270619 : ? p->minDuration
557 274447 : : TIME2STEPS(evalExpression(myConditions.find("minDur:" + toString(step))->second));
558 : }
559 :
560 : SUMOTime
561 107263 : MSActuatedTrafficLightLogic::getMaxDur(int step) const {
562 107263 : step = step < 0 ? myStep : step;
563 107263 : const MSPhaseDefinition* p = myPhases[step];
564 107263 : return p->maxDuration != MSPhaseDefinition::OVERRIDE_DURATION
565 107263 : ? p->maxDuration
566 111019 : : TIME2STEPS(evalExpression(myConditions.find("maxDur:" + toString(step))->second));
567 : }
568 :
569 : SUMOTime
570 207051 : MSActuatedTrafficLightLogic::getEarliestEnd(int step) const {
571 207051 : step = step < 0 ? myStep : step;
572 207051 : const MSPhaseDefinition* p = myPhases[step];
573 207051 : return p->earliestEnd != MSPhaseDefinition::OVERRIDE_DURATION
574 207051 : ? p->earliestEnd
575 210807 : : TIME2STEPS(evalExpression(myConditions.find("earliestEnd:" + toString(step))->second));
576 : }
577 :
578 : SUMOTime
579 244300 : MSActuatedTrafficLightLogic::getLatestEnd(int step) const {
580 244300 : step = step < 0 ? myStep : step;
581 244300 : const MSPhaseDefinition* p = myPhases[step];
582 244300 : return p->latestEnd != MSPhaseDefinition::OVERRIDE_DURATION
583 244300 : ? p->latestEnd
584 248056 : : TIME2STEPS(evalExpression(myConditions.find("latestEnd:" + toString(step))->second));
585 : }
586 :
587 :
588 : void
589 494 : MSActuatedTrafficLightLogic::initAttributeOverride() {
590 : const SUMOTime ovrd = MSPhaseDefinition::OVERRIDE_DURATION;
591 3963 : for (int i = 0; i < (int)myPhases.size(); i++) {
592 3469 : MSPhaseDefinition* phase = myPhases[i];
593 3469 : const std::string errorSuffix = "' for overriding attribute in phase " + toString(i) + " of tlLogic '" + getID() + "' in program '" + getProgramID() + "'.";
594 3469 : if (phase->minDuration == ovrd) {
595 4 : const std::string cond = "minDur:" + toString(i);
596 : if (myConditions.count(cond) == 0) {
597 0 : throw ProcessError("Missing condition '" + cond + errorSuffix);
598 : }
599 : }
600 3469 : if (phase->maxDuration == ovrd) {
601 4 : const std::string cond = "maxDur:" + toString(i);
602 : if (myConditions.count(cond) == 0) {
603 0 : throw ProcessError("Missing condition '" + cond + errorSuffix);
604 : }
605 : }
606 3469 : if (phase->earliestEnd == ovrd) {
607 4 : const std::string cond = "earliestEnd:" + toString(i);
608 : if (myConditions.count(cond) == 0) {
609 0 : throw ProcessError("Missing condition '" + cond + errorSuffix);
610 : }
611 : }
612 3469 : if (phase->latestEnd == ovrd) {
613 4 : const std::string cond = "latestEnd:" + toString(i);
614 : if (myConditions.count(cond) == 0) {
615 0 : throw ProcessError("Missing condition '" + cond + errorSuffix);
616 : }
617 : }
618 : }
619 494 : }
620 :
621 :
622 : void
623 494 : MSActuatedTrafficLightLogic::initSwitchingRules() {
624 3963 : for (int i = 0; i < (int)myPhases.size(); i++) {
625 3469 : SwitchingRules sr;
626 3469 : MSPhaseDefinition* phase = myPhases[i];
627 3469 : std::vector<int> nextPhases = phase->nextPhases;
628 3469 : if (nextPhases.size() == 0) {
629 3284 : nextPhases.push_back((i + 1) % (int)myPhases.size());
630 185 : } else if (nextPhases.size() > 1) {
631 76 : myHasMultiTarget = true;
632 : }
633 7051 : for (int next : nextPhases) {
634 3582 : if (next >= 0 && next < (int)myPhases.size()) {
635 3582 : const MSPhaseDefinition* nextPhase = myPhases[next];
636 3582 : if (nextPhase->earlyTarget != "" || nextPhase->finalTarget != "") {
637 73 : sr.enabled = true;
638 : }
639 : }
640 : }
641 : // simplifies later code
642 3469 : phase->nextPhases = nextPhases;
643 3469 : mySwitchingRules.push_back(sr);
644 3469 : }
645 494 : }
646 :
647 :
648 : void
649 3469 : MSActuatedTrafficLightLogic::initTargets(int step) {
650 : // next -> target -> transitionTime starting from step
651 : std::map<int, std::map<int, SUMOTime> > reached;
652 3469 : const std::vector<int>& next = myPhases[step]->nextPhases;
653 7051 : for (int n : next) {
654 3582 : findTargets(step, n, 0, reached[n]);
655 : }
656 35516 : for (int target = 0; target < (int)myPhases.size(); target++) {
657 32047 : int bestNext = next[0];
658 : SUMOTime bestTime = SUMOTime_MAX;
659 66423 : for (auto item : reached) {
660 : auto it = item.second.find(target);
661 34376 : if (it != item.second.end()) {
662 14687 : SUMOTime transitionTime = it->second;
663 14687 : if (transitionTime < bestTime) {
664 : bestTime = transitionTime;
665 14438 : bestNext = item.first;
666 : }
667 : }
668 : }
669 32047 : if (bestTime != SUMOTime_MAX) {
670 14355 : myTargets[step][bestNext].push_back(target);
671 : //std::cout << " myTargets step=" << step << " bestNext=" << bestNext << " target=" << target << "\n";
672 : }
673 : }
674 3469 : }
675 :
676 :
677 : void
678 27095 : MSActuatedTrafficLightLogic::findTargets(int origStep, int n, SUMOTime priorTransition, std::map<int, SUMOTime>& found) {
679 27095 : std::pair<int, SUMOTime> tDur = getTarget(n);
680 27095 : int target = tDur.first;
681 27095 : SUMOTime transitionTime = tDur.second + priorTransition;
682 : //std::cout << " findTargets origStep=" << origStep << " n=" << n << " ptt=" << priorTransition << " target=" << target << " tt=" << transitionTime << "\n";
683 27095 : if (target == origStep) {
684 : // full circle
685 : //std::cout << " foundCircle\n";
686 11310 : return;
687 : }
688 : auto it = found.find(target);
689 24683 : if (it != found.end()) {
690 9996 : if (it->second <= transitionTime) {
691 : //std::cout << " oldShorterTime=" << it->second << "\n";
692 : // found the same target again
693 : return;
694 : } else {
695 : //std::cout << " newShorterTime=" << it->second << "\n";
696 : }
697 : } else {
698 : //std::cout << " newTarget\n";
699 : }
700 15785 : found[target] = transitionTime;
701 : //std::cout << " targetNext=" << toString(myPhases[target]->nextPhases) << "\n";
702 39298 : for (int n2 : myPhases[target]->nextPhases) {
703 23513 : findTargets(origStep, n2, transitionTime, found);
704 : }
705 : }
706 :
707 :
708 : std::set<int>
709 494 : MSActuatedTrafficLightLogic::getMultiNextTargets() const {
710 : std::set<int> result;
711 494 : if (myHasMultiTarget) {
712 : // find all phase that are the target green phase of a 'next' attribute
713 419 : for (const MSPhaseDefinition* p : myPhases) {
714 843 : for (int next : p->nextPhases) {
715 956 : result.insert(getTarget(next).first);
716 : }
717 : }
718 : }
719 494 : return result;
720 : }
721 :
722 :
723 : SUMOTime
724 3749 : MSActuatedTrafficLightLogic::getMinimumMinDuration(MSLane* lane, const std::set<int>& multiNextTargets) const {
725 : SUMOTime result = std::numeric_limits<SUMOTime>::max();
726 34387 : for (int pI = 0; pI < (int)myPhases.size(); pI++) {
727 30638 : const MSPhaseDefinition* phase = myPhases[pI];
728 : const std::string& state = phase->getState();
729 712279 : for (int i = 0; i < (int)state.size(); i++) {
730 681641 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
731 370679 : for (MSLane* cand : getLanesAt(i)) {
732 184045 : if (lane == cand) {
733 : if (phase->isActuated()) {
734 10649 : result = MIN2(result, getMinDur(pI));
735 : } else if (multiNextTargets.count(pI) != 0) {
736 : result = MIN2(result, DEFAULT_STATIC_MINDUR);
737 : }
738 : }
739 : }
740 : }
741 : }
742 : }
743 3749 : return result;
744 : }
745 :
746 : bool
747 2599 : MSActuatedTrafficLightLogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
748 33798 : for (int i = 0; i < (int)state.size(); i++) {
749 33367 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
750 16483 : for (MSLane* cand : getLanesAt(i)) {
751 14696 : for (MSLane* lane : lanes) {
752 8124 : if (lane == cand) {
753 : return true;
754 : }
755 : }
756 : }
757 : }
758 : }
759 : return false;
760 : }
761 :
762 :
763 : // ------------ Switching and setting current rows
764 : void
765 494 : MSActuatedTrafficLightLogic::activateProgram() {
766 494 : MSTrafficLightLogic::activateProgram();
767 494 : for (InductLoopInfo& loopInfo : myInductLoops) {
768 0 : loopInfo.loop->setVisible(myShowDetectors);
769 : }
770 494 : }
771 :
772 :
773 : void
774 21 : MSActuatedTrafficLightLogic::deactivateProgram() {
775 21 : MSTrafficLightLogic::deactivateProgram();
776 53 : for (InductLoopInfo& loopInfo : myInductLoops) {
777 32 : loopInfo.loop->setVisible(false);
778 : }
779 21 : }
780 :
781 : void
782 24 : MSActuatedTrafficLightLogic::changeStepAndDuration(MSTLLogicControl& tlcontrol,
783 : SUMOTime simStep, int step, SUMOTime stepDuration) {
784 : // do not change timing if the phase changes
785 24 : if (step >= 0 && step != myStep) {
786 4 : myStep = step;
787 4 : myPhases[myStep]->myLastSwitch = MSNet::getInstance()->getCurrentTimeStep();
788 4 : setTrafficLightSignals(simStep);
789 4 : tlcontrol.get(getID()).executeOnSwitchActions();
790 : } else if (step < 0) {
791 : // TraCI requested new timing
792 12 : mySwitchCommand->deschedule(this);
793 12 : mySwitchCommand = new SwitchCommand(tlcontrol, this, stepDuration + simStep);
794 12 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(
795 12 : mySwitchCommand, stepDuration + simStep);
796 12 : myTraCISwitch = true;
797 : }
798 24 : }
799 :
800 :
801 : void
802 1 : MSActuatedTrafficLightLogic::loadState(MSTLLogicControl& tlcontrol, SUMOTime t, int step, SUMOTime spentDuration) {
803 1 : const SUMOTime lastSwitch = t - spentDuration;
804 1 : myStep = step;
805 1 : myPhases[myStep]->myLastSwitch = lastSwitch;
806 1 : const SUMOTime nextSwitch = t + getPhase(step).minDuration - spentDuration;
807 1 : mySwitchCommand->deschedule(this);
808 1 : mySwitchCommand = new SwitchCommand(tlcontrol, this, nextSwitch);
809 1 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(mySwitchCommand, nextSwitch);
810 1 : setTrafficLightSignals(lastSwitch);
811 1 : tlcontrol.get(getID()).executeOnSwitchActions();
812 1 : }
813 :
814 :
815 : SUMOTime
816 258617 : MSActuatedTrafficLightLogic::trySwitch() {
817 : // checks if the actual phase should be continued
818 : // @note any vehicles which arrived during the previous phases which are now waiting between the detector and the stop line are not
819 : // considere here. RiLSA recommends to set minDuration in a way that lets all vehicles pass the detector
820 258617 : SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
821 517234 : executeAssignments(myAssignments, myConditions);
822 :
823 258617 : if (myLinkGreenTimes.size() > 0) {
824 : // constraints exist, record green time durations for each link
825 18005 : const std::string& state = getCurrentPhaseDef().getState();
826 18005 : SUMOTime lastDuration = SIMSTEP - myLastTrySwitchTime;
827 163857 : for (int i = 0; i < myNumLinks; i++) {
828 145852 : if (state[i] == 'G' || state[i] == 'g') {
829 65028 : myLinkGreenTimes[i] += lastDuration;
830 : } else {
831 80824 : myLinkGreenTimes[i] = 0;
832 : }
833 145852 : if (state[i] == 'r' || state[i] == 'u') {
834 78186 : myLinkRedTimes[i] += lastDuration;
835 : } else {
836 67666 : myLinkRedTimes[i] = 0;
837 : }
838 : }
839 : }
840 258617 : myLastTrySwitchTime = now;
841 : // decide the next phase
842 258617 : const bool multiTarget = myPhases[myStep]->nextPhases.size() > 1 && myPhases[myStep]->nextPhases.front() >= 0;
843 : const int origStep = myStep;
844 : int nextStep = myStep;
845 258617 : SUMOTime actDuration = now - myPhases[myStep]->myLastSwitch;
846 :
847 258617 : if (mySwitchingRules[myStep].enabled) {
848 15137 : const bool mustSwitch = MIN2(getMaxDur() - actDuration, getLatest()) <= 0;
849 15137 : nextStep = decideNextPhaseCustom(mustSwitch);
850 : } else {
851 : // default algorithm
852 243480 : const double detectionGap = gapControl();
853 : #ifdef DEBUG_PHASE_SELECTION
854 : if (DEBUG_COND) {
855 : std::cout << SIMTIME << " p=" << myStep
856 : << " trySwitch dGap=" << (detectionGap == std::numeric_limits<double>::max() ? "inf" : toString(detectionGap))
857 : << " multi=" << multiTarget << "\n";
858 : }
859 : #endif
860 243480 : if (detectionGap < std::numeric_limits<double>::max() && !multiTarget && !myTraCISwitch) {
861 90135 : return duration(detectionGap);
862 : }
863 151177 : if (multiTarget) {
864 56574 : nextStep = decideNextPhase();
865 : } else {
866 96771 : if (myPhases[myStep]->nextPhases.size() == 1 && myPhases[myStep]->nextPhases.front() >= 0) {
867 : nextStep = myPhases[myStep]->nextPhases.front();
868 : } else {
869 0 : nextStep = (myStep + 1) % (int)myPhases.size();
870 : }
871 : }
872 : }
873 :
874 168482 : myTraCISwitch = false;
875 168482 : if (myLinkMinGreenTimes.size() > 0) {
876 165 : SUMOTime linkMinDur = getLinkMinDuration(getTarget(nextStep).first);
877 165 : if (linkMinDur > 0) {
878 : // for multiTarget, the current phase must be extended but if another
879 : // targer is chosen, earlier switching than linkMinDur is possible
880 53 : return multiTarget ? TIME2STEPS(1) : linkMinDur;
881 : }
882 : }
883 168438 : myStep = nextStep;
884 : assert(myStep <= (int)myPhases.size());
885 : assert(myStep >= 0);
886 : //stores the time the phase started
887 168438 : const SUMOTime prevStart = myPhases[myStep]->myLastSwitch;
888 168438 : if (myStep != origStep) {
889 93738 : myPhases[origStep]->myLastEnd = now;
890 93738 : myPhases[myStep]->myLastSwitch = now;
891 : actDuration = 0;
892 : }
893 : // activate coloring
894 168438 : if ((myShowDetectors || myHasMultiTarget) && getCurrentPhaseDef().isGreenPhase()) {
895 238502 : for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
896 : //std::cout << SIMTIME << " p=" << myStep << " loopinfo=" << loopInfo->loop->getID() << " set lastGreen=" << STEPS2TIME(now) << "\n";
897 : if (loopInfo->isJammed()) {
898 37 : loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
899 : } else {
900 169030 : loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
901 : }
902 169067 : loopInfo->lastGreenTime = now;
903 : }
904 : }
905 : // set the next event
906 : #ifdef DEBUG_PHASE_SELECTION
907 : if (DEBUG_COND) {
908 : std::cout << SIMTIME << " tl=" << getID() << " p=" << myStep
909 : << " nextTryMinDur=" << STEPS2TIME(getMinDur() - actDuration)
910 : << " nextTryEarliest=" << STEPS2TIME(getEarliest(prevStart)) << "\n";
911 : }
912 : #endif
913 168438 : SUMOTime minRetry = myStep != origStep ? 0 : TIME2STEPS(1);
914 168438 : return MAX3(minRetry, getMinDur() - actDuration, getEarliest(prevStart));
915 : }
916 :
917 :
918 : // ------------ "actuated" algorithm methods
919 : SUMOTime
920 90135 : MSActuatedTrafficLightLogic::duration(const double detectionGap) const {
921 : assert(getCurrentPhaseDef().isGreenPhase());
922 : assert((int)myPhases.size() > myStep);
923 90135 : const SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
924 : // ensure that minimum duration is kept
925 90135 : SUMOTime newDuration = getMinDur() - actDuration;
926 : // try to let the last detected vehicle pass the intersection (duration must be positive)
927 99673 : newDuration = MAX3(newDuration, TIME2STEPS(myDetectorGap - detectionGap), SUMOTime(1));
928 : // cut the decimal places to ensure that phases always have integer duration
929 90135 : if (newDuration % 1000 != 0) {
930 59706 : const SUMOTime totalDur = newDuration + actDuration;
931 59706 : newDuration = (totalDur / 1000 + 1) * 1000 - actDuration;
932 : }
933 : // ensure that the maximum duration is not exceeded
934 90135 : newDuration = MIN3(newDuration, getMaxDur() - actDuration, getLatest());
935 90135 : return newDuration;
936 : }
937 :
938 :
939 : double
940 244141 : MSActuatedTrafficLightLogic::gapControl() {
941 : //intergreen times should not be lengthend
942 : assert((int)myPhases.size() > myStep);
943 : double result = std::numeric_limits<double>::max();
944 244141 : if (MSGlobals::gUseMesoSim) {
945 : return result;
946 : }
947 : // switch off active colors
948 244141 : if (myShowDetectors) {
949 116473 : for (InductLoopInfo& loopInfo : myInductLoops) {
950 103097 : if (loopInfo.lastGreenTime < loopInfo.loop->getLastDetectionTime()) {
951 23360 : loopInfo.loop->setSpecialColor(&RGBColor::RED);
952 : } else {
953 79737 : loopInfo.loop->setSpecialColor(nullptr);
954 : }
955 : }
956 : }
957 244141 : if (!getCurrentPhaseDef().isGreenPhase()) {
958 : return result; // end current phase
959 : }
960 :
961 : // Checks, if the maxDuration is kept. No phase should last longer than maxDuration.
962 201102 : SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
963 201102 : if (actDuration >= getCurrentPhaseDef().maxDuration || maxLinkDurationReached() || getLatest() == 0) {
964 : #ifdef DEBUG_PHASE_SELECTION
965 : if (DEBUG_COND) {
966 : std::cout << SIMTIME << " actDuration=" << STEPS2TIME(actDuration) << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
967 : << " maxLinkDurationReached=" << maxLinkDurationReached() << " latest=" << STEPS2TIME(getLatest()) << "\n";
968 : }
969 : #endif
970 71927 : return result; // end current phase
971 : }
972 :
973 : // now the gapcontrol starts
974 489460 : for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
975 360285 : MSInductLoop* loop = loopInfo->loop;
976 : if (loopInfo->isJammed()) {
977 158 : loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
978 : } else {
979 360127 : loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
980 : }
981 360285 : const double actualGap = loop->getTimeSinceLastDetection();
982 360285 : if (actualGap < loopInfo->maxGap && !loopInfo->isJammed()) {
983 : result = MIN2(result, actualGap);
984 : }
985 : }
986 : return result;
987 : }
988 :
989 : int
990 56574 : MSActuatedTrafficLightLogic::decideNextPhase() {
991 56574 : const auto& cands = myPhases[myStep]->nextPhases;
992 : // decide by priority
993 : // first target is the default when there is no traffic
994 : // @note: to keep the current phase, even when there is no traffic, it must be added to 'next' explicitly
995 56574 : int result = cands.front();
996 : int maxPrio = 0;
997 56574 : SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
998 56574 : const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && !maxLinkDurationReached() && getLatest() > 0;
999 : if (canExtend) {
1000 : // consider keeping the current phase until maxDur is reached
1001 : // (only when there is still traffic in that phase)
1002 4185 : int currentPrio = getPhasePriority(myStep);
1003 : #ifdef DEBUG_PHASE_SELECTION
1004 : std::cout << SIMTIME << " p=" << myStep << " loops=" << myInductLoopsForPhase[myStep].size() << " currentPrio=" << currentPrio << "\n";
1005 : #endif
1006 4185 : if (currentPrio > maxPrio) {
1007 2204 : result = myStep;
1008 : maxPrio = currentPrio;
1009 : }
1010 : }
1011 171346 : for (int step : cands) {
1012 : int prio = 0;
1013 177309 : for (int target : myTargets[myStep][step]) {
1014 62537 : prio += getPhasePriority(target);
1015 : #ifdef DEBUG_PHASE_SELECTION
1016 : if (DEBUG_COND) {
1017 : std::cout << SIMTIME << " p=" << myStep << " step=" << step << " target=" << target << " loops=" << myInductLoopsForPhase[target].size() << " prio=" << prio << "\n";
1018 : }
1019 : #endif
1020 : }
1021 114772 : if (prio > maxPrio && canExtendLinkGreen(getTarget(step).first)) {
1022 : maxPrio = prio;
1023 173 : result = step;
1024 : }
1025 : }
1026 56574 : return result;
1027 : }
1028 :
1029 :
1030 : std::pair<int, SUMOTime>
1031 27911 : MSActuatedTrafficLightLogic::getTarget(int step) const {
1032 : int seen = 0;
1033 27911 : int origStep = step;
1034 : SUMOTime dur = 0;
1035 :
1036 : // if step is a transition, find the upcoming green phase
1037 51265 : while (!myPhases[step]->isGreenPhase()) {
1038 23354 : seen += 1;
1039 23354 : dur += myPhases[step]->duration;
1040 23354 : if (myPhases[step]->nextPhases.size() > 0 && myPhases[step]->nextPhases.front() >= 0) {
1041 23364 : for (int next : myPhases[step]->nextPhases) {
1042 23364 : if (next != step) {
1043 : step = next;
1044 : break;
1045 : }
1046 : }
1047 : } else {
1048 0 : step = (step + 1) % (int)myPhases.size();
1049 : }
1050 23354 : if (step == origStep || seen > (int)myPhases.size()) {
1051 0 : WRITE_WARNING("At actuated tlLogic '" + getID() + "', infinite transition loop from phase " + toString(origStep));
1052 0 : return std::make_pair(0, 0);
1053 : }
1054 : }
1055 : return std::make_pair(step, dur);
1056 : }
1057 :
1058 : int
1059 34130 : MSActuatedTrafficLightLogic::getDetectorPriority(const InductLoopInfo& loopInfo) const {
1060 34130 : MSInductLoop* loop = loopInfo.loop;
1061 34130 : const double actualGap = loop->getTimeSinceLastDetection();
1062 34130 : if ((actualGap < loopInfo.maxGap && !loopInfo.isJammed())
1063 34130 : || loopInfo.lastGreenTime < loop->getLastDetectionTime()) {
1064 5204 : SUMOTime inactiveTime = MSNet::getInstance()->getCurrentTimeStep() - loopInfo.lastGreenTime;
1065 : // @note. Inactive time could also be tracked regardless of current activity (to increase robustness in case of detection failure
1066 5204 : if (inactiveTime > myInactiveThreshold) {
1067 : #ifdef DEBUG_PHASE_SELECTION
1068 : if (DEBUG_COND) {
1069 : std::cout << " loop=" << loop->getID() << " gap=" << loop->getTimeSinceLastDetection() << " lastGreen=" << STEPS2TIME(loopInfo.lastGreenTime)
1070 : << " lastDetection=" << STEPS2TIME(loop->getLastDetectionTime()) << " inactive=" << STEPS2TIME(inactiveTime) << "\n";
1071 : }
1072 : #endif
1073 6 : return (int)STEPS2TIME(inactiveTime);
1074 : } else {
1075 : // give bonus to detectors that are currently served (if that phase can stil be extended)
1076 5198 : if (loopInfo.servedPhase[myStep]) {
1077 3023 : SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
1078 3023 : const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && getLatest() > 0;
1079 : #ifdef DEBUG_PHASE_SELECTION
1080 : if (DEBUG_COND) {
1081 : std::cout << " loop=" << loop->getID()
1082 : << " actDuration=" << STEPS2TIME(actDuration)
1083 : << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
1084 : << " getLatest=" << STEPS2TIME(getLatest())
1085 : << " canExtend=" << canExtend
1086 : << "\n";
1087 : }
1088 : #endif
1089 : if (canExtend) {
1090 : return DEFAULT_CURRENT_PRIORITY;
1091 : } else {
1092 2 : return 0;
1093 : }
1094 : }
1095 : return 1;
1096 : }
1097 : }
1098 : return 0;
1099 : }
1100 :
1101 : int
1102 66722 : MSActuatedTrafficLightLogic::getPhasePriority(int step) const {
1103 : int result = 0;
1104 100852 : for (const InductLoopInfo* loopInfo : myInductLoopsForPhase[step]) {
1105 34130 : result += getDetectorPriority(*loopInfo);
1106 : }
1107 66722 : if (myCrossingsForPhase.size() > 0) {
1108 117775 : for (const MSLink* crossingEntry : myCrossingsForPhase[step]) {
1109 : auto* aPersons = crossingEntry->getApproachingPersons();
1110 59953 : if (aPersons != nullptr && aPersons->size() > 0) {
1111 29 : result += DEFAULT_CROSSING_PRIORITY;
1112 : }
1113 : }
1114 : }
1115 66722 : return result;
1116 : }
1117 :
1118 :
1119 : void
1120 0 : MSActuatedTrafficLightLogic::setShowDetectors(bool show) {
1121 0 : myShowDetectors = show;
1122 0 : for (InductLoopInfo& loopInfo : myInductLoops) {
1123 0 : loopInfo.loop->setVisible(myShowDetectors);
1124 : }
1125 0 : }
1126 :
1127 :
1128 : bool
1129 134184 : MSActuatedTrafficLightLogic::maxLinkDurationReached() {
1130 134184 : if (myLinkMaxGreenTimes.empty()) {
1131 : return false;
1132 : }
1133 777 : for (int i = 0; i < myNumLinks; i++) {
1134 718 : if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i]) {
1135 : //std::cout << SIMTIME << " maxLinkDurationReached i=" << i << "\n";
1136 : return true;
1137 : }
1138 : }
1139 : return false;
1140 : }
1141 :
1142 : bool
1143 173 : MSActuatedTrafficLightLogic::canExtendLinkGreen(int target) {
1144 173 : if (myLinkMaxGreenTimes.empty()) {
1145 : return true;
1146 : }
1147 4 : const std::string& targetState = myPhases[target]->getState();
1148 52 : for (int i = 0; i < myNumLinks; i++) {
1149 48 : if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i] && (
1150 0 : targetState[i] == 'G' || targetState[i] == 'g')) {
1151 : //std::cout << SIMTIME << " cannotExtendLinkGreen target=" << target << " i=" << i << "\n";
1152 : return false;
1153 : }
1154 : }
1155 : return true;
1156 : }
1157 :
1158 : SUMOTime
1159 165 : MSActuatedTrafficLightLogic::getLinkMinDuration(int target) const {
1160 : SUMOTime result = 0;
1161 165 : if (target != myStep && myLinkMinGreenTimes.size() > 0) {
1162 165 : const std::string& state = myPhases[myStep]->getState();
1163 165 : const std::string& targetState = myPhases[target]->getState();
1164 2517 : for (int i = 0; i < myNumLinks; i++) {
1165 2352 : if (myLinkGreenTimes[i] < myLinkMinGreenTimes[i]
1166 604 : && (state[i] == 'G' || state[i] == 'g')
1167 2443 : && !(targetState[i] == 'G' || targetState[i] == 'g')) {
1168 67 : result = MAX2(result, myLinkMinGreenTimes[i] - myLinkGreenTimes[i]);
1169 : //std::cout << SIMTIME << " getLinkMinDuration myStep=" << myStep << " target=" << target << " i=" << i
1170 : // << " greenTime=" << STEPS2TIME(myLinkGreenTimes[i]) << " min=" << STEPS2TIME(myLinkMinGreenTimes[i]) << " result=" << STEPS2TIME(result) << "\n";
1171 : }
1172 : }
1173 : }
1174 165 : return result;
1175 : }
1176 :
1177 : int
1178 15137 : MSActuatedTrafficLightLogic::decideNextPhaseCustom(bool mustSwitch) {
1179 38357 : for (int next : getCurrentPhaseDef().nextPhases) {
1180 23788 : const MSPhaseDefinition* phase = myPhases[next];
1181 23788 : const std::string& condition = mustSwitch ? phase->finalTarget : phase->earlyTarget;
1182 : #ifdef DEBUG_PHASE_SELECTION_CUSTOM
1183 : if (DEBUG_COND) {
1184 : std::cout << SIMTIME << " mustSwitch=" << mustSwitch << " cur=" << myStep << " next=" << next << " condition=" << condition
1185 : << " eval=" << (condition == "" ? NAN : evalExpression(condition)) << "\n";
1186 : }
1187 : #endif
1188 23788 : if (condition != "") {
1189 : // backward compatibility if a user redefined DEFAULT_CONDITION
1190 17422 : if (condition == DEFAULT_CONDITION && myConditions.count(DEFAULT_CONDITION) == 0) {
1191 661 : if (gapControl() == std::numeric_limits<double>::max()) {
1192 42 : return next;
1193 : }
1194 15439 : } else if (evalExpression(condition)) {
1195 526 : return next;
1196 : }
1197 : }
1198 : }
1199 14569 : return mustSwitch ? getCurrentPhaseDef().nextPhases.back() : myStep;
1200 : }
1201 :
1202 :
1203 : double
1204 286464 : MSActuatedTrafficLightLogic::evalExpression(const std::string& condition) const {
1205 286464 : const size_t bracketOpen = condition.find('(');
1206 286464 : if (bracketOpen != std::string::npos) {
1207 : // find matching closing bracket
1208 : size_t bracketClose = std::string::npos;
1209 : int open = 1;
1210 259264 : for (size_t i = bracketOpen + 1; i < condition.size(); i++) {
1211 259264 : if (condition[i] == '(') {
1212 1866 : open++;
1213 257398 : } else if (condition[i] == ')') {
1214 21348 : open--;
1215 21348 : if (open == 0) {
1216 : bracketClose = i;
1217 : break;
1218 : }
1219 : }
1220 : }
1221 19482 : if (bracketClose == std::string::npos) {
1222 0 : throw ProcessError(TLF("Unmatched parentheses in condition %'", condition));
1223 : }
1224 : std::string cond2 = condition;
1225 19482 : const std::string inBracket = condition.substr(bracketOpen + 1, bracketClose - bracketOpen - 1);
1226 19482 : double bracketVal = evalExpression(inBracket);
1227 19482 : cond2.replace(bracketOpen, bracketClose - bracketOpen + 1, toString(bracketVal));
1228 : try {
1229 19482 : return evalExpression(cond2);
1230 0 : } catch (ProcessError& e) {
1231 0 : throw ProcessError(TLF("Error when evaluating expression '%':\n %", condition, e.what()));
1232 0 : }
1233 : }
1234 533964 : std::vector<std::string> tokens = StringTokenizer(condition).getVector();
1235 : //std::cout << SIMTIME << " tokens(" << tokens.size() << ")=" << toString(tokens) << "\n";
1236 266982 : if (tokens.size() == 0) {
1237 0 : throw ProcessError(TLF("Invalid empty condition '%'", condition));
1238 266982 : } else if (tokens.size() == 1) {
1239 : try {
1240 165278 : return evalAtomicExpression(tokens[0]);
1241 0 : } catch (ProcessError& e) {
1242 0 : throw ProcessError(TLF("Error when evaluating expression '%':\n %", condition, e.what()));
1243 0 : }
1244 101704 : } else if (tokens.size() == 2) {
1245 1244 : if (tokens[0] == "not") {
1246 : try {
1247 1244 : return evalAtomicExpression(tokens[1]) == 0. ? 1. : 0.;
1248 0 : } catch (ProcessError& e) {
1249 0 : throw ProcessError(TLF("Error when evaluating expression '%':\n %", condition, e.what()));
1250 0 : }
1251 : } else {
1252 0 : throw ProcessError(TLF("Unsupported condition '%'", condition));
1253 : }
1254 100460 : } else if (tokens.size() == 3) {
1255 : // infix expression
1256 68102 : const double a = evalAtomicExpression(tokens[0]);
1257 68102 : const double b = evalAtomicExpression(tokens[2]);
1258 : const std::string& o = tokens[1];
1259 : //std::cout << SIMTIME << " o=" << o << " a=" << a << " b=" << b << "\n";
1260 : try {
1261 68102 : return evalTernaryExpression(a, o, b, condition);
1262 0 : } catch (ProcessError& e) {
1263 0 : throw ProcessError(TLF("Error when evaluating expression '%':\n %", condition, e.what()));
1264 0 : }
1265 : } else {
1266 32358 : const int iEnd = (int)tokens.size() - 1;
1267 375810 : for (const std::string& o : OPERATOR_PRECEDENCE) {
1268 1739954 : for (int i = 1; i < iEnd; i++) {
1269 1396502 : if (tokens[i] == o) {
1270 : try {
1271 32358 : const double val = evalTernaryExpression(
1272 32358 : evalAtomicExpression(tokens[i - 1]), o,
1273 64716 : evalAtomicExpression(tokens[i + 1]), condition);
1274 32358 : std::vector<std::string> newTokens(tokens.begin(), tokens.begin() + (i - 1));
1275 64716 : newTokens.push_back(toString(val));
1276 32358 : newTokens.insert(newTokens.end(), tokens.begin() + (i + 2), tokens.end());
1277 32358 : return evalExpression(toString(newTokens));
1278 32358 : } catch (ProcessError& e) {
1279 0 : throw ProcessError(TLF("Error when evaluating expression '%':\n %", condition, e.what()));
1280 0 : }
1281 : }
1282 : }
1283 : }
1284 0 : throw ProcessError(TLF("Parsing expressions with % elements ('%') is not supported", toString(tokens.size()), condition));
1285 : }
1286 : return true;
1287 266982 : }
1288 :
1289 : double
1290 100460 : MSActuatedTrafficLightLogic::evalTernaryExpression(double a, const std::string& o, double b, const std::string& condition) const {
1291 100460 : if (o == "=" || o == "==") {
1292 19352 : return (double)(a == b);
1293 89964 : } else if (o == "<") {
1294 0 : return (double)(a < b);
1295 89964 : } else if (o == ">") {
1296 41775 : return (double)(a > b);
1297 58797 : } else if (o == "<=") {
1298 0 : return (double)(a <= b);
1299 58797 : } else if (o == ">=") {
1300 10287 : return (double)(a >= b);
1301 53594 : } else if (o == "!=") {
1302 4728 : return (double)(a != b);
1303 48966 : } else if (o == "or" || o == "||") {
1304 18499 : return (double)(a || b);
1305 30467 : } else if (o == "and" || o == "&&") {
1306 19212 : return (double)(a && b);
1307 11255 : } else if (o == "+") {
1308 6597 : return a + b;
1309 4658 : } else if (o == "-") {
1310 0 : return a - b;
1311 4658 : } else if (o == "*") {
1312 0 : return a * b;
1313 4658 : } else if (o == "/") {
1314 29 : if (b == 0) {
1315 87 : WRITE_ERRORF(TL("Division by 0 in condition '%'"), condition);
1316 29 : return 0;
1317 : }
1318 0 : return a / b;
1319 4629 : } else if (o == "%") {
1320 4628 : return fmod(a, b);
1321 1 : } else if (o == "**" || o == "^") {
1322 1 : return pow(a, b);
1323 : } else {
1324 0 : throw ProcessError(TLF("Unsupported operator '%' in condition '%'", o, condition));
1325 : }
1326 : }
1327 :
1328 : double
1329 4628 : MSActuatedTrafficLightLogic::evalCustomFunction(const std::string& fun, const std::string& arg) const {
1330 13884 : std::vector<std::string> args = StringTokenizer(arg, ",").getVector();
1331 : const Function& f = myFunctions.find(fun)->second;
1332 4628 : if ((int)args.size() != f.nArgs) {
1333 0 : throw ProcessError(TLF("Function '%' requires % arguments but % were given", fun, toString(f.nArgs), toString(args.size())));
1334 : }
1335 : std::vector<double> args2;
1336 11570 : for (auto a : args) {
1337 6942 : args2.push_back(evalExpression(a));
1338 : }
1339 4628 : myStack.push_back(myStack.back());
1340 4628 : myStack.back()["$0"] = 0;
1341 11570 : for (int i = 0; i < (int)args2.size(); i++) {
1342 20826 : myStack.back()["$" + toString(i + 1)] = args2[i];
1343 : }
1344 : try {
1345 : ConditionMap empty;
1346 4628 : executeAssignments(f.assignments, empty, myConditions);
1347 0 : } catch (ProcessError& e) {
1348 0 : throw ProcessError(TLF("Error when evaluating function '%' with args '%' (%)", fun, joinToString(args2, ","), e.what()));
1349 0 : }
1350 4628 : double result = myStack.back()["$0"];
1351 : myStack.pop_back();
1352 4628 : return result;
1353 4628 : }
1354 :
1355 :
1356 : void
1357 263245 : MSActuatedTrafficLightLogic::executeAssignments(const AssignmentMap& assignments, ConditionMap& conditions, const ConditionMap& forbidden) const {
1358 314204 : for (const auto& assignment : assignments) {
1359 50959 : if (evalExpression(std::get<1>(assignment))) {
1360 : const std::string& id = std::get<0>(assignment);
1361 27972 : const double val = evalExpression(std::get<2>(assignment));
1362 : ConditionMap::iterator it = conditions.find(id);
1363 27972 : if (it != conditions.end()) {
1364 41622 : it->second = toString(val);
1365 7161 : } else if (forbidden.find(id) != forbidden.end()) {
1366 0 : throw ProcessError(TLF("Modifying global condition '%' is forbidden", id));
1367 : } else {
1368 7161 : myStack.back()[id] = val;
1369 : }
1370 : }
1371 : }
1372 263245 : }
1373 :
1374 :
1375 : double
1376 372215 : MSActuatedTrafficLightLogic::evalAtomicExpression(const std::string& expr) const {
1377 372215 : if (expr.size() == 0) {
1378 0 : throw ProcessError(TL("Invalid empty expression"));
1379 372215 : } else if (expr[0] == '!') {
1380 9388 : return evalAtomicExpression(expr.substr(1)) == 0. ? 1. : 0.;
1381 367521 : } else if (expr[0] == '-') {
1382 79 : return -evalAtomicExpression(expr.substr(1));
1383 : } else {
1384 : // check for 'operator:'
1385 367442 : const size_t pos = expr.find(':');
1386 367442 : if (pos == std::string::npos) {
1387 : auto it = myConditions.find(expr);
1388 324500 : if (it != myConditions.end()) {
1389 : // symbol lookup
1390 76084 : return evalExpression(it->second);
1391 : } else {
1392 : // look at stack
1393 : auto it2 = myStack.back().find(expr);
1394 248416 : if (it2 != myStack.back().end()) {
1395 16198 : return it2->second;
1396 : }
1397 : // must be a number
1398 232218 : return StringUtils::toDouble(expr);
1399 : }
1400 : } else {
1401 42942 : const std::string fun = expr.substr(0, pos);
1402 42942 : const std::string arg = expr.substr(pos + 1);
1403 42942 : if (fun == "z") {
1404 30445 : return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection();
1405 12497 : } else if (fun == "a") {
1406 : try {
1407 4895 : return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection() == 0;
1408 4268 : } catch (ProcessError&) {
1409 4268 : return retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(arg, expr, true)->getCurrentVehicleNumber();
1410 4268 : }
1411 7602 : } else if (fun == "w") {
1412 : try {
1413 1636 : return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getOccupancyTime();
1414 1092 : } catch (ProcessError&) {
1415 1092 : return retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(arg, expr, true)->getCurrentJamDuration();
1416 1092 : }
1417 5966 : } else if (fun == "g" || fun == "r") {
1418 : try {
1419 1109 : int linkIndex = StringUtils::toInt(arg);
1420 1109 : if (linkIndex >= 0 && linkIndex < myNumLinks) {
1421 1109 : const std::vector<SUMOTime>& times = fun == "g" ? myLinkGreenTimes : myLinkRedTimes;
1422 1109 : if (times.empty()) {
1423 : return 0;
1424 : }
1425 1097 : if (myLastTrySwitchTime < SIMSTEP) {
1426 : // times are only updated at the start of a phase where
1427 : // switching is possible (i.e. not during minDur).
1428 : // If somebody is looking at those values in the tracker
1429 : // this would be confusing
1430 288 : const LinkState ls = getCurrentPhaseDef().getSignalState(linkIndex);
1431 144 : if ((fun == "g" && (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR))
1432 372 : || (fun == "r" && (ls == LINKSTATE_TL_RED || ls == LINKSTATE_TL_REDYELLOW))) {
1433 138 : const SUMOTime currentGreen = SIMSTEP - myLastTrySwitchTime;
1434 138 : return STEPS2TIME(times[linkIndex] + currentGreen);
1435 : } else {
1436 : return 0;
1437 : }
1438 : } else {
1439 809 : return STEPS2TIME(times[linkIndex]);
1440 : }
1441 : }
1442 0 : } catch (NumberFormatException&) { }
1443 0 : throw ProcessError(TLF("Invalid link index '%' in expression '%'", arg, expr));
1444 4857 : } else if (fun == "p") {
1445 : try {
1446 193 : int linkIndex = StringUtils::toInt(arg);
1447 193 : if (linkIndex >= 0 && linkIndex < myNumLinks) {
1448 : double approachingPersons = 0;
1449 386 : for (const MSLink* link : getLinksAt(linkIndex)) {
1450 : auto* aPersons = link->getApproachingPersons();
1451 193 : if (aPersons != nullptr) {
1452 172 : approachingPersons += (double)aPersons->size();
1453 : }
1454 : }
1455 193 : return approachingPersons;
1456 : }
1457 0 : } catch (NumberFormatException&) { }
1458 0 : throw ProcessError(TLF("Invalid link index '%' in expression '%'", arg, expr));
1459 4664 : } else if (fun == "c") {
1460 36 : return STEPS2TIME(getTimeInCycle());
1461 : } else {
1462 4628 : if (myFunctions.find(fun) == myFunctions.end()) {
1463 0 : throw ProcessError(TLF("Unsupported function '%' in expression '%'", fun, expr));
1464 : }
1465 4628 : return evalCustomFunction(fun, arg);
1466 : }
1467 : }
1468 : }
1469 : }
1470 :
1471 :
1472 : std::map<std::string, double>
1473 2654 : MSActuatedTrafficLightLogic::getDetectorStates() const {
1474 : std::map<std::string, double> result;
1475 31606 : for (auto li : myInductLoops) {
1476 55880 : result[li.loop->getID()] = li.loop->getOccupancy() > 0 ? 1 : 0;
1477 : }
1478 3490 : for (auto loop : myExtraLoops) {
1479 1626 : result[loop->getID()] = loop->getOccupancy() > 0 ? 1 : 0;
1480 : }
1481 3490 : for (auto loop : myExtraE2) {
1482 836 : result[loop->getID()] = loop->getCurrentVehicleNumber();
1483 : }
1484 2654 : return result;
1485 : }
1486 :
1487 : double
1488 0 : MSActuatedTrafficLightLogic::getDetectorState(const std::string laneID) const {
1489 : double result = 0.0;
1490 0 : for (auto li : myInductLoops) {
1491 0 : if (li.lane->getID() == laneID) {
1492 0 : result = li.loop->getOccupancy() > 0 ? 1 : 0;
1493 : break;
1494 : }
1495 : }
1496 0 : return result;
1497 : }
1498 :
1499 : std::map<std::string, double>
1500 7658 : MSActuatedTrafficLightLogic::getConditions() const {
1501 : std::map<std::string, double> result;
1502 42220 : for (auto item : myConditions) {
1503 : if (myListedConditions.count(item.first) != 0) {
1504 : try {
1505 29834 : result[item.first] = evalExpression(item.second);
1506 0 : } catch (ProcessError& e) {
1507 0 : WRITE_ERRORF(TL("Error when retrieving conditions '%' for tlLogic '%' (%)"), item.first, getID(), e.what());
1508 0 : }
1509 : }
1510 : }
1511 7658 : return result;
1512 : }
1513 :
1514 : const std::string
1515 17862 : MSActuatedTrafficLightLogic::getParameter(const std::string& key, const std::string defaultValue) const {
1516 35724 : if (StringUtils::startsWith(key, "condition.")) {
1517 2880 : const std::string cond = key.substr(10);
1518 : auto it = myConditions.find(cond);
1519 2880 : if (it != myConditions.end()) {
1520 2880 : return toString(evalExpression(it->second));
1521 : } else {
1522 0 : throw InvalidArgument(TLF("Unknown condition '%' for actuated traffic light '%'", cond, getID()));
1523 : }
1524 : } else {
1525 29964 : return MSSimpleTrafficLightLogic::getParameter(key, defaultValue);
1526 : }
1527 : }
1528 :
1529 : void
1530 20 : MSActuatedTrafficLightLogic::setParameter(const std::string& key, const std::string& value) {
1531 : // some pre-defined parameters can be updated at runtime
1532 20 : if (key == "detector-gap" || key == "passing-time" || key == "file" || key == "freq" || key == "vTypes"
1533 20 : || key == "build-all-detectors"
1534 40 : || StringUtils::startsWith(key, "linkMaxDur")
1535 60 : || StringUtils::startsWith(key, "linkMinDur")) {
1536 0 : throw InvalidArgument(key + " cannot be changed dynamically for actuated traffic light '" + getID() + "'");
1537 20 : } else if (key == "max-gap") {
1538 4 : myMaxGap = StringUtils::toDouble(value);
1539 : // overwrite custom values
1540 36 : for (InductLoopInfo& loopInfo : myInductLoops) {
1541 32 : loopInfo.maxGap = myMaxGap;
1542 : }
1543 4 : Parameterised::setParameter(key, value);
1544 32 : } else if (StringUtils::startsWith(key, "max-gap:")) {
1545 0 : const std::string laneID = key.substr(8);
1546 0 : for (InductLoopInfo& loopInfo : myInductLoops) {
1547 0 : if (loopInfo.lane->getID() == laneID) {
1548 0 : loopInfo.maxGap = StringUtils::toDouble(value);
1549 0 : Parameterised::setParameter(key, value);
1550 : return;
1551 : }
1552 : }
1553 0 : throw InvalidArgument(TLF("Invalid lane '%' in key '%' for actuated traffic light '%'", laneID, key, getID()));
1554 16 : } else if (key == "jam-threshold") {
1555 2 : myJamThreshold = StringUtils::toDouble(value);
1556 : // overwrite custom values
1557 10 : for (InductLoopInfo& loopInfo : myInductLoops) {
1558 8 : loopInfo.jamThreshold = myJamThreshold;
1559 : }
1560 2 : Parameterised::setParameter(key, value);
1561 28 : } else if (StringUtils::startsWith(key, "jam-threshold:")) {
1562 1 : const std::string laneID = key.substr(14);
1563 3 : for (InductLoopInfo& loopInfo : myInductLoops) {
1564 3 : if (loopInfo.lane->getID() == laneID) {
1565 1 : loopInfo.jamThreshold = StringUtils::toDouble(value);
1566 1 : Parameterised::setParameter(key, value);
1567 : return;
1568 : }
1569 : }
1570 0 : throw InvalidArgument(TLF("Invalid lane '%' in key '%' for actuated traffic light '%'", laneID, key, getID()));
1571 13 : } else if (key == "show-detectors") {
1572 1 : myShowDetectors = StringUtils::toBool(value);
1573 1 : Parameterised::setParameter(key, value);
1574 5 : for (InductLoopInfo& loopInfo : myInductLoops) {
1575 4 : loopInfo.loop->setVisible(myShowDetectors);
1576 : }
1577 12 : } else if (key == "inactive-threshold") {
1578 0 : myInactiveThreshold = string2time(value);
1579 0 : Parameterised::setParameter(key, value);
1580 : } else {
1581 12 : MSSimpleTrafficLightLogic::setParameter(key, value);
1582 : }
1583 : }
1584 :
1585 :
1586 : /****************************************************************************/
|