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 511 : 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 511 : const FunctionMap& functions) :
86 : MSSimpleTrafficLightLogic(tlcontrol, id, programID, offset, TrafficLightType::ACTUATED, phases, step, delay, parameter),
87 511 : myHasMultiTarget(false),
88 511 : myLastTrySwitchTime(0),
89 : myConditions(conditions),
90 511 : myAssignments(assignments),
91 : myFunctions(functions),
92 511 : myTraCISwitch(false),
93 1533 : myDetectorPrefix(id + "_" + programID + "_") {
94 1022 : myMaxGap = StringUtils::toDouble(getParameter("max-gap", DEFAULT_MAX_GAP));
95 1022 : myJamThreshold = StringUtils::toDouble(getParameter("jam-threshold", OptionsCont::getOptions().getValueString("tls.actuated.jam-threshold")));
96 1022 : myPassingTime = StringUtils::toDouble(getParameter("passing-time", DEFAULT_PASSING_TIME));
97 1022 : myDetectorGap = StringUtils::toDouble(getParameter("detector-gap", DEFAULT_DETECTOR_GAP));
98 1022 : myInactiveThreshold = string2time(getParameter("inactive-threshold", DEFAULT_INACTIVE_THRESHOLD));
99 1022 : myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
100 1022 : myBuildAllDetectors = StringUtils::toBool(getParameter("build-all-detectors", "false"));
101 1022 : myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
102 1533 : myFreq = TIME2STEPS(StringUtils::toDouble(getParameter("freq", "300")));
103 1022 : myVehicleTypes = getParameter("vTypes", "");
104 :
105 1022 : 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 1014 : const bool showAll = getParameter("show-conditions", "") == "";
115 1014 : std::vector<std::string> shown = StringTokenizer(getParameter("show-conditions", "")).getVector();
116 507 : std::set<std::string> shownSet(shown.begin(), shown.end());
117 578 : for (auto item : myConditions) {
118 71 : if (showAll || shownSet.count(item.first) != 0) {
119 : myListedConditions.insert(item.first);
120 : }
121 : }
122 507 : }
123 1022 : 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 511 : myStack.push_back(std::map<std::string, double>());
134 511 : }
135 :
136 :
137 2550 : MSActuatedTrafficLightLogic::~MSActuatedTrafficLightLogic() { }
138 :
139 : void
140 503 : MSActuatedTrafficLightLogic::init(NLDetectorBuilder& nb) {
141 503 : MSTrafficLightLogic::init(nb);
142 503 : initAttributeOverride();
143 503 : initSwitchingRules();
144 4019 : for (int i = 0; i < (int)myPhases.size(); i++) {
145 3516 : initTargets(i);
146 : }
147 503 : 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 503 : 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 503 : 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 503 : int detEdgeIndex = -1;
179 503 : int detLaneIndex = 0;
180 1006 : const double detDefaultLength = StringUtils::toDouble(getParameter("detector-length",
181 1006 : OptionsCont::getOptions().getValueString("tls.actuated.detector-length")));
182 : MSEdge* prevDetEdge = nullptr;
183 8282 : for (LaneVector& lanes : myLanes) {
184 15553 : for (MSLane* lane : lanes) {
185 7774 : const std::string customID = getParameter(lane->getID());
186 7774 : if (noVehicles(lane->getPermissions()) && customID == "") {
187 : // do not build detectors on green verges or sidewalks
188 804 : continue;
189 : }
190 6970 : if (laneInductLoopMap.find(lane) != laneInductLoopMap.end()) {
191 : // only build one detector per lane
192 3210 : continue;
193 : }
194 3760 : const SUMOTime minDur = getMinimumMinDuration(lane, multiNextTargets);
195 3760 : if (minDur == std::numeric_limits<SUMOTime>::max() && customID == "" && !myBuildAllDetectors) {
196 : // only build detector if this lane is relevant for an actuated phase
197 134 : continue;
198 : }
199 3626 : double length = lane->getLength();
200 : double ilpos;
201 : double inductLoopPosition;
202 3626 : MSInductLoop* loop = nullptr;
203 3626 : if (&lane->getEdge() != prevDetEdge) {
204 1961 : detEdgeIndex++;
205 1961 : detLaneIndex = 0;
206 : prevDetEdge = &lane->getEdge();
207 : } else {
208 1665 : detLaneIndex++;
209 : }
210 3626 : const bool isBikeLane = (lane->getPermissions() & ~SVC_PEDESTRIAN) == SVC_BICYCLE;
211 3626 : const double defaultLength = isBikeLane ? DEFAULT_BIKE_LENGTH_WITH_GAP : DEFAULT_LENGTH_WITH_GAP;
212 3626 : if (customID == "") {
213 3284 : const double speed = isBikeLane ? DEFAULT_BICYCLE_SPEED : lane->getSpeedLimit();
214 6568 : inductLoopPosition = MIN2(
215 3284 : myDetectorGap * speed,
216 3284 : (STEPS2TIME(minDur) / myPassingTime + 0.5) * defaultLength);
217 :
218 : // check whether the lane is long enough
219 3284 : ilpos = length - inductLoopPosition;
220 3284 : MSLane* placementLane = lane;
221 4463 : while (ilpos < 0 && placementLane->getIncomingLanes().size() == 1
222 4523 : && placementLane->getIncomingLanes().front().viaLink->getCorrespondingEntryLink()->getTLLogic() == nullptr) {
223 551 : placementLane = placementLane->getLogicalPredecessorLane();
224 551 : ilpos += placementLane->getLength();
225 : }
226 3284 : if (ilpos < 0) {
227 : ilpos = 0;
228 : }
229 : // Build the induct loop and set it into the container
230 3284 : const double detLength = getDouble("detector-length:" + lane->getID(), detDefaultLength);
231 9852 : std::string id = myDetectorPrefix + "D" + toString(detEdgeIndex) + "." + toString(detLaneIndex);
232 3284 : loop = static_cast<MSInductLoop*>(nb.createInductLoop(id, placementLane, ilpos, detLength, "", myVehicleTypes, "", (int)PersonMode::NONE, myShowDetectors));
233 3284 : 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 3386 : const double maxGap = getDouble("max-gap:" + lane->getID(), myMaxGap);
245 3386 : const double jamThreshold = getDouble("jam-threshold:" + lane->getID(), myJamThreshold);
246 3386 : laneInductLoopMap[lane] = loop;
247 3386 : inductLoopInfoMap[loop] = (int)myInductLoops.size();
248 3386 : myInductLoops.push_back(InductLoopInfo(loop, lane, (int)myPhases.size(), maxGap, jamThreshold));
249 :
250 3386 : 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 503 : std::vector<bool> neverMajor(numLinks, true);
281 4019 : for (const MSPhaseDefinition* phase : myPhases) {
282 : const std::string& state = phase->getState();
283 67598 : for (int i = 0; i < numLinks; i++) {
284 64082 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
285 : neverMajor[i] = false;
286 : }
287 : }
288 : }
289 503 : std::vector<bool> oneLane(numLinks, false);
290 503 : std::vector<bool> turnaround(numLinks, true);
291 8282 : for (int i = 0; i < numLinks; i++) {
292 13172 : for (MSLane* lane : getLanesAt(i)) {
293 : // only count motorized vehicle lanes
294 : int numMotorized = 0;
295 27190 : for (MSLane* l : lane->getEdge().getLanes()) {
296 19434 : if ((l->getPermissions() & motorized) != 0) {
297 12873 : numMotorized++;
298 : }
299 : }
300 7756 : if (numMotorized == 1) {
301 : oneLane[i] = true;
302 2363 : break;
303 : }
304 : }
305 8512 : for (MSLink* link : getLinksAt(i)) {
306 7601 : if (!link->isTurnaround()) {
307 : turnaround[i] = false;
308 6868 : break;
309 : }
310 : }
311 : }
312 :
313 4019 : for (const MSPhaseDefinition* phase : myPhases) {
314 3516 : 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 27144 : for (int i = 0; i < numLinks; i++) {
326 25694 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
327 : greenLinks.insert(i);
328 : if (phase->isActuated()) {
329 : actuatedLinks.insert(i);
330 : }
331 :
332 15427 : for (MSLink* link : getLinksAt(i)) {
333 7730 : 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 17997 : } else if (state[i] == LINKSTATE_TL_GREEN_MINOR) {
342 770 : if (((neverMajor[i] || turnaround[i]) // check1a, 1d
343 2622 : && hasMajor(state, getLanesAt(i))) // check1b
344 1009 : || oneLane[i] // check1c
345 4172 : || weakConflict(i, state)) { // check1e, check1f
346 : greenLinks.insert(i);
347 2388 : 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 77139 : for (MSLane* lane : getLanesAt(i)) {
369 : if (laneInductLoopMap.count(lane) != 0) {
370 21778 : loopLinks[laneInductLoopMap[lane]].insert(i);
371 : }
372 : }
373 : }
374 13041 : for (auto& item : loopLinks) {
375 11591 : MSInductLoop* loop = item.first;
376 11591 : const InductLoopInfo& info = myInductLoops[inductLoopInfoMap[loop]];
377 11591 : const MSLane* loopLane = info.lane;
378 : bool usable = true;
379 : bool foundUsable = false;
380 : // check1
381 32823 : for (int j : item.second) {
382 13161 : 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 11591 : 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 11591 : if (usable && info.jamThreshold <= 0) {
399 10785 : for (MSLink* link : loopLane->getLinkCont()) {
400 6857 : if (link->isTurnaround()) {
401 975 : continue;
402 : }
403 5882 : 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 4398 : if (usable) {
421 4385 : 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 12081 : for (int j : item.second) {
428 7696 : linkToLoops[j].insert(item.first);
429 : }
430 : }
431 : }
432 1450 : 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 3516 : myInductLoopsForPhase.push_back(loopInfos);
449 7901 : for (MSInductLoop* loop : loops) {
450 47663 : for (InductLoopInfo& loopInfo : myInductLoops) {
451 43278 : if (loopInfo.loop == loop) {
452 4385 : myInductLoopsForPhase.back().push_back(&loopInfo);
453 4385 : loopInfo.servedPhase[phaseIndex] = true;
454 : }
455 : }
456 : }
457 3516 : }
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 7382 : for (int i : actuatedLinks) {
468 8026 : if (linkToLoops[i].size() == 0 && myLinks[i].size() > 0
469 7950 : && (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 503 : 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 1468 : 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 503 : bool haveSwitchingRules = myConditions.size() > 0 || myAssignments.size() > 0 || myFunctions.size() > 0;
503 3859 : for (auto sr : mySwitchingRules) {
504 3386 : if (sr.enabled) {
505 : haveSwitchingRules = true;
506 : break;
507 : }
508 : }
509 503 : 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 1006 : }
515 :
516 :
517 : bool
518 977 : MSActuatedTrafficLightLogic::weakConflict(int tlIndex, const std::string& state) const {
519 1099 : for (MSLink* link : getLinksAt(tlIndex)) {
520 : int linkIndex = link->getIndex();
521 : const MSJunction* junction = link->getJunction();
522 12300 : for (int i = 0; i < (int)myLinks.size(); i++) {
523 12178 : if (i == tlIndex) {
524 527 : continue;
525 : }
526 11651 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
527 10408 : 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 5324 : if (junction == junction2) {
532 4174 : const MSJunctionLogic* logic = junction->getLogic();
533 : //std::cout << " greenLink=" << i << " isFoe=" << logic->getFoesFor(linkIndex).test(foeIndex) << "\n";
534 4174 : if (logic->getFoesFor(linkIndex).test(foeIndex)
535 1145 : && (foe->getPermissions() & ~SVC_VULNERABLE) != 0 // check1e
536 5022 : && &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 327478 : MSActuatedTrafficLightLogic::getMinDur(int step) const {
553 327478 : step = step < 0 ? myStep : step;
554 327478 : const MSPhaseDefinition* p = myPhases[step];
555 327478 : return p->minDuration != MSPhaseDefinition::OVERRIDE_DURATION
556 327478 : ? p->minDuration
557 331306 : : TIME2STEPS(evalExpression(myConditions.find("minDur:" + toString(step))->second));
558 : }
559 :
560 : SUMOTime
561 106963 : MSActuatedTrafficLightLogic::getMaxDur(int step) const {
562 106963 : step = step < 0 ? myStep : step;
563 106963 : const MSPhaseDefinition* p = myPhases[step];
564 106963 : return p->maxDuration != MSPhaseDefinition::OVERRIDE_DURATION
565 106963 : ? p->maxDuration
566 110719 : : TIME2STEPS(evalExpression(myConditions.find("maxDur:" + toString(step))->second));
567 : }
568 :
569 : SUMOTime
570 264064 : MSActuatedTrafficLightLogic::getEarliestEnd(int step) const {
571 264064 : step = step < 0 ? myStep : step;
572 264064 : const MSPhaseDefinition* p = myPhases[step];
573 264064 : return p->earliestEnd != MSPhaseDefinition::OVERRIDE_DURATION
574 264064 : ? p->earliestEnd
575 267820 : : TIME2STEPS(evalExpression(myConditions.find("earliestEnd:" + toString(step))->second));
576 : }
577 :
578 : SUMOTime
579 269204 : MSActuatedTrafficLightLogic::getLatestEnd(int step) const {
580 269204 : step = step < 0 ? myStep : step;
581 269204 : const MSPhaseDefinition* p = myPhases[step];
582 269204 : return p->latestEnd != MSPhaseDefinition::OVERRIDE_DURATION
583 269204 : ? p->latestEnd
584 272960 : : TIME2STEPS(evalExpression(myConditions.find("latestEnd:" + toString(step))->second));
585 : }
586 :
587 :
588 : void
589 503 : MSActuatedTrafficLightLogic::initAttributeOverride() {
590 : const SUMOTime ovrd = MSPhaseDefinition::OVERRIDE_DURATION;
591 4019 : for (int i = 0; i < (int)myPhases.size(); i++) {
592 3516 : MSPhaseDefinition* phase = myPhases[i];
593 3516 : const std::string errorSuffix = "' for overriding attribute in phase " + toString(i) + " of tlLogic '" + getID() + "' in program '" + getProgramID() + "'.";
594 3516 : 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 3516 : 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 3516 : 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 3516 : 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 503 : }
620 :
621 :
622 : void
623 503 : MSActuatedTrafficLightLogic::initSwitchingRules() {
624 4019 : for (int i = 0; i < (int)myPhases.size(); i++) {
625 3516 : SwitchingRules sr;
626 3516 : MSPhaseDefinition* phase = myPhases[i];
627 3516 : std::vector<int> nextPhases = phase->nextPhases;
628 3516 : if (nextPhases.size() == 0) {
629 3331 : nextPhases.push_back((i + 1) % (int)myPhases.size());
630 185 : } else if (nextPhases.size() > 1) {
631 76 : myHasMultiTarget = true;
632 : }
633 7145 : for (int next : nextPhases) {
634 3629 : if (next >= 0 && next < (int)myPhases.size()) {
635 3629 : const MSPhaseDefinition* nextPhase = myPhases[next];
636 3629 : if (nextPhase->earlyTarget != "" || nextPhase->finalTarget != "") {
637 73 : sr.enabled = true;
638 : }
639 : }
640 : }
641 : // simplifies later code
642 3516 : phase->nextPhases = nextPhases;
643 3516 : mySwitchingRules.push_back(sr);
644 3516 : }
645 503 : }
646 :
647 :
648 : void
649 3516 : MSActuatedTrafficLightLogic::initTargets(int step) {
650 : // next -> target -> transitionTime starting from step
651 : std::map<int, std::map<int, SUMOTime> > reached;
652 3516 : const std::vector<int>& next = myPhases[step]->nextPhases;
653 7145 : for (int n : next) {
654 3629 : findTargets(step, n, 0, reached[n]);
655 : }
656 35810 : for (int target = 0; target < (int)myPhases.size(); target++) {
657 32294 : int bestNext = next[0];
658 : SUMOTime bestTime = SUMOTime_MAX;
659 66917 : for (auto item : reached) {
660 : auto it = item.second.find(target);
661 34623 : if (it != item.second.end()) {
662 14782 : SUMOTime transitionTime = it->second;
663 14782 : if (transitionTime < bestTime) {
664 : bestTime = transitionTime;
665 14533 : bestNext = item.first;
666 : }
667 : }
668 : }
669 32294 : if (bestTime != SUMOTime_MAX) {
670 14450 : myTargets[step][bestNext].push_back(target);
671 : //std::cout << " myTargets step=" << step << " bestNext=" << bestNext << " target=" << target << "\n";
672 : }
673 : }
674 3516 : }
675 :
676 :
677 : void
678 27237 : MSActuatedTrafficLightLogic::findTargets(int origStep, int n, SUMOTime priorTransition, std::map<int, SUMOTime>& found) {
679 27237 : std::pair<int, SUMOTime> tDur = getTarget(n);
680 27237 : int target = tDur.first;
681 27237 : SUMOTime transitionTime = tDur.second + priorTransition;
682 : //std::cout << " findTargets origStep=" << origStep << " n=" << n << " ptt=" << priorTransition << " target=" << target << " tt=" << transitionTime << "\n";
683 27237 : if (target == origStep) {
684 : // full circle
685 : //std::cout << " foundCircle\n";
686 11357 : return;
687 : }
688 : auto it = found.find(target);
689 24805 : if (it != found.end()) {
690 10023 : 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 15880 : found[target] = transitionTime;
701 : //std::cout << " targetNext=" << toString(myPhases[target]->nextPhases) << "\n";
702 39488 : for (int n2 : myPhases[target]->nextPhases) {
703 23608 : findTargets(origStep, n2, transitionTime, found);
704 : }
705 : }
706 :
707 :
708 : std::set<int>
709 503 : MSActuatedTrafficLightLogic::getMultiNextTargets() const {
710 : std::set<int> result;
711 503 : 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 503 : return result;
720 : }
721 :
722 :
723 : SUMOTime
724 3760 : MSActuatedTrafficLightLogic::getMinimumMinDuration(MSLane* lane, const std::set<int>& multiNextTargets) const {
725 : SUMOTime result = std::numeric_limits<SUMOTime>::max();
726 34614 : for (int pI = 0; pI < (int)myPhases.size(); pI++) {
727 30854 : const MSPhaseDefinition* phase = myPhases[pI];
728 : const std::string& state = phase->getState();
729 714112 : for (int i = 0; i < (int)state.size(); i++) {
730 683258 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
731 370285 : for (MSLane* cand : getLanesAt(i)) {
732 183843 : if (lane == cand) {
733 : if (phase->isActuated()) {
734 10773 : 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 3760 : return result;
744 : }
745 :
746 : bool
747 2622 : MSActuatedTrafficLightLogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
748 33988 : for (int i = 0; i < (int)state.size(); i++) {
749 33552 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
750 16615 : for (MSLane* cand : getLanesAt(i)) {
751 14828 : for (MSLane* lane : lanes) {
752 8199 : 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 511 : MSActuatedTrafficLightLogic::activateProgram() {
766 511 : MSTrafficLightLogic::activateProgram();
767 511 : for (InductLoopInfo& loopInfo : myInductLoops) {
768 0 : loopInfo.loop->setVisible(myShowDetectors);
769 : }
770 511 : }
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 315352 : 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 315352 : SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
821 630704 : executeAssignments(myAssignments, myConditions);
822 :
823 315352 : 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 315352 : myLastTrySwitchTime = now;
841 : // decide the next phase
842 315352 : const bool multiTarget = myPhases[myStep]->nextPhases.size() > 1 && myPhases[myStep]->nextPhases.front() >= 0;
843 : const int origStep = myStep;
844 : int nextStep = myStep;
845 315352 : SUMOTime actDuration = now - myPhases[myStep]->myLastSwitch;
846 :
847 315352 : 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 300215 : 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 300215 : if (detectionGap < std::numeric_limits<double>::max() && !multiTarget && !myTraCISwitch) {
861 89857 : return duration(detectionGap);
862 : }
863 208132 : if (multiTarget) {
864 61236 : nextStep = decideNextPhase();
865 : } else {
866 149122 : 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 225495 : myTraCISwitch = false;
875 225495 : 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 225451 : myStep = nextStep;
884 : assert(myStep <= (int)myPhases.size());
885 : assert(myStep >= 0);
886 : //stores the time the phase started
887 225451 : const SUMOTime prevStart = myPhases[myStep]->myLastSwitch;
888 225451 : if (myStep != origStep) {
889 142213 : myPhases[origStep]->myLastEnd = now;
890 142213 : myPhases[myStep]->myLastSwitch = now;
891 : actDuration = 0;
892 : }
893 : // activate coloring
894 225451 : if ((myShowDetectors || myHasMultiTarget) && getCurrentPhaseDef().isGreenPhase()) {
895 255614 : 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 181456 : loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
901 : }
902 181493 : 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 225451 : SUMOTime minRetry = myStep != origStep ? 0 : TIME2STEPS(1);
914 225451 : return MAX3(minRetry, getMinDur() - actDuration, getEarliest(prevStart));
915 : }
916 :
917 :
918 : // ------------ "actuated" algorithm methods
919 : SUMOTime
920 89857 : MSActuatedTrafficLightLogic::duration(const double detectionGap) const {
921 : assert(getCurrentPhaseDef().isGreenPhase());
922 : assert((int)myPhases.size() > myStep);
923 89857 : const SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
924 : // ensure that minimum duration is kept
925 89857 : SUMOTime newDuration = getMinDur() - actDuration;
926 : // try to let the last detected vehicle pass the intersection (duration must be positive)
927 99532 : newDuration = MAX3(newDuration, TIME2STEPS(myDetectorGap - detectionGap), SUMOTime(1));
928 : // cut the decimal places to ensure that phases always have integer duration
929 89857 : if (newDuration % 1000 != 0) {
930 59979 : const SUMOTime totalDur = newDuration + actDuration;
931 59979 : newDuration = (totalDur / 1000 + 1) * 1000 - actDuration;
932 : }
933 : // ensure that the maximum duration is not exceeded
934 89857 : newDuration = MIN3(newDuration, getMaxDur() - actDuration, getLatest());
935 89857 : return newDuration;
936 : }
937 :
938 :
939 : double
940 300876 : MSActuatedTrafficLightLogic::gapControl() {
941 : //intergreen times should not be lengthend
942 : assert((int)myPhases.size() > myStep);
943 : double result = std::numeric_limits<double>::max();
944 300876 : if (MSGlobals::gUseMesoSim) {
945 : return result;
946 : }
947 : // switch off active colors
948 300876 : if (myShowDetectors) {
949 116499 : for (InductLoopInfo& loopInfo : myInductLoops) {
950 103121 : if (loopInfo.lastGreenTime < loopInfo.loop->getLastDetectionTime()) {
951 23330 : loopInfo.loop->setSpecialColor(&RGBColor::RED);
952 : } else {
953 79791 : loopInfo.loop->setSpecialColor(nullptr);
954 : }
955 : }
956 : }
957 300876 : 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 233991 : SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
963 233991 : 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 80611 : return result; // end current phase
971 : }
972 :
973 : // now the gapcontrol starts
974 552320 : for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
975 398940 : MSInductLoop* loop = loopInfo->loop;
976 : if (loopInfo->isJammed()) {
977 155 : loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
978 : } else {
979 398785 : loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
980 : }
981 398940 : const double actualGap = loop->getTimeSinceLastDetection();
982 398940 : if (actualGap < loopInfo->maxGap && !loopInfo->isJammed()) {
983 : result = MIN2(result, actualGap);
984 : }
985 : }
986 : return result;
987 : }
988 :
989 : int
990 61236 : MSActuatedTrafficLightLogic::decideNextPhase() {
991 61236 : 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 61236 : int result = cands.front();
996 : int maxPrio = 0;
997 61236 : SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
998 61236 : 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 5113 : 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 5113 : if (currentPrio > maxPrio) {
1007 2264 : result = myStep;
1008 : maxPrio = currentPrio;
1009 : }
1010 : }
1011 185332 : for (int step : cands) {
1012 : int prio = 0;
1013 191295 : for (int target : myTargets[myStep][step]) {
1014 67199 : 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 124096 : if (prio > maxPrio && canExtendLinkGreen(getTarget(step).first)) {
1022 : maxPrio = prio;
1023 197 : result = step;
1024 : }
1025 : }
1026 61236 : return result;
1027 : }
1028 :
1029 :
1030 : std::pair<int, SUMOTime>
1031 28077 : MSActuatedTrafficLightLogic::getTarget(int step) const {
1032 : int seen = 0;
1033 28077 : int origStep = step;
1034 : SUMOTime dur = 0;
1035 :
1036 : // if step is a transition, find the upcoming green phase
1037 51590 : while (!myPhases[step]->isGreenPhase()) {
1038 23513 : seen += 1;
1039 23513 : dur += myPhases[step]->duration;
1040 23513 : if (myPhases[step]->nextPhases.size() > 0 && myPhases[step]->nextPhases.front() >= 0) {
1041 23523 : for (int next : myPhases[step]->nextPhases) {
1042 23523 : if (next != step) {
1043 : step = next;
1044 : break;
1045 : }
1046 : }
1047 : } else {
1048 0 : step = (step + 1) % (int)myPhases.size();
1049 : }
1050 23513 : 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 36870 : MSActuatedTrafficLightLogic::getDetectorPriority(const InductLoopInfo& loopInfo) const {
1060 36870 : MSInductLoop* loop = loopInfo.loop;
1061 36870 : const double actualGap = loop->getTimeSinceLastDetection();
1062 36870 : if ((actualGap < loopInfo.maxGap && !loopInfo.isJammed())
1063 36870 : || loopInfo.lastGreenTime < loop->getLastDetectionTime()) {
1064 5275 : 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 5275 : 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 5269 : if (loopInfo.servedPhase[myStep]) {
1077 3094 : SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
1078 3094 : 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 72312 : MSActuatedTrafficLightLogic::getPhasePriority(int step) const {
1103 : int result = 0;
1104 109182 : for (const InductLoopInfo* loopInfo : myInductLoopsForPhase[step]) {
1105 36870 : result += getDetectorPriority(*loopInfo);
1106 : }
1107 72312 : if (myCrossingsForPhase.size() > 0) {
1108 128349 : for (const MSLink* crossingEntry : myCrossingsForPhase[step]) {
1109 : auto* aPersons = crossingEntry->getApproachingPersons();
1110 64937 : if (aPersons != nullptr && aPersons->size() > 0) {
1111 53 : result += DEFAULT_CROSSING_PRIORITY;
1112 : }
1113 : }
1114 : }
1115 72312 : 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 159317 : MSActuatedTrafficLightLogic::maxLinkDurationReached() {
1130 159317 : 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 197 : MSActuatedTrafficLightLogic::canExtendLinkGreen(int target) {
1144 197 : 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 319980 : MSActuatedTrafficLightLogic::executeAssignments(const AssignmentMap& assignments, ConditionMap& conditions, const ConditionMap& forbidden) const {
1358 370939 : 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 319980 : }
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 18146 : MSActuatedTrafficLightLogic::getParameter(const std::string& key, const std::string defaultValue) const {
1516 36292 : 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 30532 : 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 : /****************************************************************************/
|