LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSActuatedTrafficLightLogic.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 595 665 89.5 %
Date: 2024-05-19 15:37:39 Functions: 37 38 97.4 %

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

Generated by: LCOV version 1.14