LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSActuatedTrafficLightLogic.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 89.6 % 730 654
Test Date: 2025-11-13 15:38:19 Functions: 95.2 % 42 40

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

Generated by: LCOV version 2.0-1