Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 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 646 : 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 646 : const FunctionMap& functions) :
86 : MSSimpleTrafficLightLogic(tlcontrol, id, programID, offset, TrafficLightType::ACTUATED, phases, step, delay, parameter),
87 646 : myHasMultiTarget(false),
88 646 : myLastTrySwitchTime(0),
89 : myConditions(conditions),
90 646 : myAssignments(assignments),
91 : myFunctions(functions),
92 646 : myTraCISwitch(false),
93 1938 : myDetectorPrefix(id + "_" + programID + "_") {
94 1292 : myMaxGap = StringUtils::toDouble(getParameter("max-gap", DEFAULT_MAX_GAP));
95 1292 : myJamThreshold = StringUtils::toDouble(getParameter("jam-threshold", OptionsCont::getOptions().getValueString("tls.actuated.jam-threshold")));
96 1292 : myPassingTime = StringUtils::toDouble(getParameter("passing-time", DEFAULT_PASSING_TIME));
97 1292 : myDetectorGap = StringUtils::toDouble(getParameter("detector-gap", DEFAULT_DETECTOR_GAP));
98 1292 : myInactiveThreshold = string2time(getParameter("inactive-threshold", DEFAULT_INACTIVE_THRESHOLD));
99 1292 : myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
100 1292 : myBuildAllDetectors = StringUtils::toBool(getParameter("build-all-detectors", "false"));
101 1292 : myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
102 1938 : myFreq = TIME2STEPS(StringUtils::toDouble(getParameter("freq", "300")));
103 1292 : myVehicleTypes = getParameter("vTypes", "");
104 :
105 1292 : if (hasParameter("hide-conditions")) {
106 16 : std::vector<std::string> hidden = StringTokenizer(getParameter("hide-conditions", "")).getVector();
107 8 : std::set<std::string> hiddenSet(hidden.begin(), hidden.end());
108 56 : for (auto item : myConditions) {
109 : if (hiddenSet.count(item.first) == 0) {
110 : myListedConditions.insert(item.first);
111 : }
112 : }
113 8 : } else {
114 1276 : const bool showAll = getParameter("show-conditions", "") == "";
115 1276 : std::vector<std::string> shown = StringTokenizer(getParameter("show-conditions", "")).getVector();
116 638 : std::set<std::string> shownSet(shown.begin(), shown.end());
117 764 : for (auto item : myConditions) {
118 126 : if (showAll || shownSet.count(item.first) != 0) {
119 : myListedConditions.insert(item.first);
120 : }
121 : }
122 638 : }
123 1292 : if (hasParameter("extra-detectors")) {
124 4 : const std::string extraIDs = getParameter("extra-detectors", "");
125 8 : for (std::string customID : StringTokenizer(extraIDs).getVector()) {
126 : try {
127 4 : myExtraLoops.push_back(retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(customID, extraIDs, true));
128 2 : } catch (ProcessError&) {
129 2 : myExtraE2.push_back(retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(customID, extraIDs, true));
130 2 : }
131 2 : }
132 : }
133 646 : myStack.push_back(std::map<std::string, double>());
134 646 : }
135 :
136 :
137 3225 : MSActuatedTrafficLightLogic::~MSActuatedTrafficLightLogic() { }
138 :
139 : void
140 646 : MSActuatedTrafficLightLogic::init(NLDetectorBuilder& nb) {
141 646 : MSTrafficLightLogic::init(nb);
142 646 : initAttributeOverride();
143 646 : initSwitchingRules();
144 5063 : for (int i = 0; i < (int)myPhases.size(); i++) {
145 4417 : initTargets(i);
146 : }
147 646 : 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 646 : 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 646 : 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 646 : int detEdgeIndex = -1;
179 646 : int detLaneIndex = 0;
180 1292 : const double detDefaultLength = StringUtils::toDouble(getParameter("detector-length",
181 1292 : OptionsCont::getOptions().getValueString("tls.actuated.detector-length")));
182 : MSEdge* prevDetEdge = nullptr;
183 10281 : for (LaneVector& lanes : myLanes) {
184 19403 : for (MSLane* lane : lanes) {
185 9768 : const std::string customID = getParameter(lane->getID());
186 9768 : if (noVehicles(lane->getPermissions()) && customID == "") {
187 : // do not build detectors on green verges or sidewalks
188 819 : continue;
189 : }
190 8949 : if (laneInductLoopMap.find(lane) != laneInductLoopMap.end()) {
191 : // only build one detector per lane
192 4249 : continue;
193 : }
194 4700 : const SUMOTime minDur = getMinimumMinDuration(lane, multiNextTargets);
195 4700 : if (minDur == std::numeric_limits<SUMOTime>::max() && customID == "" && !myBuildAllDetectors) {
196 : // only build detector if this lane is relevant for an actuated phase
197 214 : continue;
198 : }
199 4486 : double length = lane->getLength();
200 : double ilpos;
201 : double inductLoopPosition;
202 4486 : MSInductLoop* loop = nullptr;
203 4486 : if (&lane->getEdge() != prevDetEdge) {
204 2508 : detEdgeIndex++;
205 2508 : detLaneIndex = 0;
206 : prevDetEdge = &lane->getEdge();
207 : } else {
208 1978 : detLaneIndex++;
209 : }
210 4486 : const bool isBikeLane = (lane->getPermissions() & ~SVC_PEDESTRIAN) == SVC_BICYCLE;
211 4486 : const double defaultLength = isBikeLane ? DEFAULT_BIKE_LENGTH_WITH_GAP : DEFAULT_LENGTH_WITH_GAP;
212 4486 : if (customID == "") {
213 4042 : const double speed = isBikeLane ? DEFAULT_BICYCLE_SPEED : lane->getSpeedLimit();
214 8084 : inductLoopPosition = MIN2(
215 4042 : myDetectorGap * speed,
216 4042 : (STEPS2TIME(minDur) / myPassingTime + 0.5) * defaultLength);
217 :
218 : // check whether the lane is long enough
219 4042 : ilpos = length - inductLoopPosition;
220 4042 : MSLane* placementLane = lane;
221 5246 : while (ilpos < 0 && placementLane->getIncomingLanes().size() == 1
222 5312 : && placementLane->getIncomingLanes().front().viaLink->getCorrespondingEntryLink()->getTLLogic() == nullptr) {
223 561 : placementLane = placementLane->getLogicalPredecessorLane();
224 561 : ilpos += placementLane->getLength();
225 : }
226 4042 : if (ilpos < 0) {
227 : ilpos = 0;
228 : }
229 : // Build the induct loop and set it into the container
230 4042 : const double detLength = getDouble("detector-length:" + lane->getID(), detDefaultLength);
231 12126 : std::string id = myDetectorPrefix + "D" + toString(detEdgeIndex) + "." + toString(detLaneIndex);
232 4042 : loop = static_cast<MSInductLoop*>(nb.createInductLoop(id, placementLane, ilpos, detLength, "", myVehicleTypes, "", (int)PersonMode::NONE, myShowDetectors));
233 4042 : MSNet::getInstance()->getDetectorControl().add(SUMO_TAG_INDUCTION_LOOP, loop, myFile, myFreq);
234 444 : } else if (customID == NO_DETECTOR) {
235 240 : continue;
236 : } else {
237 408 : loop = dynamic_cast<MSInductLoop*>(MSNet::getInstance()->getDetectorControl().getTypedDetectors(SUMO_TAG_INDUCTION_LOOP).get(customID));
238 204 : 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 204 : inductLoopPosition = length - ilpos;
243 : }
244 4246 : const double maxGap = getDouble("max-gap:" + lane->getID(), myMaxGap);
245 4246 : const double jamThreshold = getDouble("jam-threshold:" + lane->getID(), myJamThreshold);
246 4246 : laneInductLoopMap[lane] = loop;
247 4246 : inductLoopInfoMap[loop] = (int)myInductLoops.size();
248 4246 : myInductLoops.push_back(InductLoopInfo(loop, lane, (int)myPhases.size(), maxGap, jamThreshold));
249 :
250 4246 : 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 646 : std::vector<bool> neverMajor(numLinks, true);
281 5063 : for (const MSPhaseDefinition* phase : myPhases) {
282 : const std::string& state = phase->getState();
283 79731 : for (int i = 0; i < numLinks; i++) {
284 75314 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
285 : neverMajor[i] = false;
286 : }
287 : }
288 : }
289 646 : std::vector<bool> oneLane(numLinks, false);
290 646 : std::vector<bool> turnaround(numLinks, true);
291 10281 : for (int i = 0; i < numLinks; i++) {
292 15928 : for (MSLane* lane : getLanesAt(i)) {
293 : // only count motorized vehicle lanes
294 : int numMotorized = 0;
295 32447 : for (MSLane* l : lane->getEdge().getLanes()) {
296 22715 : if ((l->getPermissions() & motorized) != 0) {
297 15940 : numMotorized++;
298 : }
299 : }
300 9732 : if (numMotorized == 1) {
301 : oneLane[i] = true;
302 3439 : break;
303 : }
304 : }
305 10672 : for (MSLink* link : getLinksAt(i)) {
306 9450 : if (!link->isTurnaround()) {
307 : turnaround[i] = false;
308 8413 : break;
309 : }
310 : }
311 : }
312 :
313 5063 : for (const MSPhaseDefinition* phase : myPhases) {
314 4417 : 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 32531 : for (int i = 0; i < numLinks; i++) {
326 30681 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
327 : greenLinks.insert(i);
328 : if (phase->isActuated()) {
329 : actuatedLinks.insert(i);
330 : }
331 :
332 18062 : for (MSLink* link : getLinksAt(i)) {
333 9109 : if (link->getLane()->isCrossing()) {
334 2542 : while (myCrossingsForPhase.size() < myPhases.size()) {
335 1420 : myCrossingsForPhase.push_back(std::vector<const MSLink*>());
336 : }
337 1122 : myCrossingsForPhase[phaseIndex].push_back(link);
338 : }
339 : }
340 :
341 21728 : } else if (state[i] == LINKSTATE_TL_GREEN_MINOR) {
342 1042 : if (((neverMajor[i] || turnaround[i]) // check1a, 1d
343 3238 : && hasMajor(state, getLanesAt(i))) // check1b
344 1302 : || oneLane[i] // check1c
345 5290 : || weakConflict(i, state)) { // check1e, check1f
346 : greenLinks.insert(i);
347 2938 : 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 92569 : for (MSLane* lane : getLanesAt(i)) {
369 : if (laneInductLoopMap.count(lane) != 0) {
370 27149 : loopLinks[laneInductLoopMap[lane]].insert(i);
371 : }
372 : }
373 : }
374 16191 : for (auto& item : loopLinks) {
375 14341 : MSInductLoop* loop = item.first;
376 14341 : const InductLoopInfo& info = myInductLoops[inductLoopInfoMap[loop]];
377 14341 : const MSLane* loopLane = info.lane;
378 : bool usable = true;
379 : bool foundUsable = false;
380 : // check1
381 40470 : for (int j : item.second) {
382 16256 : 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 14341 : 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 14341 : if (usable && info.jamThreshold <= 0) {
399 13518 : for (MSLink* link : loopLane->getLinkCont()) {
400 8729 : if (link->isTurnaround()) {
401 1228 : continue;
402 : }
403 7501 : const MSLane* next = link->getLane();
404 : if (laneInductLoopMap.count(next) != 0) {
405 671 : MSInductLoop* nextLoop = laneInductLoopMap[next];
406 1369 : 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 5271 : if (usable) {
421 5252 : 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 14725 : for (int j : item.second) {
428 9473 : linkToLoops[j].insert(item.first);
429 : }
430 : }
431 : }
432 1850 : if (loops.size() == 0 && phase->isActuated()) {
433 48 : 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 4417 : myInductLoopsForPhase.push_back(loopInfos);
449 9669 : for (MSInductLoop* loop : loops) {
450 55366 : for (InductLoopInfo& loopInfo : myInductLoops) {
451 50114 : if (loopInfo.loop == loop) {
452 5252 : myInductLoopsForPhase.back().push_back(&loopInfo);
453 5252 : loopInfo.servedPhase[phaseIndex] = true;
454 : }
455 : }
456 : }
457 4417 : }
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 8979 : for (int i : actuatedLinks) {
468 9506 : if (linkToLoops[i].size() == 0 && myLinks[i].size() > 0
469 9423 : && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
470 626 : if (getParameter(myLinks[i].front()->getLaneBefore()->getID()) != NO_DETECTOR) {
471 73 : warnLinks.push_back(i);
472 : }
473 : }
474 : }
475 646 : if (warnLinks.size() > 0) {
476 69 : 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 1784 : for (const auto& kv : getParametersMap()) {
480 2276 : if (StringUtils::startsWith(kv.first, "linkMaxDur:")) {
481 2 : int link = StringUtils::toInt(kv.first.substr(11));
482 2 : 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 2 : if (myLinkMaxGreenTimes.empty()) {
487 2 : myLinkMaxGreenTimes = std::vector<SUMOTime>(myNumLinks, std::numeric_limits<SUMOTime>::max());
488 : }
489 2 : myLinkMaxGreenTimes[link] = string2time(kv.second);
490 2272 : } else if (StringUtils::startsWith(kv.first, "linkMinDur:")) {
491 18 : int link = StringUtils::toInt(kv.first.substr(11));
492 18 : 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 18 : if (myLinkMinGreenTimes.empty()) {
497 4 : myLinkMinGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
498 : }
499 18 : myLinkMinGreenTimes[link] = string2time(kv.second);
500 : }
501 : }
502 646 : bool haveSwitchingRules = myConditions.size() > 0 || myAssignments.size() > 0 || myFunctions.size() > 0;
503 4775 : for (auto sr : mySwitchingRules) {
504 4185 : if (sr.enabled) {
505 : haveSwitchingRules = true;
506 : break;
507 : }
508 : }
509 646 : if (myLinkMaxGreenTimes.size() > 0 || myLinkMinGreenTimes.size() > 0 || haveSwitchingRules) {
510 64 : myLinkGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
511 64 : myLinkRedTimes = std::vector<SUMOTime>(myNumLinks, 0);
512 : }
513 : //std::cout << SIMTIME << " linkMaxGreenTimes=" << toString(myLinkMaxGreenTimes) << "\n";
514 1292 : }
515 :
516 :
517 : bool
518 1264 : MSActuatedTrafficLightLogic::weakConflict(int tlIndex, const std::string& state) const {
519 1400 : for (MSLink* link : getLinksAt(tlIndex)) {
520 : int linkIndex = link->getIndex();
521 : const MSJunction* junction = link->getJunction();
522 14140 : for (int i = 0; i < (int)myLinks.size(); i++) {
523 14004 : if (i == tlIndex) {
524 681 : continue;
525 : }
526 13323 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
527 11750 : 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 6216 : if (junction == junction2) {
532 5036 : const MSJunctionLogic* logic = junction->getLogic();
533 : //std::cout << " greenLink=" << i << " isFoe=" << logic->getFoesFor(linkIndex).test(foeIndex) << "\n";
534 5036 : if (logic->getFoesFor(linkIndex).test(foeIndex)
535 1444 : && (foe->getPermissions() & ~SVC_VULNERABLE) != 0 // check1e
536 6165 : && &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 403990 : MSActuatedTrafficLightLogic::getMinDur(int step) const {
553 403990 : step = step < 0 ? myStep : step;
554 403990 : const MSPhaseDefinition* p = myPhases[step];
555 403990 : return p->minDuration != MSPhaseDefinition::OVERRIDE_DURATION
556 403990 : ? p->minDuration
557 411646 : : TIME2STEPS(evalExpression(myConditions.find("minDur:" + toString(step))->second));
558 : }
559 :
560 : SUMOTime
561 180348 : MSActuatedTrafficLightLogic::getMaxDur(int step) const {
562 180348 : step = step < 0 ? myStep : step;
563 180348 : const MSPhaseDefinition* p = myPhases[step];
564 180348 : return p->maxDuration != MSPhaseDefinition::OVERRIDE_DURATION
565 180348 : ? p->maxDuration
566 187860 : : TIME2STEPS(evalExpression(myConditions.find("maxDur:" + toString(step))->second));
567 : }
568 :
569 : SUMOTime
570 319137 : MSActuatedTrafficLightLogic::getEarliestEnd(int step) const {
571 319137 : step = step < 0 ? myStep : step;
572 319137 : const MSPhaseDefinition* p = myPhases[step];
573 319137 : return p->earliestEnd != MSPhaseDefinition::OVERRIDE_DURATION
574 319137 : ? p->earliestEnd
575 326649 : : TIME2STEPS(evalExpression(myConditions.find("earliestEnd:" + toString(step))->second));
576 : }
577 :
578 : SUMOTime
579 399313 : MSActuatedTrafficLightLogic::getLatestEnd(int step) const {
580 399313 : step = step < 0 ? myStep : step;
581 399313 : const MSPhaseDefinition* p = myPhases[step];
582 399313 : return p->latestEnd != MSPhaseDefinition::OVERRIDE_DURATION
583 399313 : ? p->latestEnd
584 406825 : : TIME2STEPS(evalExpression(myConditions.find("latestEnd:" + toString(step))->second));
585 : }
586 :
587 :
588 : void
589 646 : MSActuatedTrafficLightLogic::initAttributeOverride() {
590 : const SUMOTime ovrd = MSPhaseDefinition::OVERRIDE_DURATION;
591 5063 : for (int i = 0; i < (int)myPhases.size(); i++) {
592 4417 : MSPhaseDefinition* phase = myPhases[i];
593 4417 : const std::string errorSuffix = "' for overriding attribute in phase " + toString(i) + " of tlLogic '" + getID() + "' in program '" + getProgramID() + "'.";
594 4417 : if (phase->minDuration == ovrd) {
595 8 : const std::string cond = "minDur:" + toString(i);
596 : if (myConditions.count(cond) == 0) {
597 0 : throw ProcessError("Missing condition '" + cond + errorSuffix);
598 : }
599 : }
600 4417 : if (phase->maxDuration == ovrd) {
601 8 : const std::string cond = "maxDur:" + toString(i);
602 : if (myConditions.count(cond) == 0) {
603 0 : throw ProcessError("Missing condition '" + cond + errorSuffix);
604 : }
605 : }
606 4417 : if (phase->earliestEnd == ovrd) {
607 8 : const std::string cond = "earliestEnd:" + toString(i);
608 : if (myConditions.count(cond) == 0) {
609 0 : throw ProcessError("Missing condition '" + cond + errorSuffix);
610 : }
611 : }
612 4417 : if (phase->latestEnd == ovrd) {
613 8 : 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 646 : }
620 :
621 :
622 : void
623 646 : MSActuatedTrafficLightLogic::initSwitchingRules() {
624 5063 : for (int i = 0; i < (int)myPhases.size(); i++) {
625 4417 : SwitchingRules sr;
626 4417 : MSPhaseDefinition* phase = myPhases[i];
627 4417 : std::vector<int> nextPhases = phase->nextPhases;
628 4417 : if (nextPhases.size() == 0) {
629 4075 : nextPhases.push_back((i + 1) % (int)myPhases.size());
630 342 : } else if (nextPhases.size() > 1) {
631 126 : myHasMultiTarget = true;
632 : }
633 9034 : for (int next : nextPhases) {
634 4617 : if (next >= 0 && next < (int)myPhases.size()) {
635 4617 : const MSPhaseDefinition* nextPhase = myPhases[next];
636 4617 : if (nextPhase->earlyTarget != "" || nextPhase->finalTarget != "") {
637 130 : sr.enabled = true;
638 : }
639 : }
640 : }
641 : // simplifies later code
642 4417 : phase->nextPhases = nextPhases;
643 4417 : mySwitchingRules.push_back(sr);
644 4417 : }
645 646 : }
646 :
647 :
648 : void
649 4417 : MSActuatedTrafficLightLogic::initTargets(int step) {
650 : // next -> target -> transitionTime starting from step
651 : std::map<int, std::map<int, SUMOTime> > reached;
652 4417 : const std::vector<int>& next = myPhases[step]->nextPhases;
653 9034 : for (int n : next) {
654 4617 : findTargets(step, n, 0, reached[n]);
655 : }
656 45430 : for (int target = 0; target < (int)myPhases.size(); target++) {
657 41013 : int bestNext = next[0];
658 : SUMOTime bestTime = SUMOTime_MAX;
659 86580 : for (auto item : reached) {
660 : auto it = item.second.find(target);
661 45567 : if (it != item.second.end()) {
662 17966 : SUMOTime transitionTime = it->second;
663 17966 : if (transitionTime < bestTime) {
664 : bestTime = transitionTime;
665 17468 : bestNext = item.first;
666 : }
667 : }
668 : }
669 41013 : if (bestTime != SUMOTime_MAX) {
670 17302 : myTargets[step][bestNext].push_back(target);
671 : //std::cout << " myTargets step=" << step << " bestNext=" << bestNext << " target=" << target << "\n";
672 : }
673 : }
674 4417 : }
675 :
676 :
677 : void
678 40157 : MSActuatedTrafficLightLogic::findTargets(int origStep, int n, SUMOTime priorTransition, std::map<int, SUMOTime>& found) {
679 40157 : std::pair<int, SUMOTime> tDur = getTarget(n);
680 40157 : int target = tDur.first;
681 40157 : SUMOTime transitionTime = tDur.second + priorTransition;
682 : //std::cout << " findTargets origStep=" << origStep << " n=" << n << " ptt=" << priorTransition << " target=" << target << " tt=" << transitionTime << "\n";
683 40157 : if (target == origStep) {
684 : // full circle
685 : //std::cout << " foundCircle\n";
686 19995 : return;
687 : }
688 : auto it = found.find(target);
689 36730 : if (it != found.end()) {
690 18764 : 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 20162 : found[target] = transitionTime;
701 : //std::cout << " targetNext=" << toString(myPhases[target]->nextPhases) << "\n";
702 55702 : for (int n2 : myPhases[target]->nextPhases) {
703 35540 : findTargets(origStep, n2, transitionTime, found);
704 : }
705 : }
706 :
707 :
708 : std::set<int>
709 646 : MSActuatedTrafficLightLogic::getMultiNextTargets() const {
710 : std::set<int> result;
711 646 : if (myHasMultiTarget) {
712 : // find all phase that are the target green phase of a 'next' attribute
713 708 : for (const MSPhaseDefinition* p : myPhases) {
714 1452 : for (int next : p->nextPhases) {
715 1652 : result.insert(getTarget(next).first);
716 : }
717 : }
718 : }
719 646 : return result;
720 : }
721 :
722 :
723 : SUMOTime
724 4700 : MSActuatedTrafficLightLogic::getMinimumMinDuration(MSLane* lane, const std::set<int>& multiNextTargets) const {
725 : SUMOTime result = std::numeric_limits<SUMOTime>::max();
726 42083 : for (int pI = 0; pI < (int)myPhases.size(); pI++) {
727 37383 : const MSPhaseDefinition* phase = myPhases[pI];
728 : const std::string& state = phase->getState();
729 807351 : for (int i = 0; i < (int)state.size(); i++) {
730 769968 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
731 410509 : for (MSLane* cand : getLanesAt(i)) {
732 204460 : if (lane == cand) {
733 : if (phase->isActuated()) {
734 12931 : 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 4700 : return result;
744 : }
745 :
746 : bool
747 3238 : MSActuatedTrafficLightLogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
748 39067 : for (int i = 0; i < (int)state.size(); i++) {
749 38553 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
750 18807 : for (MSLane* cand : getLanesAt(i)) {
751 17024 : for (MSLane* lane : lanes) {
752 9566 : 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 646 : MSActuatedTrafficLightLogic::activateProgram() {
766 646 : MSTrafficLightLogic::activateProgram();
767 646 : for (InductLoopInfo& loopInfo : myInductLoops) {
768 0 : loopInfo.loop->setVisible(myShowDetectors);
769 : }
770 646 : }
771 :
772 :
773 : void
774 38 : MSActuatedTrafficLightLogic::deactivateProgram() {
775 38 : MSTrafficLightLogic::deactivateProgram();
776 70 : for (InductLoopInfo& loopInfo : myInductLoops) {
777 32 : loopInfo.loop->setVisible(false);
778 : }
779 38 : }
780 :
781 : void
782 28 : MSActuatedTrafficLightLogic::changeStepAndDuration(MSTLLogicControl& tlcontrol,
783 : SUMOTime simStep, int step, SUMOTime stepDuration) {
784 : // do not change timing if the phase changes
785 28 : 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 28 : }
799 :
800 :
801 : void
802 1 : MSActuatedTrafficLightLogic::loadState(MSTLLogicControl& tlcontrol, SUMOTime t, int step, SUMOTime spentDuration, bool active) {
803 1 : myAmActive = active;
804 1 : const SUMOTime lastSwitch = t - spentDuration;
805 1 : myStep = step;
806 1 : myPhases[myStep]->myLastSwitch = lastSwitch;
807 1 : const SUMOTime nextSwitch = t + getPhase(step).minDuration - spentDuration;
808 1 : mySwitchCommand->deschedule(this);
809 1 : mySwitchCommand = new SwitchCommand(tlcontrol, this, nextSwitch);
810 1 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(mySwitchCommand, nextSwitch);
811 1 : if (myAmActive) {
812 1 : setTrafficLightSignals(lastSwitch);
813 : }
814 1 : tlcontrol.get(getID()).executeOnSwitchActions();
815 1 : }
816 :
817 :
818 : SUMOTime
819 388353 : MSActuatedTrafficLightLogic::trySwitch() {
820 : // checks if the actual phase should be continued
821 : // @note any vehicles which arrived during the previous phases which are now waiting between the detector and the stop line are not
822 : // considere here. RiLSA recommends to set minDuration in a way that lets all vehicles pass the detector
823 388353 : SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
824 776706 : executeAssignments(myAssignments, myConditions);
825 :
826 388353 : if (myLinkGreenTimes.size() > 0) {
827 : // constraints exist, record green time durations for each link
828 35742 : const std::string& state = getCurrentPhaseDef().getState();
829 35742 : SUMOTime lastDuration = SIMSTEP - myLastTrySwitchTime;
830 325302 : for (int i = 0; i < myNumLinks; i++) {
831 289560 : if (state[i] == 'G' || state[i] == 'g') {
832 129272 : myLinkGreenTimes[i] += lastDuration;
833 : } else {
834 160288 : myLinkGreenTimes[i] = 0;
835 : }
836 289560 : if (state[i] == 'r' || state[i] == 'u') {
837 155156 : myLinkRedTimes[i] += lastDuration;
838 : } else {
839 134404 : myLinkRedTimes[i] = 0;
840 : }
841 : }
842 : }
843 388353 : myLastTrySwitchTime = now;
844 : // decide the next phase
845 388353 : const bool multiTarget = myPhases[myStep]->nextPhases.size() > 1 && myPhases[myStep]->nextPhases.front() >= 0;
846 : const int origStep = myStep;
847 : int nextStep = myStep;
848 388353 : SUMOTime actDuration = now - myPhases[myStep]->myLastSwitch;
849 :
850 388353 : if (mySwitchingRules[myStep].enabled) {
851 30078 : const bool mustSwitch = MIN2(getMaxDur() - actDuration, getLatest()) <= 0;
852 30078 : nextStep = decideNextPhaseCustom(mustSwitch);
853 : } else {
854 : // default algorithm
855 358275 : const double detectionGap = gapControl();
856 : #ifdef DEBUG_PHASE_SELECTION
857 : if (DEBUG_COND) {
858 : std::cout << SIMTIME << " p=" << myStep
859 : << " trySwitch dGap=" << (detectionGap == std::numeric_limits<double>::max() ? "inf" : toString(detectionGap))
860 : << " multi=" << multiTarget << "\n";
861 : }
862 : #endif
863 358275 : if (detectionGap < std::numeric_limits<double>::max() && !multiTarget && !myTraCISwitch) {
864 146332 : return duration(detectionGap);
865 : }
866 207546 : if (multiTarget) {
867 76828 : nextStep = decideNextPhase();
868 : } else {
869 135115 : if (myPhases[myStep]->nextPhases.size() == 1 && myPhases[myStep]->nextPhases.front() >= 0) {
870 : nextStep = myPhases[myStep]->nextPhases.front();
871 : } else {
872 0 : nextStep = (myStep + 1) % (int)myPhases.size();
873 : }
874 : }
875 : }
876 :
877 242021 : myTraCISwitch = false;
878 242021 : if (myLinkMinGreenTimes.size() > 0) {
879 330 : SUMOTime linkMinDur = getLinkMinDuration(getTarget(nextStep).first);
880 330 : if (linkMinDur > 0) {
881 : // for multiTarget, the current phase must be extended but if another
882 : // targer is chosen, earlier switching than linkMinDur is possible
883 106 : return multiTarget ? TIME2STEPS(1) : linkMinDur;
884 : }
885 : }
886 241933 : myStep = nextStep;
887 : assert(myStep <= (int)myPhases.size());
888 : assert(myStep >= 0);
889 : //stores the time the phase started
890 241933 : const SUMOTime prevStart = myPhases[myStep]->myLastSwitch;
891 241933 : if (myStep != origStep) {
892 131745 : myPhases[origStep]->myLastEnd = now;
893 131745 : myPhases[myStep]->myLastSwitch = now;
894 : actDuration = 0;
895 : }
896 : // activate coloring
897 241933 : if ((myShowDetectors || myHasMultiTarget) && getCurrentPhaseDef().isGreenPhase()) {
898 342887 : for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
899 : //std::cout << SIMTIME << " p=" << myStep << " loopinfo=" << loopInfo->loop->getID() << " set lastGreen=" << STEPS2TIME(now) << "\n";
900 : if (loopInfo->isJammed()) {
901 74 : loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
902 : } else {
903 240244 : loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
904 : }
905 240318 : loopInfo->lastGreenTime = now;
906 : }
907 : }
908 : // set the next event
909 : #ifdef DEBUG_PHASE_SELECTION
910 : if (DEBUG_COND) {
911 : std::cout << SIMTIME << " tl=" << getID() << " p=" << myStep
912 : << " nextTryMinDur=" << STEPS2TIME(getMinDur() - actDuration)
913 : << " nextTryEarliest=" << STEPS2TIME(getEarliest(prevStart)) << "\n";
914 : }
915 : #endif
916 241933 : SUMOTime minRetry = myStep != origStep ? 0 : TIME2STEPS(1);
917 241933 : return MAX3(minRetry, getMinDur() - actDuration, getEarliest(prevStart));
918 : }
919 :
920 :
921 : // ------------ "actuated" algorithm methods
922 : SUMOTime
923 146332 : MSActuatedTrafficLightLogic::duration(const double detectionGap) const {
924 : assert(getCurrentPhaseDef().isGreenPhase());
925 : assert((int)myPhases.size() > myStep);
926 146332 : const SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
927 : // ensure that minimum duration is kept
928 146332 : SUMOTime newDuration = getMinDur() - actDuration;
929 : // try to let the last detected vehicle pass the intersection (duration must be positive)
930 162049 : newDuration = MAX3(newDuration, TIME2STEPS(myDetectorGap - detectionGap), SUMOTime(1));
931 : // cut the decimal places to ensure that phases always have integer duration
932 146332 : if (newDuration % 1000 != 0) {
933 101566 : const SUMOTime totalDur = newDuration + actDuration;
934 101566 : newDuration = (totalDur / 1000 + 1) * 1000 - actDuration;
935 : }
936 : // ensure that the maximum duration is not exceeded
937 146332 : newDuration = MIN3(newDuration, getMaxDur() - actDuration, getLatest());
938 146332 : return newDuration;
939 : }
940 :
941 :
942 : double
943 359597 : MSActuatedTrafficLightLogic::gapControl() {
944 : //intergreen times should not be lengthend
945 : assert((int)myPhases.size() > myStep);
946 : double result = std::numeric_limits<double>::max();
947 359597 : if (MSGlobals::gUseMesoSim) {
948 : return result;
949 : }
950 : // switch off active colors
951 359597 : if (myShowDetectors) {
952 232946 : for (InductLoopInfo& loopInfo : myInductLoops) {
953 206194 : if (loopInfo.lastGreenTime < loopInfo.loop->getLastDetectionTime()) {
954 46720 : loopInfo.loop->setSpecialColor(&RGBColor::RED);
955 : } else {
956 159474 : loopInfo.loop->setSpecialColor(nullptr);
957 : }
958 : }
959 : }
960 359597 : if (!getCurrentPhaseDef().isGreenPhase()) {
961 : return result; // end current phase
962 : }
963 :
964 : // Checks, if the maxDuration is kept. No phase should last longer than maxDuration.
965 298861 : SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
966 298861 : if (actDuration >= getCurrentPhaseDef().maxDuration || maxLinkDurationReached() || getLatest() == 0) {
967 : #ifdef DEBUG_PHASE_SELECTION
968 : if (DEBUG_COND) {
969 : std::cout << SIMTIME << " actDuration=" << STEPS2TIME(actDuration) << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
970 : << " maxLinkDurationReached=" << maxLinkDurationReached() << " latest=" << STEPS2TIME(getLatest()) << "\n";
971 : }
972 : #endif
973 95015 : return result; // end current phase
974 : }
975 :
976 : // now the gapcontrol starts
977 725173 : for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
978 521327 : MSInductLoop* loop = loopInfo->loop;
979 : if (loopInfo->isJammed()) {
980 295 : loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
981 : } else {
982 521032 : loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
983 : }
984 521327 : const double actualGap = loop->getTimeSinceLastDetection();
985 521327 : if (actualGap < loopInfo->maxGap && !loopInfo->isJammed()) {
986 : result = MIN2(result, actualGap);
987 : }
988 : }
989 : return result;
990 : }
991 :
992 : int
993 76828 : MSActuatedTrafficLightLogic::decideNextPhase() {
994 76828 : const auto& cands = myPhases[myStep]->nextPhases;
995 : // decide by priority
996 : // first target is the default when there is no traffic
997 : // @note: to keep the current phase, even when there is no traffic, it must be added to 'next' explicitly
998 76828 : int result = cands.front();
999 : int maxPrio = 0;
1000 76828 : SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
1001 76828 : const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && !maxLinkDurationReached() && getLatest() > 0;
1002 : if (canExtend) {
1003 : // consider keeping the current phase until maxDur is reached
1004 : // (only when there is still traffic in that phase)
1005 7703 : int currentPrio = getPhasePriority(myStep);
1006 : #ifdef DEBUG_PHASE_SELECTION
1007 : std::cout << SIMTIME << " p=" << myStep << " loops=" << myInductLoopsForPhase[myStep].size() << " currentPrio=" << currentPrio << "\n";
1008 : #endif
1009 7703 : if (currentPrio > maxPrio) {
1010 4470 : result = myStep;
1011 : maxPrio = currentPrio;
1012 : }
1013 : }
1014 233732 : for (int step : cands) {
1015 : int prio = 0;
1016 245658 : for (int target : myTargets[myStep][step]) {
1017 88754 : prio += getPhasePriority(target);
1018 : #ifdef DEBUG_PHASE_SELECTION
1019 : if (DEBUG_COND) {
1020 : std::cout << SIMTIME << " p=" << myStep << " step=" << step << " target=" << target << " loops=" << myInductLoopsForPhase[target].size() << " prio=" << prio << "\n";
1021 : }
1022 : #endif
1023 : }
1024 156904 : if (prio > maxPrio && canExtendLinkGreen(getTarget(step).first)) {
1025 : maxPrio = prio;
1026 365 : result = step;
1027 : }
1028 : }
1029 76828 : return result;
1030 : }
1031 :
1032 :
1033 : std::pair<int, SUMOTime>
1034 41678 : MSActuatedTrafficLightLogic::getTarget(int step) const {
1035 : int seen = 0;
1036 41678 : int origStep = step;
1037 : SUMOTime dur = 0;
1038 :
1039 : // if step is a transition, find the upcoming green phase
1040 78031 : while (!myPhases[step]->isGreenPhase()) {
1041 36353 : seen += 1;
1042 36353 : dur += myPhases[step]->duration;
1043 36353 : if (myPhases[step]->nextPhases.size() > 0 && myPhases[step]->nextPhases.front() >= 0) {
1044 36373 : for (int next : myPhases[step]->nextPhases) {
1045 36373 : if (next != step) {
1046 : step = next;
1047 : break;
1048 : }
1049 : }
1050 : } else {
1051 0 : step = (step + 1) % (int)myPhases.size();
1052 : }
1053 36353 : if (step == origStep || seen > (int)myPhases.size()) {
1054 0 : WRITE_WARNING("At actuated tlLogic '" + getID() + "', infinite transition loop from phase " + toString(origStep));
1055 0 : return std::make_pair(0, 0);
1056 : }
1057 : }
1058 : return std::make_pair(step, dur);
1059 : }
1060 :
1061 : int
1062 66435 : MSActuatedTrafficLightLogic::getDetectorPriority(const InductLoopInfo& loopInfo) const {
1063 66435 : MSInductLoop* loop = loopInfo.loop;
1064 66435 : const double actualGap = loop->getTimeSinceLastDetection();
1065 66435 : if ((actualGap < loopInfo.maxGap && !loopInfo.isJammed())
1066 66435 : || loopInfo.lastGreenTime < loop->getLastDetectionTime()) {
1067 10470 : SUMOTime inactiveTime = MSNet::getInstance()->getCurrentTimeStep() - loopInfo.lastGreenTime;
1068 : // @note. Inactive time could also be tracked regardless of current activity (to increase robustness in case of detection failure
1069 10470 : if (inactiveTime > myInactiveThreshold) {
1070 : #ifdef DEBUG_PHASE_SELECTION
1071 : if (DEBUG_COND) {
1072 : std::cout << " loop=" << loop->getID() << " gap=" << loop->getTimeSinceLastDetection() << " lastGreen=" << STEPS2TIME(loopInfo.lastGreenTime)
1073 : << " lastDetection=" << STEPS2TIME(loop->getLastDetectionTime()) << " inactive=" << STEPS2TIME(inactiveTime) << "\n";
1074 : }
1075 : #endif
1076 12 : return (int)STEPS2TIME(inactiveTime);
1077 : } else {
1078 : // give bonus to detectors that are currently served (if that phase can stil be extended)
1079 10458 : if (loopInfo.servedPhase[myStep]) {
1080 6108 : SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
1081 6108 : const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && getLatest() > 0;
1082 : #ifdef DEBUG_PHASE_SELECTION
1083 : if (DEBUG_COND) {
1084 : std::cout << " loop=" << loop->getID()
1085 : << " actDuration=" << STEPS2TIME(actDuration)
1086 : << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
1087 : << " getLatest=" << STEPS2TIME(getLatest())
1088 : << " canExtend=" << canExtend
1089 : << "\n";
1090 : }
1091 : #endif
1092 : if (canExtend) {
1093 : return DEFAULT_CURRENT_PRIORITY;
1094 : } else {
1095 4 : return 0;
1096 : }
1097 : }
1098 : return 1;
1099 : }
1100 : }
1101 : return 0;
1102 : }
1103 :
1104 : int
1105 96457 : MSActuatedTrafficLightLogic::getPhasePriority(int step) const {
1106 : int result = 0;
1107 162892 : for (const InductLoopInfo* loopInfo : myInductLoopsForPhase[step]) {
1108 66435 : result += getDetectorPriority(*loopInfo);
1109 : }
1110 96457 : if (myCrossingsForPhase.size() > 0) {
1111 159406 : for (const MSLink* crossingEntry : myCrossingsForPhase[step]) {
1112 : auto* aPersons = crossingEntry->getApproachingPersons();
1113 80749 : if (aPersons != nullptr && aPersons->size() > 0) {
1114 77 : result += DEFAULT_CROSSING_PRIORITY;
1115 : }
1116 : }
1117 : }
1118 96457 : return result;
1119 : }
1120 :
1121 :
1122 : void
1123 0 : MSActuatedTrafficLightLogic::setShowDetectors(bool show) {
1124 0 : myShowDetectors = show;
1125 0 : for (InductLoopInfo& loopInfo : myInductLoops) {
1126 0 : loopInfo.loop->setVisible(myShowDetectors);
1127 : }
1128 0 : }
1129 :
1130 :
1131 : bool
1132 213197 : MSActuatedTrafficLightLogic::maxLinkDurationReached() {
1133 213197 : if (myLinkMaxGreenTimes.empty()) {
1134 : return false;
1135 : }
1136 1554 : for (int i = 0; i < myNumLinks; i++) {
1137 1436 : if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i]) {
1138 : //std::cout << SIMTIME << " maxLinkDurationReached i=" << i << "\n";
1139 : return true;
1140 : }
1141 : }
1142 : return false;
1143 : }
1144 :
1145 : bool
1146 365 : MSActuatedTrafficLightLogic::canExtendLinkGreen(int target) {
1147 365 : if (myLinkMaxGreenTimes.empty()) {
1148 : return true;
1149 : }
1150 8 : const std::string& targetState = myPhases[target]->getState();
1151 104 : for (int i = 0; i < myNumLinks; i++) {
1152 96 : if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i] && (
1153 0 : targetState[i] == 'G' || targetState[i] == 'g')) {
1154 : //std::cout << SIMTIME << " cannotExtendLinkGreen target=" << target << " i=" << i << "\n";
1155 : return false;
1156 : }
1157 : }
1158 : return true;
1159 : }
1160 :
1161 : SUMOTime
1162 330 : MSActuatedTrafficLightLogic::getLinkMinDuration(int target) const {
1163 : SUMOTime result = 0;
1164 330 : if (target != myStep && myLinkMinGreenTimes.size() > 0) {
1165 330 : const std::string& state = myPhases[myStep]->getState();
1166 330 : const std::string& targetState = myPhases[target]->getState();
1167 5034 : for (int i = 0; i < myNumLinks; i++) {
1168 4704 : if (myLinkGreenTimes[i] < myLinkMinGreenTimes[i]
1169 1208 : && (state[i] == 'G' || state[i] == 'g')
1170 4886 : && !(targetState[i] == 'G' || targetState[i] == 'g')) {
1171 134 : result = MAX2(result, myLinkMinGreenTimes[i] - myLinkGreenTimes[i]);
1172 : //std::cout << SIMTIME << " getLinkMinDuration myStep=" << myStep << " target=" << target << " i=" << i
1173 : // << " greenTime=" << STEPS2TIME(myLinkGreenTimes[i]) << " min=" << STEPS2TIME(myLinkMinGreenTimes[i]) << " result=" << STEPS2TIME(result) << "\n";
1174 : }
1175 : }
1176 : }
1177 330 : return result;
1178 : }
1179 :
1180 : int
1181 30078 : MSActuatedTrafficLightLogic::decideNextPhaseCustom(bool mustSwitch) {
1182 76394 : for (int next : getCurrentPhaseDef().nextPhases) {
1183 47380 : const MSPhaseDefinition* phase = myPhases[next];
1184 47380 : const std::string& condition = mustSwitch ? phase->finalTarget : phase->earlyTarget;
1185 : #ifdef DEBUG_PHASE_SELECTION_CUSTOM
1186 : if (DEBUG_COND) {
1187 : std::cout << SIMTIME << " mustSwitch=" << mustSwitch << " cur=" << myStep << " next=" << next << " condition=" << condition
1188 : << " eval=" << (condition == "" ? NAN : evalExpression(condition)) << "\n";
1189 : }
1190 : #endif
1191 47380 : if (condition != "") {
1192 : // backward compatibility if a user redefined DEFAULT_CONDITION
1193 34648 : if (condition == DEFAULT_CONDITION && myConditions.count(DEFAULT_CONDITION) == 0) {
1194 1322 : if (gapControl() == std::numeric_limits<double>::max()) {
1195 84 : return next;
1196 : }
1197 30682 : } else if (evalExpression(condition)) {
1198 980 : return next;
1199 : }
1200 : }
1201 : }
1202 29014 : return mustSwitch ? getCurrentPhaseDef().nextPhases.back() : myStep;
1203 : }
1204 :
1205 :
1206 : double
1207 554816 : MSActuatedTrafficLightLogic::evalExpression(const std::string& condition) const {
1208 554816 : const size_t bracketOpen = condition.find('(');
1209 554816 : if (bracketOpen != std::string::npos) {
1210 : // find matching closing bracket
1211 : size_t bracketClose = std::string::npos;
1212 : int open = 1;
1213 518528 : for (size_t i = bracketOpen + 1; i < condition.size(); i++) {
1214 518528 : if (condition[i] == '(') {
1215 3732 : open++;
1216 514796 : } else if (condition[i] == ')') {
1217 42696 : open--;
1218 42696 : if (open == 0) {
1219 : bracketClose = i;
1220 : break;
1221 : }
1222 : }
1223 : }
1224 38964 : if (bracketClose == std::string::npos) {
1225 0 : throw ProcessError(TLF("Unmatched parentheses in condition %'", condition));
1226 : }
1227 : std::string cond2 = condition;
1228 38964 : const std::string inBracket = condition.substr(bracketOpen + 1, bracketClose - bracketOpen - 1);
1229 38964 : double bracketVal = evalExpression(inBracket);
1230 38964 : cond2.replace(bracketOpen, bracketClose - bracketOpen + 1, toString(bracketVal));
1231 : try {
1232 38964 : return evalExpression(cond2);
1233 0 : } catch (ProcessError& e) {
1234 0 : throw ProcessError(TLF("Error when evaluating expression '%':\n %", condition, e.what()));
1235 0 : }
1236 : }
1237 1031704 : std::vector<std::string> tokens = StringTokenizer(condition).getVector();
1238 : //std::cout << SIMTIME << " tokens(" << tokens.size() << ")=" << toString(tokens) << "\n";
1239 515852 : if (tokens.size() == 0) {
1240 0 : throw ProcessError(TLF("Invalid empty condition '%'", condition));
1241 515852 : } else if (tokens.size() == 1) {
1242 : try {
1243 330360 : return evalAtomicExpression(tokens[0]);
1244 0 : } catch (ProcessError& e) {
1245 0 : throw ProcessError(TLF("Error when evaluating expression '%':\n %", condition, e.what()));
1246 0 : }
1247 185492 : } else if (tokens.size() == 2) {
1248 2488 : if (tokens[0] == "not") {
1249 : try {
1250 2488 : return evalAtomicExpression(tokens[1]) == 0. ? 1. : 0.;
1251 0 : } catch (ProcessError& e) {
1252 0 : throw ProcessError(TLF("Error when evaluating expression '%':\n %", condition, e.what()));
1253 0 : }
1254 : } else {
1255 0 : throw ProcessError(TLF("Unsupported condition '%'", condition));
1256 : }
1257 183004 : } else if (tokens.size() == 3) {
1258 : // infix expression
1259 130232 : const double a = evalAtomicExpression(tokens[0]);
1260 130232 : const double b = evalAtomicExpression(tokens[2]);
1261 : const std::string& o = tokens[1];
1262 : //std::cout << SIMTIME << " o=" << o << " a=" << a << " b=" << b << "\n";
1263 : try {
1264 130232 : return evalTernaryExpression(a, o, b, condition);
1265 0 : } catch (ProcessError& e) {
1266 0 : throw ProcessError(TLF("Error when evaluating expression '%':\n %", condition, e.what()));
1267 0 : }
1268 : } else {
1269 52772 : const int iEnd = (int)tokens.size() - 1;
1270 608292 : for (const std::string& o : OPERATOR_PRECEDENCE) {
1271 2799100 : for (int i = 1; i < iEnd; i++) {
1272 2243580 : if (tokens[i] == o) {
1273 : try {
1274 52772 : const double val = evalTernaryExpression(
1275 52772 : evalAtomicExpression(tokens[i - 1]), o,
1276 105544 : evalAtomicExpression(tokens[i + 1]), condition);
1277 52772 : std::vector<std::string> newTokens(tokens.begin(), tokens.begin() + (i - 1));
1278 105544 : newTokens.push_back(toString(val));
1279 52772 : newTokens.insert(newTokens.end(), tokens.begin() + (i + 2), tokens.end());
1280 52772 : return evalExpression(toString(newTokens));
1281 52772 : } catch (ProcessError& e) {
1282 0 : throw ProcessError(TLF("Error when evaluating expression '%':\n %", condition, e.what()));
1283 0 : }
1284 : }
1285 : }
1286 : }
1287 0 : throw ProcessError(TLF("Parsing expressions with % elements ('%') is not supported", toString(tokens.size()), condition));
1288 : }
1289 : return true;
1290 515852 : }
1291 :
1292 : double
1293 183004 : MSActuatedTrafficLightLogic::evalTernaryExpression(double a, const std::string& o, double b, const std::string& condition) const {
1294 183004 : if (o == "=" || o == "==") {
1295 38704 : return (double)(a == b);
1296 162012 : } else if (o == "<") {
1297 0 : return (double)(a < b);
1298 162012 : } else if (o == ">") {
1299 70426 : return (double)(a > b);
1300 111622 : } else if (o == "<=") {
1301 0 : return (double)(a <= b);
1302 111622 : } else if (o == ">=") {
1303 20574 : return (double)(a >= b);
1304 101216 : } else if (o == "!=") {
1305 9456 : return (double)(a != b);
1306 91960 : } else if (o == "or" || o == "||") {
1307 36998 : return (double)(a || b);
1308 54962 : } else if (o == "and" || o == "&&") {
1309 32452 : return (double)(a && b);
1310 22510 : } else if (o == "+") {
1311 13194 : return a + b;
1312 9316 : } else if (o == "-") {
1313 0 : return a - b;
1314 9316 : } else if (o == "*") {
1315 0 : return a * b;
1316 9316 : } else if (o == "/") {
1317 58 : if (b == 0) {
1318 174 : WRITE_ERRORF(TL("Division by 0 in condition '%'"), condition);
1319 58 : return 0;
1320 : }
1321 0 : return a / b;
1322 9258 : } else if (o == "%") {
1323 9256 : return fmod(a, b);
1324 2 : } else if (o == "**" || o == "^") {
1325 2 : return pow(a, b);
1326 : } else {
1327 0 : throw ProcessError(TLF("Unsupported operator '%' in condition '%'", o, condition));
1328 : }
1329 : }
1330 :
1331 : double
1332 9256 : MSActuatedTrafficLightLogic::evalCustomFunction(const std::string& fun, const std::string& arg) const {
1333 27768 : std::vector<std::string> args = StringTokenizer(arg, ",").getVector();
1334 : const Function& f = myFunctions.find(fun)->second;
1335 9256 : if ((int)args.size() != f.nArgs) {
1336 0 : throw ProcessError(TLF("Function '%' requires % arguments but % were given", fun, toString(f.nArgs), toString(args.size())));
1337 : }
1338 : std::vector<double> args2;
1339 23140 : for (auto a : args) {
1340 13884 : args2.push_back(evalExpression(a));
1341 : }
1342 9256 : myStack.push_back(myStack.back());
1343 9256 : myStack.back()["$0"] = 0;
1344 23140 : for (int i = 0; i < (int)args2.size(); i++) {
1345 41652 : myStack.back()["$" + toString(i + 1)] = args2[i];
1346 : }
1347 : try {
1348 : ConditionMap empty;
1349 9256 : executeAssignments(f.assignments, empty, myConditions);
1350 0 : } catch (ProcessError& e) {
1351 0 : throw ProcessError(TLF("Error when evaluating function '%' with args '%' (%)", fun, joinToString(args2, ","), e.what()));
1352 0 : }
1353 9256 : double result = myStack.back()["$0"];
1354 : myStack.pop_back();
1355 9256 : return result;
1356 9256 : }
1357 :
1358 :
1359 : void
1360 397609 : MSActuatedTrafficLightLogic::executeAssignments(const AssignmentMap& assignments, ConditionMap& conditions, const ConditionMap& forbidden) const {
1361 499527 : for (const auto& assignment : assignments) {
1362 101918 : if (evalExpression(std::get<1>(assignment))) {
1363 : const std::string& id = std::get<0>(assignment);
1364 55944 : const double val = evalExpression(std::get<2>(assignment));
1365 : ConditionMap::iterator it = conditions.find(id);
1366 55944 : if (it != conditions.end()) {
1367 83244 : it->second = toString(val);
1368 14322 : } else if (forbidden.find(id) != forbidden.end()) {
1369 0 : throw ProcessError(TLF("Modifying global condition '%' is forbidden", id));
1370 : } else {
1371 14322 : myStack.back()[id] = val;
1372 : }
1373 : }
1374 : }
1375 397609 : }
1376 :
1377 :
1378 : double
1379 708402 : MSActuatedTrafficLightLogic::evalAtomicExpression(const std::string& expr) const {
1380 708402 : if (expr.size() == 0) {
1381 0 : throw ProcessError(TL("Invalid empty expression"));
1382 708402 : } else if (expr[0] == '!') {
1383 18776 : return evalAtomicExpression(expr.substr(1)) == 0. ? 1. : 0.;
1384 699014 : } else if (expr[0] == '-') {
1385 158 : return -evalAtomicExpression(expr.substr(1));
1386 : } else {
1387 : // check for 'operator:'
1388 698856 : const size_t pos = expr.find(':');
1389 698856 : if (pos == std::string::npos) {
1390 : auto it = myConditions.find(expr);
1391 624916 : if (it != myConditions.end()) {
1392 : // symbol lookup
1393 151972 : return evalExpression(it->second);
1394 : } else {
1395 : // look at stack
1396 : auto it2 = myStack.back().find(expr);
1397 472944 : if (it2 != myStack.back().end()) {
1398 32396 : return it2->second;
1399 : }
1400 : // must be a number
1401 440548 : return StringUtils::toDouble(expr);
1402 : }
1403 : } else {
1404 73940 : const std::string fun = expr.substr(0, pos);
1405 73940 : const std::string arg = expr.substr(pos + 1);
1406 73940 : if (fun == "z") {
1407 48946 : return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection();
1408 24994 : } else if (fun == "a") {
1409 : try {
1410 9790 : return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection() == 0;
1411 8536 : } catch (ProcessError&) {
1412 8536 : return retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(arg, expr, true)->getCurrentVehicleNumber();
1413 8536 : }
1414 15204 : } else if (fun == "w") {
1415 : try {
1416 3272 : return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getOccupancyTime();
1417 2184 : } catch (ProcessError&) {
1418 2184 : return retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(arg, expr, true)->getCurrentJamDuration();
1419 2184 : }
1420 11932 : } else if (fun == "g" || fun == "r") {
1421 : try {
1422 2218 : int linkIndex = StringUtils::toInt(arg);
1423 2218 : if (linkIndex >= 0 && linkIndex < myNumLinks) {
1424 2218 : const std::vector<SUMOTime>& times = fun == "g" ? myLinkGreenTimes : myLinkRedTimes;
1425 2218 : if (times.empty()) {
1426 : return 0;
1427 : }
1428 2194 : if (myLastTrySwitchTime < SIMSTEP) {
1429 : // times are only updated at the start of a phase where
1430 : // switching is possible (i.e. not during minDur).
1431 : // If somebody is looking at those values in the tracker
1432 : // this would be confusing
1433 576 : const LinkState ls = getCurrentPhaseDef().getSignalState(linkIndex);
1434 288 : if ((fun == "g" && (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR))
1435 744 : || (fun == "r" && (ls == LINKSTATE_TL_RED || ls == LINKSTATE_TL_REDYELLOW))) {
1436 276 : const SUMOTime currentGreen = SIMSTEP - myLastTrySwitchTime;
1437 276 : return STEPS2TIME(times[linkIndex] + currentGreen);
1438 : } else {
1439 : return 0;
1440 : }
1441 : } else {
1442 1618 : return STEPS2TIME(times[linkIndex]);
1443 : }
1444 : }
1445 0 : } catch (NumberFormatException&) { }
1446 0 : throw ProcessError(TLF("Invalid link index '%' in expression '%'", arg, expr));
1447 9714 : } else if (fun == "p") {
1448 : try {
1449 386 : int linkIndex = StringUtils::toInt(arg);
1450 386 : if (linkIndex >= 0 && linkIndex < myNumLinks) {
1451 : double approachingPersons = 0;
1452 772 : for (const MSLink* link : getLinksAt(linkIndex)) {
1453 : auto* aPersons = link->getApproachingPersons();
1454 386 : if (aPersons != nullptr) {
1455 344 : approachingPersons += (double)aPersons->size();
1456 : }
1457 : }
1458 386 : return approachingPersons;
1459 : }
1460 0 : } catch (NumberFormatException&) { }
1461 0 : throw ProcessError(TLF("Invalid link index '%' in expression '%'", arg, expr));
1462 9328 : } else if (fun == "c") {
1463 72 : return STEPS2TIME(getTimeInCycle());
1464 : } else {
1465 9256 : if (myFunctions.find(fun) == myFunctions.end()) {
1466 0 : throw ProcessError(TLF("Unsupported function '%' in expression '%'", fun, expr));
1467 : }
1468 9256 : return evalCustomFunction(fun, arg);
1469 : }
1470 : }
1471 : }
1472 : }
1473 :
1474 :
1475 : std::map<std::string, double>
1476 4584 : MSActuatedTrafficLightLogic::getDetectorStates() const {
1477 : std::map<std::string, double> result;
1478 56696 : for (auto li : myInductLoops) {
1479 100304 : result[li.loop->getID()] = li.loop->getOccupancy() > 0 ? 1 : 0;
1480 : }
1481 6256 : for (auto loop : myExtraLoops) {
1482 3252 : result[loop->getID()] = loop->getOccupancy() > 0 ? 1 : 0;
1483 : }
1484 6256 : for (auto loop : myExtraE2) {
1485 1672 : result[loop->getID()] = loop->getCurrentVehicleNumber();
1486 : }
1487 4584 : return result;
1488 : }
1489 :
1490 : double
1491 0 : MSActuatedTrafficLightLogic::getDetectorState(const std::string laneID) const {
1492 : double result = 0.0;
1493 0 : for (auto li : myInductLoops) {
1494 0 : if (li.lane->getID() == laneID) {
1495 0 : result = li.loop->getOccupancy() > 0 ? 1 : 0;
1496 : break;
1497 : }
1498 : }
1499 0 : return result;
1500 : }
1501 :
1502 : std::map<std::string, double>
1503 14592 : MSActuatedTrafficLightLogic::getConditions() const {
1504 : std::map<std::string, double> result;
1505 80820 : for (auto item : myConditions) {
1506 : if (myListedConditions.count(item.first) != 0) {
1507 : try {
1508 56772 : result[item.first] = evalExpression(item.second);
1509 0 : } catch (ProcessError& e) {
1510 0 : WRITE_ERRORF(TL("Error when retrieving conditions '%' for tlLogic '%' (%)"), item.first, getID(), e.what());
1511 0 : }
1512 : }
1513 : }
1514 14592 : return result;
1515 : }
1516 :
1517 : const std::string
1518 22043 : MSActuatedTrafficLightLogic::getParameter(const std::string& key, const std::string defaultValue) const {
1519 44086 : if (StringUtils::startsWith(key, "condition.")) {
1520 2880 : const std::string cond = key.substr(10);
1521 : auto it = myConditions.find(cond);
1522 2880 : if (it != myConditions.end()) {
1523 2880 : return toString(evalExpression(it->second));
1524 : } else {
1525 0 : throw InvalidArgument(TLF("Unknown condition '%' for actuated traffic light '%'", cond, getID()));
1526 : }
1527 : } else {
1528 38326 : return MSSimpleTrafficLightLogic::getParameter(key, defaultValue);
1529 : }
1530 : }
1531 :
1532 : void
1533 24 : MSActuatedTrafficLightLogic::setParameter(const std::string& key, const std::string& value) {
1534 : // some pre-defined parameters can be updated at runtime
1535 24 : if (key == "detector-gap" || key == "passing-time" || key == "file" || key == "freq" || key == "vTypes"
1536 24 : || key == "build-all-detectors"
1537 48 : || StringUtils::startsWith(key, "linkMaxDur")
1538 72 : || StringUtils::startsWith(key, "linkMinDur")) {
1539 0 : throw InvalidArgument(key + " cannot be changed dynamically for actuated traffic light '" + getID() + "'");
1540 24 : } else if (key == "max-gap") {
1541 4 : myMaxGap = StringUtils::toDouble(value);
1542 : // overwrite custom values
1543 36 : for (InductLoopInfo& loopInfo : myInductLoops) {
1544 32 : loopInfo.maxGap = myMaxGap;
1545 : }
1546 4 : Parameterised::setParameter(key, value);
1547 40 : } else if (StringUtils::startsWith(key, "max-gap:")) {
1548 0 : const std::string laneID = key.substr(8);
1549 0 : for (InductLoopInfo& loopInfo : myInductLoops) {
1550 0 : if (loopInfo.lane->getID() == laneID) {
1551 0 : loopInfo.maxGap = StringUtils::toDouble(value);
1552 0 : Parameterised::setParameter(key, value);
1553 : return;
1554 : }
1555 : }
1556 0 : throw InvalidArgument(TLF("Invalid lane '%' in key '%' for actuated traffic light '%'", laneID, key, getID()));
1557 20 : } else if (key == "jam-threshold") {
1558 4 : myJamThreshold = StringUtils::toDouble(value);
1559 : // overwrite custom values
1560 20 : for (InductLoopInfo& loopInfo : myInductLoops) {
1561 16 : loopInfo.jamThreshold = myJamThreshold;
1562 : }
1563 4 : Parameterised::setParameter(key, value);
1564 32 : } else if (StringUtils::startsWith(key, "jam-threshold:")) {
1565 2 : const std::string laneID = key.substr(14);
1566 6 : for (InductLoopInfo& loopInfo : myInductLoops) {
1567 6 : if (loopInfo.lane->getID() == laneID) {
1568 2 : loopInfo.jamThreshold = StringUtils::toDouble(value);
1569 2 : Parameterised::setParameter(key, value);
1570 : return;
1571 : }
1572 : }
1573 0 : throw InvalidArgument(TLF("Invalid lane '%' in key '%' for actuated traffic light '%'", laneID, key, getID()));
1574 14 : } else if (key == "show-detectors") {
1575 2 : myShowDetectors = StringUtils::toBool(value);
1576 2 : Parameterised::setParameter(key, value);
1577 10 : for (InductLoopInfo& loopInfo : myInductLoops) {
1578 8 : loopInfo.loop->setVisible(myShowDetectors);
1579 : }
1580 12 : } else if (key == "inactive-threshold") {
1581 0 : myInactiveThreshold = string2time(value);
1582 0 : Parameterised::setParameter(key, value);
1583 : } else {
1584 12 : MSSimpleTrafficLightLogic::setParameter(key, value);
1585 : }
1586 : }
1587 :
1588 :
1589 : /****************************************************************************/
|