LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSActuatedTrafficLightLogic.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.2 % 752 678
Test Date: 2026-07-26 16:30:20 Functions: 95.5 % 44 42

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    MSActuatedTrafficLightLogic.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Julia Ringel
      17              : /// @author  Jakob Erdmann
      18              : /// @author  Michael Behrisch
      19              : /// @author  Laura Bieker
      20              : /// @date    Sept 2002
      21              : ///
      22              : // An actuated (adaptive) traffic light logic
      23              : /****************************************************************************/
      24              : #include <config.h>
      25              : 
      26              : #include <cassert>
      27              : #include <utility>
      28              : #include <vector>
      29              : #include <bitset>
      30              : #include <utils/common/FileHelpers.h>
      31              : #include <utils/common/StringUtils.h>
      32              : #include <utils/common/StringTokenizer.h>
      33              : #include <microsim/output/MSInductLoop.h>
      34              : #include <microsim/MSGlobals.h>
      35              : #include <microsim/MSNet.h>
      36              : #include <microsim/MSLane.h>
      37              : #include <microsim/MSEdge.h>
      38              : #include <microsim/MSJunctionLogic.h>
      39              : #include <netload/NLDetectorBuilder.h>
      40              : #include "MSActuatedTrafficLightLogic.h"
      41              : 
      42              : //#define DEBUG_DETECTORS
      43              : //#define DEBUG_PHASE_SELECTION
      44              : //#define DEBUG_PHASE_SELECTION_CUSTOM
      45              : #define DEBUG_COND (getID()=="C")
      46              : 
      47              : // ===========================================================================
      48              : // static members
      49              : // ===========================================================================
      50              : const std::vector<std::string> MSActuatedTrafficLightLogic::OPERATOR_PRECEDENCE({
      51              :     "**", "^", "*", "/", "+", "-", "%",
      52              :     "=", "==", "!=", "<", ">", "<=", ">=",
      53              :     "and", "&&", "or", "||",
      54              : });
      55              : 
      56              : // ===========================================================================
      57              : // parameter defaults definitions
      58              : // ===========================================================================
      59              : #define DEFAULT_MAX_GAP "3.0"
      60              : #define DEFAULT_PASSING_TIME "1.9"
      61              : #define DEFAULT_DETECTOR_GAP "2.0"
      62              : #define DEFAULT_INACTIVE_THRESHOLD "180"
      63              : #define DEFAULT_CURRENT_PRIORITY 10
      64              : #define DEFAULT_CROSSING_PRIORITY 100
      65              : 
      66              : #define DEFAULT_LENGTH_WITH_GAP 7.5
      67              : #define DEFAULT_BIKE_LENGTH_WITH_GAP (getDefaultVehicleLength(SVC_BICYCLE) + 0.5)
      68              : #define DEFAULT_STATIC_MINDUR TIME2STEPS(0) // loop position for non-stretchable phases
      69              : 
      70              : #define NO_DETECTOR "NO_DETECTOR"
      71              : #define DEFAULT_CONDITION "DEFAULT"
      72              : 
      73              : // ===========================================================================
      74              : // method definitions
      75              : // ===========================================================================
      76          905 : 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          905 :         const FunctionMap& functions) :
      86              :     MSSimpleTrafficLightLogic(tlcontrol, id, programID, offset, TrafficLightType::ACTUATED, phases, step, delay, parameter),
      87          905 :     myHasMultiTarget(false),
      88          905 :     myLastTrySwitchTime(0),
      89              :     myConditions(conditions),
      90          905 :     myAssignments(assignments),
      91              :     myFunctions(functions),
      92          905 :     myTraCISwitch(false),
      93         2715 :     myDetectorPrefix(id + "_" + programID + "_") {
      94         1810 :     myMaxGap = StringUtils::toDouble(getParameter("max-gap", DEFAULT_MAX_GAP));
      95         1810 :     myJamThreshold = StringUtils::toDouble(getParameter("jam-threshold", OptionsCont::getOptions().getValueString("tls.actuated.jam-threshold")));
      96         1810 :     myPassingTime = StringUtils::toDouble(getParameter("passing-time", DEFAULT_PASSING_TIME));
      97         1810 :     myDetectorGap = StringUtils::toDouble(getParameter("detector-gap", DEFAULT_DETECTOR_GAP));
      98         1810 :     myInactiveThreshold = string2time(getParameter("inactive-threshold", DEFAULT_INACTIVE_THRESHOLD));
      99         1810 :     myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
     100         1810 :     myBuildAllDetectors = StringUtils::toBool(getParameter("build-all-detectors", "false"));
     101         1810 :     myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
     102         2715 :     myFreq = TIME2STEPS(StringUtils::toDouble(getParameter("freq", "300")));
     103         1810 :     myVehicleTypes = getParameter("vTypes", "");
     104              : 
     105         1810 :     if (hasParameter("hide-conditions")) {
     106           16 :         std::vector<std::string> hidden = StringTokenizer(getParameter("hide-conditions", "")).getVector();
     107            8 :         std::set<std::string> hiddenSet(hidden.begin(), hidden.end());
     108           56 :         for (auto item : myConditions) {
     109              :             if (hiddenSet.count(item.first) == 0) {
     110              :                 myListedConditions.insert(item.first);
     111              :             }
     112              :         }
     113            8 :     } else {
     114         1794 :         const bool showAll = getParameter("show-conditions", "") == "";
     115         1794 :         std::vector<std::string> shown = StringTokenizer(getParameter("show-conditions", "")).getVector();
     116          897 :         std::set<std::string> shownSet(shown.begin(), shown.end());
     117         1089 :         for (auto item : myConditions) {
     118          192 :             if (showAll || shownSet.count(item.first) != 0) {
     119              :                 myListedConditions.insert(item.first);
     120              :             }
     121              :         }
     122          897 :     }
     123         1810 :     if (hasParameter("extra-detectors")) {
     124            8 :         const std::string extraIDs = getParameter("extra-detectors", "");
     125           14 :         for (std::string customID : StringTokenizer(extraIDs).getVector()) {
     126              :             try {
     127            6 :                 myExtraLoops.push_back(retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(customID, extraIDs, true));
     128            4 :             } catch (ProcessError&) {
     129            4 :                 myExtraE2.push_back(retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(customID, extraIDs, true));
     130            4 :             }
     131            4 :         }
     132              :     }
     133          905 :     myStack.push_back(std::map<std::string, double>());
     134          933 : }
     135              : 
     136              : 
     137         4500 : MSActuatedTrafficLightLogic::~MSActuatedTrafficLightLogic() { }
     138              : 
     139              : void
     140          903 : MSActuatedTrafficLightLogic::init(NLDetectorBuilder& nb) {
     141          903 :     MSTrafficLightLogic::init(nb);
     142          903 :     initAttributeOverride();
     143          903 :     initSwitchingRules();
     144         6956 :     for (int i = 0; i < (int)myPhases.size(); i++) {
     145         6053 :         initTargets(i);
     146              :     }
     147          903 :     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          903 :     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          903 :     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          903 :     int detEdgeIndex = -1;
     179          903 :     int detLaneIndex = 0;
     180         1806 :     const double detDefaultLength = StringUtils::toDouble(getParameter("detector-length",
     181         1806 :                                     OptionsCont::getOptions().getValueString("tls.actuated.detector-length")));
     182              :     MSEdge* prevDetEdge = nullptr;
     183        14160 :     for (LaneVector& lanes : myLanes) {
     184        26767 :         for (MSLane* lane : lanes) {
     185        13514 :             const std::string customID = getParameter(lane->getID());
     186        13510 :             if (noVehicles(lane->getPermissions()) && customID == "") {
     187              :                 // do not build detectors on green verges or sidewalks
     188          843 :                 continue;
     189              :             }
     190        12667 :             if (laneInductLoopMap.find(lane) != laneInductLoopMap.end()) {
     191              :                 // only build one detector per lane
     192         6307 :                 continue;
     193              :             }
     194         6360 :             const SUMOTime minDur = getMinimumMinDuration(lane, multiNextTargets);
     195         6360 :             if (minDur == std::numeric_limits<SUMOTime>::max() && customID == "" && !myBuildAllDetectors) {
     196              :                 // only build detector if this lane is relevant for an actuated phase
     197          254 :                 continue;
     198              :             }
     199         6106 :             double length = lane->getLength();
     200              :             double ilpos;
     201              :             double inductLoopPosition;
     202         6106 :             MSInductLoop* loop = nullptr;
     203         6106 :             if (&lane->getEdge() != prevDetEdge) {
     204         3512 :                 detEdgeIndex++;
     205         3512 :                 detLaneIndex = 0;
     206              :                 prevDetEdge = &lane->getEdge();
     207              :             } else {
     208         2594 :                 detLaneIndex++;
     209              :             }
     210         6106 :             const bool isBikeLane = (lane->getPermissions() & ~SVC_PEDESTRIAN) == SVC_BICYCLE;
     211         6106 :             const double defaultLength = isBikeLane ? DEFAULT_BIKE_LENGTH_WITH_GAP : DEFAULT_LENGTH_WITH_GAP;
     212         6106 :             if (customID == "") {
     213         5658 :                 const double speed = isBikeLane ? DEFAULT_BICYCLE_SPEED : lane->getSpeedLimit();
     214        11316 :                 inductLoopPosition = MIN2(
     215         5658 :                                          myDetectorGap * speed,
     216         5658 :                                          (STEPS2TIME(minDur) / myPassingTime + 0.5) * defaultLength);
     217              : 
     218              :                 // check whether the lane is long enough
     219         5658 :                 ilpos = length - inductLoopPosition;
     220         5658 :                 MSLane* placementLane = lane;
     221         7020 :                 while (ilpos < 0 && placementLane->getIncomingLanes().size() == 1
     222         7136 :                         && placementLane->getIncomingLanes().front().viaLink->getCorrespondingEntryLink()->getTLLogic() == nullptr) {
     223          653 :                     placementLane = placementLane->getLogicalPredecessorLane();
     224          653 :                     ilpos += placementLane->getLength();
     225              :                 }
     226         5658 :                 if (ilpos < 0) {
     227              :                     ilpos = 0;
     228              :                 }
     229              :                 // Build the induct loop and set it into the container
     230         5658 :                 const double detLength = getDouble("detector-length:" + lane->getID(), detDefaultLength);
     231        16974 :                 std::string id = myDetectorPrefix + "D" + toString(detEdgeIndex) + "." + toString(detLaneIndex);
     232         5658 :                 loop = static_cast<MSInductLoop*>(nb.createInductLoop(id, placementLane, ilpos, detLength, "", myVehicleTypes, "", (int)PersonMode::NONE, myShowDetectors, true));
     233         5658 :                 MSNet::getInstance()->getDetectorControl().add(SUMO_TAG_INDUCTION_LOOP, loop, myFile, myFreq);
     234          448 :             } else if (customID == NO_DETECTOR) {
     235          240 :                 continue;
     236              :             } else {
     237          416 :                 loop = dynamic_cast<MSInductLoop*>(MSNet::getInstance()->getDetectorControl().getTypedDetectors(SUMO_TAG_INDUCTION_LOOP).get(customID));
     238          208 :                 if (loop == nullptr) {
     239           12 :                     throw ProcessError(TLF("Unknown inductionLoop '%' given as custom detector for actuated tlLogic '%', program '%.", customID, getID(), getProgramID()));
     240              :                 }
     241              :                 ilpos = loop->getPosition();
     242          204 :                 inductLoopPosition = length - ilpos;
     243              :             }
     244         5862 :             const double maxGap = getDouble("max-gap:" + lane->getID(), myMaxGap);
     245         5862 :             const double jamThreshold = getDouble("jam-threshold:" + lane->getID(), myJamThreshold);
     246         5862 :             laneInductLoopMap[lane] = loop;
     247         5862 :             inductLoopInfoMap[loop] = (int)myInductLoops.size();
     248         5862 :             myInductLoops.push_back(InductLoopInfo(loop, lane, (int)myPhases.size(), maxGap, jamThreshold));
     249              : 
     250         5862 :             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            4 :                 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          899 :     std::vector<bool> neverMajor(numLinks, true);
     281         6920 :     for (const MSPhaseDefinition* phase : myPhases) {
     282              :         const std::string& state = phase->getState();
     283       103655 :         for (int i = 0; i < numLinks; i++)  {
     284        97634 :             if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
     285              :                 neverMajor[i] = false;
     286              :             }
     287              :         }
     288              :     }
     289          899 :     std::vector<bool> oneLane(numLinks, false);
     290          899 :     std::vector<bool> turnaround(numLinks, true);
     291        14132 :     for (int i = 0; i < numLinks; i++)  {
     292        21038 :         for (MSLane* lane : getLanesAt(i)) {
     293              :             // only count motorized vehicle lanes
     294              :             int numMotorized = 0;
     295        42501 :             for (MSLane* l : lane->getEdge().getLanes()) {
     296        29091 :                 if ((l->getPermissions() & motorized) != 0) {
     297        21872 :                     numMotorized++;
     298              :                 }
     299              :             }
     300        13410 :             if (numMotorized == 1) {
     301              :                 oneLane[i] = true;
     302         5605 :                 break;
     303              :             }
     304              :         }
     305        14888 :         for (MSLink* link : getLinksAt(i)) {
     306        12998 :             if (!link->isTurnaround()) {
     307              :                 turnaround[i] = false;
     308        11343 :                 break;
     309              :             }
     310              :         }
     311              :     }
     312              : 
     313         6920 :     for (const MSPhaseDefinition* phase : myPhases) {
     314         6021 :         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        43019 :             for (int i = 0; i < numLinks; i++)  {
     326        40477 :                 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
     327              :                     greenLinks.insert(i);
     328              :                     if (phase->isActuated()) {
     329              :                         actuatedLinks.insert(i);
     330              :                     }
     331              : 
     332        23332 :                     for (MSLink* link : getLinksAt(i)) {
     333        11791 :                         if (link->getLane()->isCrossing()) {
     334         2600 :                             while (myCrossingsForPhase.size() < myPhases.size()) {
     335         1470 :                                 myCrossingsForPhase.push_back(std::vector<const MSLink*>());
     336              :                             }
     337         1130 :                             myCrossingsForPhase[phaseIndex].push_back(link);
     338              :                         }
     339              :                     }
     340              : 
     341        28936 :                 } else if (state[i] == LINKSTATE_TL_GREEN_MINOR) {
     342         1496 :                     if (((neverMajor[i] || turnaround[i])  // check1a, 1d
     343         4592 :                             && hasMajor(state, getLanesAt(i))) // check1b
     344         1836 :                             || oneLane[i] // check1c
     345         7500 :                             || weakConflict(i, state)) { // check1e, check1f
     346              :                         greenLinks.insert(i);
     347         4150 :                         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       122345 :                 for (MSLane* lane : getLanesAt(i)) {
     369              :                     if (laneInductLoopMap.count(lane) != 0) {
     370        37247 :                         loopLinks[laneInductLoopMap[lane]].insert(i);
     371              :                     }
     372              :                 }
     373              :             }
     374        21937 :             for (auto& item : loopLinks) {
     375        19395 :                 MSInductLoop* loop = item.first;
     376        19395 :                 const InductLoopInfo& info = myInductLoops[inductLoopInfoMap[loop]];
     377        19395 :                 const MSLane* loopLane = info.lane;
     378              :                 bool usable = true;
     379              :                 bool foundUsable = false;
     380              :                 // check1
     381        55066 :                 for (int j : item.second) {
     382        22100 :                     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        19395 :                 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        19395 :                 if (usable && info.jamThreshold <= 0) {
     399        18910 :                     for (MSLink* link : loopLane->getLinkCont()) {
     400        12428 :                         if (link->isTurnaround()) {
     401         1755 :                             continue;
     402              :                         }
     403        10673 :                         const MSLane* next = link->getLane();
     404              :                         if (laneInductLoopMap.count(next) != 0) {
     405          778 :                             MSInductLoop* nextLoop = laneInductLoopMap[next];
     406         1593 :                             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         6990 :                 if (usable) {
     421         6959 :                     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        20045 :                     for (int j : item.second) {
     428        13086 :                         linkToLoops[j].insert(item.first);
     429              :                     }
     430              :                 }
     431              :             }
     432         2542 :             if (loops.size() == 0 && phase->isActuated()) {
     433           72 :                 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         6021 :         myInductLoopsForPhase.push_back(loopInfos);
     449        12980 :         for (MSInductLoop* loop : loops) {
     450        72394 :             for (InductLoopInfo& loopInfo : myInductLoops) {
     451        65435 :                 if (loopInfo.loop == loop) {
     452         6959 :                     myInductLoopsForPhase.back().push_back(&loopInfo);
     453         6959 :                     loopInfo.servedPhase[phaseIndex] = true;
     454              :                 }
     455              :             }
     456              :         }
     457         6021 :     }
     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        12102 :     for (int i : actuatedLinks) {
     468        12474 :         if (linkToLoops[i].size() == 0 && myLinks[i].size() > 0
     469        12353 :                 && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
     470          694 :             if (getParameter(myLinks[i].front()->getLaneBefore()->getID()) != NO_DETECTOR) {
     471          107 :                 warnLinks.push_back(i);
     472              :             }
     473              :         }
     474              :     }
     475          899 :     if (warnLinks.size() > 0) {
     476          114 :         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         2161 :     for (const auto& kv : getParametersMap()) {
     480         2524 :         if (StringUtils::startsWith(kv.first, "linkMaxDur:")) {
     481            4 :             int link = StringUtils::toInt(kv.first.substr(11));
     482            4 :             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            4 :             if (myLinkMaxGreenTimes.empty()) {
     487            4 :                 myLinkMaxGreenTimes = std::vector<SUMOTime>(myNumLinks, std::numeric_limits<SUMOTime>::max());
     488              :             }
     489            4 :             myLinkMaxGreenTimes[link] = string2time(kv.second);
     490         2516 :         } else if (StringUtils::startsWith(kv.first, "linkMinDur:")) {
     491           36 :             int link = StringUtils::toInt(kv.first.substr(11));
     492           36 :             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           36 :             if (myLinkMinGreenTimes.empty()) {
     497            8 :                 myLinkMinGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
     498              :             }
     499           36 :             myLinkMinGreenTimes[link] = string2time(kv.second);
     500              :         }
     501              :     }
     502          899 :     bool haveSwitchingRules = myConditions.size() > 0 || myAssignments.size() > 0 || myFunctions.size() > 0;
     503         6524 :     for (auto sr : mySwitchingRules) {
     504         5696 :         if (sr.enabled) {
     505              :             haveSwitchingRules = true;
     506              :             break;
     507              :         }
     508              :     }
     509          899 :     if (myLinkMaxGreenTimes.size() > 0 || myLinkMinGreenTimes.size() > 0 || haveSwitchingRules) {
     510           85 :         myLinkGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
     511           85 :         myLinkRedTimes = std::vector<SUMOTime>(myNumLinks, 0);
     512              :     }
     513              :     //std::cout << SIMTIME << " linkMaxGreenTimes=" << toString(myLinkMaxGreenTimes) << "\n";
     514         1798 : }
     515              : 
     516              : 
     517              : bool
     518         1792 : MSActuatedTrafficLightLogic::weakConflict(int tlIndex, const std::string& state) const {
     519         1958 :     for (MSLink* link : getLinksAt(tlIndex)) {
     520              :         int linkIndex = link->getIndex();
     521              :         const MSJunction* junction = link->getJunction();
     522        19044 :         for (int i = 0; i < (int)myLinks.size(); i++) {
     523        18878 :             if (i == tlIndex) {
     524          947 :                 continue;
     525              :             }
     526        17931 :             if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
     527        15652 :                 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         8292 :                     if (junction == junction2) {
     532         6664 :                         const MSJunctionLogic* logic = junction->getLogic();
     533              :                         //std::cout << " greenLink=" << i << " isFoe=" << logic->getFoesFor(linkIndex).test(foeIndex) << "\n";
     534         6664 :                         if (logic->getFoesFor(linkIndex).test(foeIndex)
     535         1968 :                                 && (foe->getPermissions() & ~SVC_VULNERABLE) != 0 // check1e
     536         8267 :                                 && &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       592599 : MSActuatedTrafficLightLogic::getMinDur(int step) const {
     553       592599 :     step = step < 0 ? myStep : step;
     554       592599 :     const MSPhaseDefinition* p = myPhases[step];
     555       592599 :     return p->minDuration != MSPhaseDefinition::OVERRIDE_DURATION
     556       592599 :            ? p->minDuration
     557       613143 :            : TIME2STEPS(evalExpression(myConditions.find("minDur:" + toString(step))->second));
     558              : }
     559              : 
     560              : SUMOTime
     561       327636 : MSActuatedTrafficLightLogic::getMaxDur(int step) const {
     562       327636 :     step = step < 0 ? myStep : step;
     563       327636 :     const MSPhaseDefinition* p = myPhases[step];
     564       327636 :     return p->maxDuration != MSPhaseDefinition::OVERRIDE_DURATION
     565       327636 :            ? p->maxDuration
     566       347892 :            : TIME2STEPS(evalExpression(myConditions.find("maxDur:" + toString(step))->second));
     567              : }
     568              : 
     569              : SUMOTime
     570       452302 : MSActuatedTrafficLightLogic::getEarliestEnd(int step) const {
     571       452302 :     step = step < 0 ? myStep : step;
     572       452302 :     const MSPhaseDefinition* p = myPhases[step];
     573       452302 :     return p->earliestEnd != MSPhaseDefinition::OVERRIDE_DURATION
     574       452302 :            ? p->earliestEnd
     575       472558 :            : TIME2STEPS(evalExpression(myConditions.find("earliestEnd:" + toString(step))->second));
     576              : }
     577              : 
     578              : SUMOTime
     579       730091 : MSActuatedTrafficLightLogic::getLatestEnd(int step) const {
     580       730091 :     step = step < 0 ? myStep : step;
     581       730091 :     const MSPhaseDefinition* p = myPhases[step];
     582       730091 :     return p->latestEnd != MSPhaseDefinition::OVERRIDE_DURATION
     583       730091 :            ? p->latestEnd
     584       750347 :            : TIME2STEPS(evalExpression(myConditions.find("latestEnd:" + toString(step))->second));
     585              : }
     586              : 
     587              : 
     588              : void
     589          903 : MSActuatedTrafficLightLogic::initAttributeOverride() {
     590              :     const SUMOTime ovrd = MSPhaseDefinition::OVERRIDE_DURATION;
     591         6956 :     for (int i = 0; i < (int)myPhases.size(); i++) {
     592         6053 :         MSPhaseDefinition* phase = myPhases[i];
     593         6053 :         const std::string errorSuffix = "' for overriding attribute in phase " + toString(i) + " of tlLogic '" + getID() + "' in program '" + getProgramID() + "'.";
     594         6053 :         if (phase->minDuration == ovrd) {
     595           16 :             const std::string cond = "minDur:" + toString(i);
     596              :             if (myConditions.count(cond) == 0) {
     597            0 :                 throw ProcessError("Missing condition '" + cond + errorSuffix);
     598              :             }
     599              :         }
     600         6053 :         if (phase->maxDuration == ovrd) {
     601           16 :             const std::string cond = "maxDur:" + toString(i);
     602              :             if (myConditions.count(cond) == 0) {
     603            0 :                 throw ProcessError("Missing condition '" + cond + errorSuffix);
     604              :             }
     605              :         }
     606         6053 :         if (phase->earliestEnd == ovrd) {
     607           16 :             const std::string cond = "earliestEnd:" + toString(i);
     608              :             if (myConditions.count(cond) == 0) {
     609            0 :                 throw ProcessError("Missing condition '" + cond + errorSuffix);
     610              :             }
     611              :         }
     612         6053 :         if (phase->latestEnd == ovrd) {
     613           16 :             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          903 : }
     620              : 
     621              : 
     622              : void
     623          903 : MSActuatedTrafficLightLogic::initSwitchingRules() {
     624         6956 :     for (int i = 0; i < (int)myPhases.size(); i++) {
     625         6053 :         SwitchingRules sr;
     626         6053 :         MSPhaseDefinition* phase = myPhases[i];
     627         6053 :         std::vector<int> nextPhases = phase->nextPhases;
     628         6053 :         if (nextPhases.size() == 0) {
     629         5413 :             nextPhases.push_back((i + 1) % (int)myPhases.size());
     630          640 :         } else if (nextPhases.size() > 1) {
     631          204 :             myHasMultiTarget = true;
     632              :         }
     633        12458 :         for (int next : nextPhases) {
     634         6405 :             if (next >= 0 && next < (int)myPhases.size()) {
     635         6405 :                 const MSPhaseDefinition* nextPhase = myPhases[next];
     636         6405 :                 if (nextPhase->earlyTarget != "" || nextPhase->finalTarget != "") {
     637          182 :                     sr.enabled = true;
     638              :                 }
     639              :             }
     640              :         }
     641              :         // simplifies later code
     642         6053 :         phase->nextPhases = nextPhases;
     643         6053 :         mySwitchingRules.push_back(sr);
     644         6053 :     }
     645          903 : }
     646              : 
     647              : 
     648              : void
     649         6053 : MSActuatedTrafficLightLogic::initTargets(int step) {
     650              :     // next -> target -> transitionTime starting from step
     651              :     std::map<int, std::map<int, SUMOTime> > reached;
     652         6053 :     const std::vector<int>& next = myPhases[step]->nextPhases;
     653        12458 :     for (int n : next) {
     654         6405 :         findTargets(step, n, 0, reached[n]);
     655              :     }
     656        63478 :     for (int target = 0; target < (int)myPhases.size(); target++) {
     657        57425 :         int bestNext = next[0];
     658              :         SUMOTime bestTime = SUMOTime_MAX;
     659       123748 :         for (auto item : reached) {
     660              :             auto it = item.second.find(target);
     661        66323 :             if (it != item.second.end()) {
     662        24014 :                 SUMOTime transitionTime = it->second;
     663        24014 :                 if (transitionTime < bestTime) {
     664              :                     bestTime = transitionTime;
     665        23028 :                     bestNext = item.first;
     666              :                 }
     667              :             }
     668              :         }
     669        57425 :         if (bestTime != SUMOTime_MAX) {
     670        22688 :             myTargets[step][bestNext].push_back(target);
     671              :             //std::cout << " myTargets step=" << step << " bestNext=" << bestNext << " target=" << target << "\n";
     672              :         }
     673              :     }
     674         6053 : }
     675              : 
     676              : 
     677              : void
     678        65459 : MSActuatedTrafficLightLogic::findTargets(int origStep, int n, SUMOTime priorTransition, std::map<int, SUMOTime>& found) {
     679        65459 :     std::pair<int, SUMOTime> tDur = getTarget(n);
     680        65459 :     int target = tDur.first;
     681        65459 :     SUMOTime transitionTime = tDur.second + priorTransition;
     682              :     //std::cout << "   findTargets origStep=" << origStep << " n=" << n << " ptt=" << priorTransition << " target=" << target << " tt=" << transitionTime << "\n";
     683        65459 :     if (target == origStep) {
     684              :         // full circle
     685              :         //std::cout << "     foundCircle\n";
     686        37061 :         return;
     687              :     }
     688              :     auto it = found.find(target);
     689        60118 :     if (it != found.end()) {
     690        36104 :         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        28398 :     found[target] = transitionTime;
     701              :     //std::cout << "    targetNext=" << toString(myPhases[target]->nextPhases) << "\n";
     702        87452 :     for (int n2 : myPhases[target]->nextPhases) {
     703        59054 :         findTargets(origStep, n2, transitionTime, found);
     704              :     }
     705              : }
     706              : 
     707              : 
     708              : std::set<int>
     709          903 : MSActuatedTrafficLightLogic::getMultiNextTargets() const {
     710              :     std::set<int> result;
     711          903 :     if (myHasMultiTarget) {
     712              :         // find all phase that are the target green phase of a 'next' attribute
     713         1176 :         for (const MSPhaseDefinition* p : myPhases) {
     714         2468 :             for (int next : p->nextPhases) {
     715         2820 :                 result.insert(getTarget(next).first);
     716              :             }
     717              :         }
     718              :     }
     719          903 :     return result;
     720              : }
     721              : 
     722              : 
     723              : SUMOTime
     724         6360 : MSActuatedTrafficLightLogic::getMinimumMinDuration(MSLane* lane, const std::set<int>& multiNextTargets) const {
     725              :     SUMOTime result = std::numeric_limits<SUMOTime>::max();
     726        56207 :     for (int pI = 0; pI < (int)myPhases.size(); pI++) {
     727        49847 :         const MSPhaseDefinition* phase = myPhases[pI];
     728              :         const std::string& state = phase->getState();
     729      1011453 :         for (int i = 0; i < (int)state.size(); i++)  {
     730       961606 :             if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
     731       497769 :                 for (MSLane* cand : getLanesAt(i)) {
     732       247614 :                     if (lane == cand) {
     733              :                         if (phase->isActuated()) {
     734        17187 :                             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         6360 :     return result;
     744              : }
     745              : 
     746              : bool
     747         4592 : MSActuatedTrafficLightLogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
     748        53247 :     for (int i = 0; i < (int)state.size(); i++) {
     749        52527 :         if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
     750        26085 :             for (MSLane* cand : getLanesAt(i)) {
     751        23412 :                 for (MSLane* lane : lanes) {
     752        13180 :                     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          903 : MSActuatedTrafficLightLogic::activateProgram() {
     766          903 :     MSTrafficLightLogic::activateProgram();
     767          903 :     for (InductLoopInfo& loopInfo : myInductLoops) {
     768            0 :         loopInfo.loop->setVisible(myShowDetectors);
     769              :     }
     770          903 : }
     771              : 
     772              : 
     773              : void
     774           49 : MSActuatedTrafficLightLogic::deactivateProgram() {
     775           49 :     MSTrafficLightLogic::deactivateProgram();
     776           89 :     for (InductLoopInfo& loopInfo : myInductLoops) {
     777           40 :         loopInfo.loop->setVisible(false);
     778              :     }
     779           49 : }
     780              : 
     781              : void
     782           41 : MSActuatedTrafficLightLogic::changeStepAndDuration(MSTLLogicControl& tlcontrol,
     783              :         SUMOTime simStep, int step, SUMOTime stepDuration) {
     784              :     // do not change timing if the phase changes
     785           41 :     if (step >= 0 && step != myStep) {
     786            5 :         myStep = step;
     787            5 :         myPhases[myStep]->myLastSwitch = MSNet::getInstance()->getCurrentTimeStep();
     788            5 :         setTrafficLightSignals(simStep);
     789            5 :         tlcontrol.get(getID()).executeOnSwitchActions();
     790              :     } else if (step < 0) {
     791              :         // TraCI requested new timing
     792           15 :         mySwitchCommand->deschedule(this);
     793           15 :         mySwitchCommand = new SwitchCommand(tlcontrol, this, stepDuration + simStep);
     794           15 :         MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(
     795           15 :             mySwitchCommand, stepDuration + simStep);
     796           15 :         myTraCISwitch = true;
     797              :     }
     798           41 : }
     799              : 
     800              : 
     801              : void
     802            2 : MSActuatedTrafficLightLogic::loadState(MSTLLogicControl& tlcontrol, SUMOTime t, int step, SUMOTime spentDuration, SUMOTime nextSwitch, SUMOTime /*timeInCycle*/, bool active) {
     803            2 :     myAmActive = active;
     804            2 :     const SUMOTime lastSwitch = t - spentDuration;
     805            2 :     myStep = step;
     806            2 :     myPhases[myStep]->myLastSwitch = lastSwitch;
     807            2 :     mySwitchCommand->deschedule(this);
     808            2 :     mySwitchCommand = new SwitchCommand(tlcontrol, this, nextSwitch);
     809            2 :     MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(mySwitchCommand, nextSwitch);
     810            2 :     if (myAmActive) {
     811            2 :         setTrafficLightSignals(lastSwitch);
     812              :     }
     813            2 :     tlcontrol.get(getID()).executeOnSwitchActions();
     814            2 : }
     815              : 
     816              : 
     817              : SUMOTime
     818       569748 : MSActuatedTrafficLightLogic::trySwitch() {
     819              :     // checks if the actual phase should be continued
     820              :     // @note any vehicles which arrived during the previous phases which are now waiting between the detector and the stop line are not
     821              :     // considere here. RiLSA recommends to set minDuration in a way that lets all vehicles pass the detector
     822       569748 :     SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
     823      1139496 :     executeAssignments(myAssignments, myConditions);
     824              : 
     825       569748 :     if (myLinkGreenTimes.size() > 0) {
     826              :         // constraints exist, record green time durations for each link
     827        45959 :         const std::string& state = getCurrentPhaseDef().getState();
     828        45959 :         SUMOTime lastDuration = SIMSTEP - myLastTrySwitchTime;
     829       426879 :         for (int i = 0; i < myNumLinks; i++) {
     830       380920 :             if (state[i] == 'G' || state[i] == 'g') {
     831       166752 :                 myLinkGreenTimes[i] += lastDuration;
     832              :             } else {
     833       214168 :                 myLinkGreenTimes[i] = 0;
     834              :             }
     835       380920 :             if (state[i] == 'r' || state[i] == 'u') {
     836       205824 :                 myLinkRedTimes[i] += lastDuration;
     837              :             } else {
     838       175096 :                 myLinkRedTimes[i] = 0;
     839              :             }
     840              :         }
     841              :     }
     842       569748 :     myLastTrySwitchTime = now;
     843              :     // decide the next phase
     844       569748 :     const bool multiTarget = myPhases[myStep]->nextPhases.size() > 1 && myPhases[myStep]->nextPhases.front() >= 0;
     845              :     const int origStep = myStep;
     846              :     int nextStep = myStep;
     847       569748 :     SUMOTime actDuration = now - myPhases[myStep]->myLastSwitch;
     848              : 
     849       569748 :     if (mySwitchingRules[myStep].enabled) {
     850        37741 :         const bool mustSwitch = MIN2(getMaxDur() - actDuration, getLatest()) <= 0;
     851        37741 :         nextStep = decideNextPhaseCustom(mustSwitch);
     852              :     } else {
     853              :         // default algorithm
     854       532007 :         const double detectionGap = gapControl();
     855              : #ifdef DEBUG_PHASE_SELECTION
     856              :         if (DEBUG_COND) {
     857              :             std::cout << SIMTIME << " p=" << myStep
     858              :                       << " trySwitch dGap=" << (detectionGap == std::numeric_limits<double>::max() ? "inf" : toString(detectionGap))
     859              :                       << " multi=" << multiTarget << "\n";
     860              :         }
     861              : #endif
     862       532007 :         if (detectionGap < std::numeric_limits<double>::max() && !multiTarget && !myTraCISwitch) {
     863       281882 :             return duration(detectionGap);
     864              :         }
     865       237892 :         if (multiTarget) {
     866        84805 :             nextStep = decideNextPhase();
     867              :         } else {
     868       165320 :             if (myPhases[myStep]->nextPhases.size() == 1 && myPhases[myStep]->nextPhases.front() >= 0) {
     869              :                 nextStep = myPhases[myStep]->nextPhases.front();
     870              :             } else {
     871            0 :                 nextStep = (myStep + 1) % (int)myPhases.size();
     872              :             }
     873              :         }
     874              :     }
     875              : 
     876       287866 :     myTraCISwitch = false;
     877       287866 :     if (myLinkMinGreenTimes.size() > 0) {
     878         1310 :         SUMOTime linkMinDur = getLinkMinDuration(getTarget(nextStep).first);
     879         1310 :         if (linkMinDur > 0) {
     880              :             // for multiTarget, the current phase must be extended but if another
     881              :             // targer is chosen, earlier switching than linkMinDur is possible
     882          368 :             return multiTarget ? TIME2STEPS(1) : linkMinDur;
     883              :         }
     884              :     }
     885       287612 :     myStep = nextStep;
     886              :     assert(myStep <= (int)myPhases.size());
     887              :     assert(myStep >= 0);
     888              :     //stores the time the phase started
     889       287612 :     const SUMOTime prevStart = myPhases[myStep]->myLastSwitch;
     890       287612 :     if (myStep != origStep) {
     891       163291 :         myPhases[origStep]->myLastEnd = now;
     892       163291 :         myPhases[myStep]->myLastSwitch = now;
     893              :         actDuration = 0;
     894              :     }
     895              :     // activate coloring
     896       287612 :     if ((myShowDetectors || myHasMultiTarget) && getCurrentPhaseDef().isGreenPhase()) {
     897       388026 :         for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
     898              :             //std::cout << SIMTIME << " p=" << myStep << " loopinfo=" << loopInfo->loop->getID() << " set lastGreen=" << STEPS2TIME(now) << "\n";
     899              :             if (loopInfo->isJammed()) {
     900           74 :                 loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
     901              :             } else {
     902       272450 :                 loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
     903              :             }
     904       272524 :             loopInfo->lastGreenTime = now;
     905              :         }
     906              :     }
     907              :     // set the next event
     908              : #ifdef DEBUG_PHASE_SELECTION
     909              :     if (DEBUG_COND) {
     910              :         std::cout << SIMTIME << " tl=" << getID() << " p=" << myStep
     911              :                   << " nextTryMinDur=" << STEPS2TIME(getMinDur() - actDuration)
     912              :                   << " nextTryEarliest=" << STEPS2TIME(getEarliest(prevStart)) << "\n";
     913              :     }
     914              : #endif
     915       287612 :     SUMOTime minRetry = myStep != origStep ? 0 : TIME2STEPS(1);
     916       287612 :     return MAX3(minRetry, getMinDur() - actDuration, getEarliest(prevStart));
     917              : }
     918              : 
     919              : 
     920              : // ------------ "actuated" algorithm methods
     921              : SUMOTime
     922       281882 : MSActuatedTrafficLightLogic::duration(const double detectionGap) const {
     923              :     assert(getCurrentPhaseDef().isGreenPhase());
     924              :     assert((int)myPhases.size() > myStep);
     925       281882 :     const SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
     926              :     // ensure that minimum duration is kept
     927       281882 :     SUMOTime newDuration = getMinDur() - actDuration;
     928              :     // try to let the last detected vehicle pass the intersection (duration must be positive)
     929       330303 :     newDuration = MAX3(newDuration, TIME2STEPS(myDetectorGap - detectionGap), SUMOTime(1));
     930              :     // cut the decimal places to ensure that phases always have integer duration
     931       281882 :     if (newDuration % 1000 != 0) {
     932       235875 :         const SUMOTime totalDur = newDuration + actDuration;
     933       235875 :         newDuration = (totalDur / 1000 + 1) * 1000 - actDuration;
     934              :     }
     935              :     // ensure that the maximum duration is not exceeded
     936       281882 :     newDuration = MIN3(newDuration, getMaxDur() - actDuration, getLatest());
     937       281882 :     return newDuration;
     938              : }
     939              : 
     940              : 
     941              : double
     942       534703 : MSActuatedTrafficLightLogic::gapControl() {
     943              :     //intergreen times should not be lengthend
     944              :     assert((int)myPhases.size() > myStep);
     945              :     double result = std::numeric_limits<double>::max();
     946              :     // switch off active colors
     947       534703 :     if (myShowDetectors) {
     948       300558 :         for (InductLoopInfo& loopInfo : myInductLoops) {
     949       265162 :             if (loopInfo.lastGreenTime < loopInfo.loop->getLastDetectionTime()) {
     950        63472 :                 loopInfo.loop->setSpecialColor(&RGBColor::RED);
     951              :             } else {
     952       201690 :                 loopInfo.loop->setSpecialColor(nullptr);
     953              :             }
     954              :         }
     955              :     }
     956       534703 :     if (!getCurrentPhaseDef().isGreenPhase()) {
     957              :         return result; // end current phase
     958              :     }
     959              : 
     960              :     // Checks, if the maxDuration is kept. No phase should last longer than maxDuration.
     961       457901 :     SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
     962       457901 :     if (actDuration >= getCurrentPhaseDef().maxDuration || maxLinkDurationReached() || getLatest() == 0) {
     963              : #ifdef DEBUG_PHASE_SELECTION
     964              :         if (DEBUG_COND) {
     965              :             std::cout << SIMTIME << " actDuration=" << STEPS2TIME(actDuration) << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
     966              :                       << " maxLinkDurationReached=" << maxLinkDurationReached() << " latest=" << STEPS2TIME(getLatest()) << "\n";
     967              :         }
     968              : #endif
     969        95590 :         return result; // end current phase
     970              :     }
     971              : 
     972              :     // now the gapcontrol starts
     973      1237612 :     for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
     974       875301 :         MSInductLoop* loop = loopInfo->loop;
     975              :         if (loopInfo->isJammed()) {
     976          293 :             loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
     977              :         } else {
     978       875008 :             loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
     979              :         }
     980       875301 :         const double actualGap = loop->getTimeSinceLastDetection();
     981       875301 :         if (actualGap < loopInfo->maxGap && !loopInfo->isJammed()) {
     982              :             result = MIN2(result, actualGap);
     983              :         }
     984              :     }
     985              :     return result;
     986              : }
     987              : 
     988              : int
     989        84805 : MSActuatedTrafficLightLogic::decideNextPhase() {
     990        84805 :     const auto& cands = myPhases[myStep]->nextPhases;
     991              :     // decide by priority
     992              :     // first target is the default when there is no traffic
     993              :     // @note: to keep the current phase, even when there is no traffic, it must be added to 'next' explicitly
     994        84805 :     int result = cands.front();
     995              :     int maxPrio = 0;
     996        84805 :     SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
     997        84805 :     const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && !maxLinkDurationReached() && getLatest() > 0;
     998              :     if (canExtend) {
     999              :         // consider keeping the current phase until maxDur is reached
    1000              :         // (only when there is still traffic in that phase)
    1001        17619 :         int currentPrio = getPhasePriority(myStep);
    1002              : #ifdef DEBUG_PHASE_SELECTION
    1003              :         std::cout << SIMTIME << " p=" << myStep << " loops=" << myInductLoopsForPhase[myStep].size() << " currentPrio=" << currentPrio << "\n";
    1004              : #endif
    1005        17619 :         if (currentPrio > maxPrio) {
    1006        12398 :             result = myStep;
    1007              :             maxPrio = currentPrio;
    1008              :         }
    1009              :     }
    1010       260419 :     for (int step : cands) {
    1011              :         int prio = 0;
    1012       291335 :         for (int target : myTargets[myStep][step]) {
    1013       115721 :             prio += getPhasePriority(target);
    1014              : #ifdef DEBUG_PHASE_SELECTION
    1015              :             if (DEBUG_COND) {
    1016              :                 std::cout << SIMTIME << " p=" << myStep << " step=" << step << " target=" << target << " loops=" << myInductLoopsForPhase[target].size() << " prio=" << prio << "\n";
    1017              :             }
    1018              : #endif
    1019              :         }
    1020       175614 :         if (prio > maxPrio && canExtendLinkGreen(getTarget(step).first)) {
    1021              :             maxPrio = prio;
    1022          857 :             result = step;
    1023              :         }
    1024              :     }
    1025        84805 :     return result;
    1026              : }
    1027              : 
    1028              : 
    1029              : std::pair<int, SUMOTime>
    1030        69036 : MSActuatedTrafficLightLogic::getTarget(int step) const {
    1031              :     int seen = 0;
    1032        69036 :     int origStep = step;
    1033              :     SUMOTime dur = 0;
    1034              : 
    1035              :     // if step is a transition, find the upcoming green phase
    1036       130955 :     while (!myPhases[step]->isGreenPhase()) {
    1037        61919 :         seen += 1;
    1038        61919 :         dur += myPhases[step]->duration;
    1039        61919 :         if (myPhases[step]->nextPhases.size() > 0 && myPhases[step]->nextPhases.front() >= 0) {
    1040        61959 :             for (int next : myPhases[step]->nextPhases) {
    1041        61959 :                 if (next != step) {
    1042              :                     step = next;
    1043              :                     break;
    1044              :                 }
    1045              :             }
    1046              :         } else {
    1047            0 :             step = (step + 1) % (int)myPhases.size();
    1048              :         }
    1049        61919 :         if (step == origStep || seen > (int)myPhases.size()) {
    1050            0 :             WRITE_WARNING("At actuated tlLogic '" + getID() + "', infinite transition loop from phase " + toString(origStep));
    1051            0 :             return std::make_pair(0, 0);
    1052              :         }
    1053              :     }
    1054              :     return std::make_pair(step, dur);
    1055              : }
    1056              : 
    1057              : int
    1058       163977 : MSActuatedTrafficLightLogic::getDetectorPriority(const InductLoopInfo& loopInfo) const {
    1059       163977 :     MSInductLoop* loop = loopInfo.loop;
    1060       163977 :     const double actualGap = loop->getTimeSinceLastDetection();
    1061       163977 :     if ((actualGap < loopInfo.maxGap && !loopInfo.isJammed())
    1062       163977 :             || loopInfo.lastGreenTime < loop->getLastDetectionTime()) {
    1063        33330 :         SUMOTime inactiveTime = MSNet::getInstance()->getCurrentTimeStep() - loopInfo.lastGreenTime;
    1064              :         // @note. Inactive time could also be tracked regardless of current activity (to increase robustness in case of detection failure
    1065        33330 :         if (inactiveTime > myInactiveThreshold) {
    1066              : #ifdef DEBUG_PHASE_SELECTION
    1067              :             if (DEBUG_COND) {
    1068              :                 std::cout << "    loop=" << loop->getID() << " gap=" << loop->getTimeSinceLastDetection() << " lastGreen=" << STEPS2TIME(loopInfo.lastGreenTime)
    1069              :                           << " lastDetection=" << STEPS2TIME(loop->getLastDetectionTime()) << " inactive=" << STEPS2TIME(inactiveTime) << "\n";
    1070              :             }
    1071              : #endif
    1072           46 :             return (int)STEPS2TIME(inactiveTime);
    1073              :         } else {
    1074              :             // give bonus to detectors that are currently served (if that phase can stil be extended)
    1075        33284 :             if (loopInfo.servedPhase[myStep]) {
    1076        19550 :                 SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
    1077        19550 :                 const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && getLatest() > 0;
    1078              : #ifdef DEBUG_PHASE_SELECTION
    1079              :                 if (DEBUG_COND) {
    1080              :                     std::cout << "    loop=" << loop->getID()
    1081              :                               << " actDuration=" << STEPS2TIME(actDuration)
    1082              :                               << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
    1083              :                               << " getLatest=" << STEPS2TIME(getLatest())
    1084              :                               << " canExtend=" << canExtend
    1085              :                               << "\n";
    1086              :                 }
    1087              : #endif
    1088              :                 if (canExtend) {
    1089              :                     return DEFAULT_CURRENT_PRIORITY;
    1090              :                 } else {
    1091            8 :                     return 0;
    1092              :                 }
    1093              :             }
    1094              :             return 1;
    1095              :         }
    1096              :     }
    1097              :     return 0;
    1098              : }
    1099              : 
    1100              : int
    1101       133340 : MSActuatedTrafficLightLogic::getPhasePriority(int step) const {
    1102              :     int result = 0;
    1103       297317 :     for (const InductLoopInfo* loopInfo : myInductLoopsForPhase[step]) {
    1104       163977 :         result += getDetectorPriority(*loopInfo);
    1105              :     }
    1106       133340 :     if (myCrossingsForPhase.size() > 0) {
    1107       175471 :         for (const MSLink* crossingEntry : myCrossingsForPhase[step]) {
    1108              :             auto* aPersons = crossingEntry->getApproachingPersons();
    1109        87233 :             if (aPersons != nullptr && aPersons->size() > 0) {
    1110           73 :                 result += DEFAULT_CROSSING_PRIORITY;
    1111              :             }
    1112              :         }
    1113              :     }
    1114       133340 :     return result;
    1115              : }
    1116              : 
    1117              : 
    1118              : void
    1119            0 : MSActuatedTrafficLightLogic::setShowDetectors(bool show) {
    1120            0 :     myShowDetectors = show;
    1121            0 :     for (InductLoopInfo& loopInfo : myInductLoops) {
    1122            0 :         loopInfo.loop->setVisible(myShowDetectors);
    1123              :     }
    1124            0 : }
    1125              : 
    1126              : 
    1127              : bool
    1128       383527 : MSActuatedTrafficLightLogic::maxLinkDurationReached() {
    1129       383527 :     if (myLinkMaxGreenTimes.empty()) {
    1130              :         return false;
    1131              :     }
    1132         3608 :     for (int i = 0; i < myNumLinks; i++) {
    1133         3332 :         if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i]) {
    1134              :             //std::cout << SIMTIME << " maxLinkDurationReached i=" << i << "\n";
    1135              :             return true;
    1136              :         }
    1137              :     }
    1138              :     return false;
    1139              : }
    1140              : 
    1141              : bool
    1142          857 : MSActuatedTrafficLightLogic::canExtendLinkGreen(int target) {
    1143          857 :     if (myLinkMaxGreenTimes.empty()) {
    1144              :         return true;
    1145              :     }
    1146           16 :     const std::string& targetState = myPhases[target]->getState();
    1147          208 :     for (int i = 0; i < myNumLinks; i++) {
    1148          192 :         if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i] && (
    1149            0 :                     targetState[i] == 'G' || targetState[i] == 'g')) {
    1150              :             //std::cout << SIMTIME << " cannotExtendLinkGreen target=" << target << " i=" << i << "\n";
    1151              :             return false;
    1152              :         }
    1153              :     }
    1154              :     return true;
    1155              : }
    1156              : 
    1157              : SUMOTime
    1158         1310 : MSActuatedTrafficLightLogic::getLinkMinDuration(int target) const {
    1159              :     SUMOTime result = 0;
    1160         1310 :     if (target != myStep && myLinkMinGreenTimes.size() > 0) {
    1161         1310 :         const std::string& state = myPhases[myStep]->getState();
    1162         1310 :         const std::string& targetState = myPhases[target]->getState();
    1163        21118 :         for (int i = 0; i < myNumLinks; i++) {
    1164        19808 :             if (myLinkGreenTimes[i] < myLinkMinGreenTimes[i]
    1165         3008 :                     && (state[i] == 'G' || state[i] == 'g')
    1166        20406 :                     && !(targetState[i] == 'G' || targetState[i] == 'g')) {
    1167          354 :                 result = MAX2(result, myLinkMinGreenTimes[i] - myLinkGreenTimes[i]);
    1168              :                 //std::cout << SIMTIME << " getLinkMinDuration myStep=" << myStep << " target=" << target << " i=" << i
    1169              :                 //    << " greenTime=" << STEPS2TIME(myLinkGreenTimes[i]) << " min=" << STEPS2TIME(myLinkMinGreenTimes[i]) << " result=" << STEPS2TIME(result) << "\n";
    1170              :             }
    1171              :         }
    1172              :     }
    1173         1310 :     return result;
    1174              : }
    1175              : 
    1176              : int
    1177        37741 : MSActuatedTrafficLightLogic::decideNextPhaseCustom(bool mustSwitch) {
    1178        92702 :     for (int next : getCurrentPhaseDef().nextPhases) {
    1179        56489 :         const MSPhaseDefinition* phase = myPhases[next];
    1180        56489 :         const std::string& condition = mustSwitch ? phase->finalTarget : phase->earlyTarget;
    1181              : #ifdef DEBUG_PHASE_SELECTION_CUSTOM
    1182              :         if (DEBUG_COND) {
    1183              :             std::cout << SIMTIME << " mustSwitch=" << mustSwitch << " cur=" << myStep << " next=" << next << " condition=" << condition
    1184              :                       << " eval=" << (condition == "" ? NAN : evalExpression(condition)) << "\n";
    1185              :         }
    1186              : #endif
    1187        56489 :         if (condition != "") {
    1188              :             // backward compatibility if a user redefined DEFAULT_CONDITION
    1189        46365 :             if (condition == DEFAULT_CONDITION && myConditions.count(DEFAULT_CONDITION) == 0) {
    1190         2696 :                 if (gapControl() == std::numeric_limits<double>::max()) {
    1191          164 :                     return next;
    1192              :                 }
    1193        38277 :             } else if (evalExpression(condition)) {
    1194         1364 :                 return next;
    1195              :             }
    1196              :         }
    1197              :     }
    1198        36213 :     return mustSwitch ? getCurrentPhaseDef().nextPhases.back() : myStep;
    1199              : }
    1200              : 
    1201              : 
    1202              : double
    1203       618158 : MSActuatedTrafficLightLogic::evalExpression(const std::string& condition) const {
    1204       618158 :     const size_t bracketOpen = condition.find('(');
    1205       618158 :     if (bracketOpen != std::string::npos) {
    1206              :         // find matching closing bracket
    1207              :         size_t bracketClose = std::string::npos;
    1208              :         int open = 1;
    1209       518528 :         for (size_t i = bracketOpen + 1; i < condition.size(); i++) {
    1210       518528 :             if (condition[i] == '(') {
    1211         3732 :                 open++;
    1212       514796 :             } else if (condition[i] == ')') {
    1213        42696 :                 open--;
    1214        42696 :                 if (open == 0) {
    1215              :                     bracketClose = i;
    1216              :                     break;
    1217              :                 }
    1218              :             }
    1219              :         }
    1220        38964 :         if (bracketClose == std::string::npos) {
    1221            0 :             throw ProcessError(TLF("Unmatched parentheses in condition %'", condition));
    1222              :         }
    1223              :         std::string cond2 = condition;
    1224        38964 :         const std::string inBracket = condition.substr(bracketOpen + 1, bracketClose - bracketOpen - 1);
    1225        38964 :         double bracketVal = evalExpression(inBracket);
    1226        38964 :         cond2.replace(bracketOpen, bracketClose - bracketOpen + 1, toString(bracketVal));
    1227              :         try {
    1228        38964 :             return evalExpression(cond2);
    1229            0 :         } catch (ProcessError& e) {
    1230            0 :             throw ProcessError(TLF("Error when evaluating expression '%':\n  %", condition, e.what()));
    1231            0 :         }
    1232              :     }
    1233      1158388 :     std::vector<std::string> tokens = StringTokenizer(condition).getVector();
    1234              :     //std::cout << SIMTIME << " tokens(" << tokens.size() << ")=" << toString(tokens) << "\n";
    1235       579194 :     if (tokens.size() == 0) {
    1236            0 :         throw ProcessError(TLF("Invalid empty condition '%'", condition));
    1237       579194 :     } else if (tokens.size() == 1) {
    1238              :         try {
    1239       354617 :             return evalAtomicExpression(tokens[0]);
    1240            0 :         } catch (ProcessError& e) {
    1241            0 :             throw ProcessError(TLF("Error when evaluating expression '%':\n  %", condition, e.what()));
    1242            0 :         }
    1243       224577 :     } else if (tokens.size() == 2) {
    1244         2488 :         if (tokens[0] == "not") {
    1245              :             try {
    1246         2488 :                 return evalAtomicExpression(tokens[1]) == 0. ? 1. : 0.;
    1247            0 :             } catch (ProcessError& e) {
    1248            0 :                 throw ProcessError(TLF("Error when evaluating expression '%':\n  %", condition, e.what()));
    1249            0 :             }
    1250              :         } else {
    1251            0 :             throw ProcessError(TLF("Unsupported condition '%'", condition));
    1252              :         }
    1253       222089 :     } else if (tokens.size() == 3) {
    1254              :         // infix expression
    1255       146307 :         const double a = evalAtomicExpression(tokens[0]);
    1256       146307 :         const double b = evalAtomicExpression(tokens[2]);
    1257              :         const std::string& o = tokens[1];
    1258              :         //std::cout << SIMTIME << " o=" << o << " a=" << a << " b=" << b << "\n";
    1259              :         try {
    1260       146307 :             return evalTernaryExpression(a, o, b, condition);
    1261            0 :         } catch (ProcessError& e) {
    1262            0 :             throw ProcessError(TLF("Error when evaluating expression '%':\n  %", condition, e.what()));
    1263            0 :         }
    1264              :     } else {
    1265        75782 :         const int iEnd = (int)tokens.size() - 1;
    1266       884412 :         for (const std::string& o : OPERATOR_PRECEDENCE) {
    1267      4110670 :             for (int i = 1; i < iEnd; i++) {
    1268      3302040 :                 if (tokens[i] == o) {
    1269              :                     try {
    1270        75782 :                         const double val = evalTernaryExpression(
    1271        75782 :                                                evalAtomicExpression(tokens[i - 1]), o,
    1272       151564 :                                                evalAtomicExpression(tokens[i + 1]), condition);
    1273        75782 :                         std::vector<std::string> newTokens(tokens.begin(), tokens.begin() + (i - 1));
    1274       151564 :                         newTokens.push_back(toString(val));
    1275        75782 :                         newTokens.insert(newTokens.end(), tokens.begin() + (i + 2), tokens.end());
    1276        75782 :                         return evalExpression(toString(newTokens));
    1277        75782 :                     } catch (ProcessError& e) {
    1278            0 :                         throw ProcessError(TLF("Error when evaluating expression '%':\n  %", condition, e.what()));
    1279            0 :                     }
    1280              :                 }
    1281              :             }
    1282              :         }
    1283            0 :         throw ProcessError(TLF("Parsing expressions with % elements ('%') is not supported", toString(tokens.size()), condition));
    1284              :     }
    1285              :     return true;
    1286       579194 : }
    1287              : 
    1288              : double
    1289       222089 : MSActuatedTrafficLightLogic::evalTernaryExpression(double a, const std::string& o, double b, const std::string& condition) const {
    1290       222089 :     if (o == "=" || o == "==") {
    1291        38704 :         return (double)(a == b);
    1292       201097 :     } else if (o == "<") {
    1293            0 :         return (double)(a < b);
    1294       201097 :     } else if (o == ">") {
    1295       105091 :         return (double)(a > b);
    1296       126645 :     } else if (o == "<=") {
    1297            0 :         return (double)(a <= b);
    1298       126645 :     } else if (o == ">=") {
    1299        20574 :         return (double)(a >= b);
    1300       116239 :     } else if (o == "!=") {
    1301         9456 :         return (double)(a != b);
    1302       106983 :     } else if (o == "or" || o == "||") {
    1303        39094 :         return (double)(a || b);
    1304        67889 :     } else if (o == "and" || o == "&&") {
    1305        45379 :         return (double)(a && b);
    1306        22510 :     } else if (o == "+") {
    1307        13194 :         return a + b;
    1308         9316 :     } else if (o == "-") {
    1309            0 :         return a - b;
    1310         9316 :     } else if (o == "*") {
    1311            0 :         return a * b;
    1312         9316 :     } else if (o == "/") {
    1313           58 :         if (b == 0) {
    1314          174 :             WRITE_ERRORF(TL("Division by 0 in condition '%'"), condition);
    1315           58 :             return 0;
    1316              :         }
    1317            0 :         return a / b;
    1318         9258 :     } else if (o == "%") {
    1319         9256 :         return fmod(a, b);
    1320            2 :     } else if (o == "**" || o == "^") {
    1321            2 :         return pow(a, b);
    1322              :     } else  {
    1323            0 :         throw ProcessError(TLF("Unsupported operator '%' in condition '%'", o, condition));
    1324              :     }
    1325              : }
    1326              : 
    1327              : double
    1328         9256 : MSActuatedTrafficLightLogic::evalCustomFunction(const std::string& fun, const std::string& arg) const {
    1329        27768 :     std::vector<std::string> args = StringTokenizer(arg, ",").getVector();
    1330              :     const Function& f = myFunctions.find(fun)->second;
    1331         9256 :     if ((int)args.size() != f.nArgs) {
    1332            0 :         throw ProcessError(TLF("Function '%' requires % arguments but % were given", fun, toString(f.nArgs), toString(args.size())));
    1333              :     }
    1334              :     std::vector<double> args2;
    1335        23140 :     for (auto a : args) {
    1336        13884 :         args2.push_back(evalExpression(a));
    1337              :     }
    1338         9256 :     myStack.push_back(myStack.back());
    1339         9256 :     myStack.back()["$0"] = 0;
    1340        23140 :     for (int i = 0; i < (int)args2.size(); i++) {
    1341        41652 :         myStack.back()["$" + toString(i + 1)] = args2[i];
    1342              :     }
    1343              :     try {
    1344              :         ConditionMap empty;
    1345         9256 :         executeAssignments(f.assignments, empty, myConditions);
    1346            0 :     } catch (ProcessError& e) {
    1347            0 :         throw ProcessError(TLF("Error when evaluating function '%' with args '%' (%)", fun, joinToString(args2, ","), e.what()));
    1348            0 :     }
    1349         9256 :     double result = myStack.back()["$0"];
    1350              :     myStack.pop_back();
    1351         9256 :     return result;
    1352         9256 : }
    1353              : 
    1354              : 
    1355              : void
    1356       579004 : MSActuatedTrafficLightLogic::executeAssignments(const AssignmentMap& assignments, ConditionMap& conditions, const ConditionMap& forbidden) const {
    1357       680922 :     for (const auto& assignment : assignments) {
    1358       101918 :         if (evalExpression(std::get<1>(assignment))) {
    1359              :             const std::string& id = std::get<0>(assignment);
    1360        55944 :             const double val = evalExpression(std::get<2>(assignment));
    1361              :             ConditionMap::iterator it = conditions.find(id);
    1362        55944 :             if (it != conditions.end()) {
    1363        83244 :                 it->second = toString(val);
    1364        14322 :             } else if (forbidden.find(id) != forbidden.end()) {
    1365            0 :                 throw ProcessError(TLF("Modifying global condition '%' is forbidden", id));
    1366              :             } else {
    1367        14322 :                 myStack.back()[id] = val;
    1368              :             }
    1369              :         }
    1370              :     }
    1371       579004 : }
    1372              : 
    1373              : 
    1374              : double
    1375       810829 : MSActuatedTrafficLightLogic::evalAtomicExpression(const std::string& expr) const {
    1376       810829 :     if (expr.size() == 0) {
    1377            0 :         throw ProcessError(TL("Invalid empty expression"));
    1378       810829 :     } else if (expr[0] == '!') {
    1379        18776 :         return evalAtomicExpression(expr.substr(1)) == 0. ? 1. : 0.;
    1380       801441 :     } else if (expr[0] == '-') {
    1381          158 :         return -evalAtomicExpression(expr.substr(1));
    1382              :     } else {
    1383              :         // check for 'operator:'
    1384       801283 :         const size_t pos = expr.find(':');
    1385       801283 :         if (pos == std::string::npos) {
    1386              :             auto it = myConditions.find(expr);
    1387       701129 :             if (it != myConditions.end()) {
    1388              :                 // symbol lookup
    1389       166225 :                 return evalExpression(it->second);
    1390              :             } else {
    1391              :                 // look at stack
    1392              :                 auto it2 = myStack.back().find(expr);
    1393       534904 :                 if (it2 != myStack.back().end()) {
    1394        32396 :                     return it2->second;
    1395              :                 }
    1396              :                 // must be a number
    1397       502508 :                 return StringUtils::toDouble(expr);
    1398              :             }
    1399              :         } else {
    1400       100154 :             const std::string fun = expr.substr(0, pos);
    1401       100154 :             const std::string arg = expr.substr(pos + 1);
    1402       100154 :             if (fun == "z") {
    1403        70912 :                 return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection();
    1404        29242 :             } else if (fun == "a") {
    1405              :                 try {
    1406         9790 :                     return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection() == 0;
    1407         8536 :                 } catch (ProcessError&) {
    1408         8536 :                     return retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(arg, expr, true)->getCurrentVehicleNumber();
    1409         8536 :                 }
    1410        19452 :             } else if (fun == "w") {
    1411              :                 try {
    1412         3272 :                     return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getOccupancyTime();
    1413         2184 :                 } catch (ProcessError&) {
    1414         2184 :                     return retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(arg, expr, true)->getCurrentJamDuration();
    1415         2184 :                 }
    1416        16180 :             } else if (fun == "d") {
    1417              :                 try {
    1418         2096 :                     return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getArrivalDelay();
    1419         1052 :                 } catch (ProcessError&) {
    1420         1052 :                     return retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(arg, expr, true)->getArrivalDelay();
    1421         1052 :                 }
    1422        14084 :             } else if (fun == "g" || fun == "r") {
    1423              :                 try {
    1424         4370 :                     int linkIndex = StringUtils::toInt(arg);
    1425         4370 :                     if (linkIndex >= 0 && linkIndex < myNumLinks) {
    1426         4370 :                         const std::vector<SUMOTime>& times = fun == "g" ? myLinkGreenTimes : myLinkRedTimes;
    1427         4370 :                         if (times.empty()) {
    1428              :                             return 0;
    1429              :                         }
    1430         4322 :                         if (myLastTrySwitchTime < SIMSTEP) {
    1431              :                             // times are only updated at the start of a phase where
    1432              :                             // switching is possible (i.e. not during minDur).
    1433              :                             // If somebody is looking at those values in the tracker
    1434              :                             // this would be confusing
    1435          576 :                             const LinkState ls = getCurrentPhaseDef().getSignalState(linkIndex);
    1436          288 :                             if ((fun == "g" && (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR))
    1437          744 :                                     || (fun == "r" && (ls == LINKSTATE_TL_RED || ls == LINKSTATE_TL_REDYELLOW))) {
    1438          276 :                                 const SUMOTime currentGreen = SIMSTEP - myLastTrySwitchTime;
    1439          276 :                                 return STEPS2TIME(times[linkIndex] + currentGreen);
    1440              :                             } else {
    1441              :                                 return 0;
    1442              :                             }
    1443              :                         } else {
    1444         3746 :                             return STEPS2TIME(times[linkIndex]);
    1445              :                         }
    1446              :                     }
    1447            0 :                 } catch (NumberFormatException&) { }
    1448            0 :                 throw ProcessError(TLF("Invalid link index '%' in expression '%'", arg, expr));
    1449         9714 :             } else if (fun == "p") {
    1450              :                 try {
    1451          386 :                     int linkIndex = StringUtils::toInt(arg);
    1452          386 :                     if (linkIndex >= 0 && linkIndex < myNumLinks) {
    1453              :                         double approachingPersons = 0;
    1454          772 :                         for (const MSLink* link : getLinksAt(linkIndex)) {
    1455              :                             auto* aPersons = link->getApproachingPersons();
    1456          386 :                             if (aPersons != nullptr) {
    1457          344 :                                 approachingPersons += (double)aPersons->size();
    1458              :                             }
    1459              :                         }
    1460          386 :                         return approachingPersons;
    1461              :                     }
    1462            0 :                 } catch (NumberFormatException&) { }
    1463            0 :                 throw ProcessError(TLF("Invalid link index '%' in expression '%'", arg, expr));
    1464         9328 :             } else if (fun == "c") {
    1465           72 :                 return STEPS2TIME(getTimeInCycle());
    1466              :             } else {
    1467         9256 :                 if (myFunctions.find(fun) == myFunctions.end()) {
    1468            0 :                     throw ProcessError(TLF("Unsupported function '%' in expression '%'", fun, expr));
    1469              :                 }
    1470         9256 :                 return evalCustomFunction(fun, arg);
    1471              :             }
    1472              :         }
    1473              :     }
    1474              : }
    1475              : 
    1476              : 
    1477              : std::map<std::string, double>
    1478         4765 : MSActuatedTrafficLightLogic::getDetectorStates() const {
    1479              :     std::map<std::string, double> result;
    1480        58325 :     for (auto li : myInductLoops) {
    1481       103200 :         result[li.loop->getID()] = li.loop->getOccupancy() > 0 ? 1 : 0;
    1482              :     }
    1483         6437 :     for (auto loop : myExtraLoops) {
    1484         3252 :         result[loop->getID()] = loop->getOccupancy() > 0 ? 1 : 0;
    1485              :     }
    1486         6437 :     for (auto loop : myExtraE2) {
    1487         1672 :         result[loop->getID()] = loop->getCurrentVehicleNumber();
    1488              :     }
    1489         4765 :     return result;
    1490              : }
    1491              : 
    1492              : double
    1493            0 : MSActuatedTrafficLightLogic::getDetectorState(const std::string laneID) const {
    1494              :     double result = 0.0;
    1495            0 :     for (auto li : myInductLoops) {
    1496            0 :         if (li.lane->getID() == laneID) {
    1497            0 :             result = li.loop->getOccupancy() > 0 ? 1 : 0;
    1498              :             break;
    1499              :         }
    1500              :     }
    1501            0 :     return result;
    1502              : }
    1503              : 
    1504              : std::map<std::string, double>
    1505        14773 : MSActuatedTrafficLightLogic::getConditions() const {
    1506              :     std::map<std::string, double> result;
    1507        81725 :     for (auto item : myConditions) {
    1508              :         if (myListedConditions.count(item.first) != 0) {
    1509              :             try {
    1510        57496 :                 result[item.first] = evalExpression(item.second);
    1511            0 :             } catch (ProcessError& e) {
    1512            0 :                 WRITE_ERRORF(TL("Error when retrieving conditions '%' for tlLogic '%' (%)"), item.first, getID(), e.what());
    1513            0 :             }
    1514              :         }
    1515              :     }
    1516        14773 :     return result;
    1517              : }
    1518              : 
    1519              : const std::string
    1520        30508 : MSActuatedTrafficLightLogic::getParameter(const std::string& key, const std::string defaultValue) const {
    1521        61016 :     if (StringUtils::startsWith(key, "condition.")) {
    1522         3600 :         const std::string cond = key.substr(10);
    1523              :         auto it = myConditions.find(cond);
    1524         3600 :         if (it != myConditions.end()) {
    1525         3600 :             return toString(evalExpression(it->second));
    1526              :         } else {
    1527            0 :             throw InvalidArgument(TLF("Unknown condition '%' for actuated traffic light '%'", cond, getID()));
    1528              :         }
    1529              :     } else {
    1530        53816 :         return MSSimpleTrafficLightLogic::getParameter(key, defaultValue);
    1531              :     }
    1532              : }
    1533              : 
    1534              : void
    1535           36 : MSActuatedTrafficLightLogic::setParameter(const std::string& key, const std::string& value) {
    1536              :     // some pre-defined parameters can be updated at runtime
    1537           36 :     if (key == "detector-gap" || key == "passing-time" || key == "file" || key == "freq" || key == "vTypes"
    1538           36 :             || key == "build-all-detectors"
    1539           72 :             || StringUtils::startsWith(key, "linkMaxDur")
    1540          108 :             || StringUtils::startsWith(key, "linkMinDur")) {
    1541            0 :         throw InvalidArgument(key + " cannot be changed dynamically for actuated traffic light '" + getID() + "'");
    1542           36 :     } else if (key == "max-gap") {
    1543            5 :         myMaxGap = StringUtils::toDouble(value);
    1544              :         // overwrite custom values
    1545           45 :         for (InductLoopInfo& loopInfo : myInductLoops) {
    1546           40 :             loopInfo.maxGap = myMaxGap;
    1547              :         }
    1548            5 :         Parameterised::setParameter(key, value);
    1549           62 :     } else if (StringUtils::startsWith(key, "max-gap:")) {
    1550            0 :         const std::string laneID = key.substr(8);
    1551            0 :         for (InductLoopInfo& loopInfo : myInductLoops) {
    1552            0 :             if (loopInfo.lane->getID() == laneID) {
    1553            0 :                 loopInfo.maxGap = StringUtils::toDouble(value);
    1554            0 :                 Parameterised::setParameter(key, value);
    1555              :                 return;
    1556              :             }
    1557              :         }
    1558            0 :         throw InvalidArgument(TLF("Invalid lane '%' in key '%' for actuated traffic light '%'", laneID, key, getID()));
    1559           31 :     } else if (key == "jam-threshold") {
    1560            8 :         myJamThreshold = StringUtils::toDouble(value);
    1561              :         // overwrite custom values
    1562           40 :         for (InductLoopInfo& loopInfo : myInductLoops) {
    1563           32 :             loopInfo.jamThreshold = myJamThreshold;
    1564              :         }
    1565            8 :         Parameterised::setParameter(key, value);
    1566           46 :     } else if (StringUtils::startsWith(key, "jam-threshold:")) {
    1567            4 :         const std::string laneID = key.substr(14);
    1568           12 :         for (InductLoopInfo& loopInfo : myInductLoops) {
    1569           12 :             if (loopInfo.lane->getID() == laneID) {
    1570            4 :                 loopInfo.jamThreshold = StringUtils::toDouble(value);
    1571            4 :                 Parameterised::setParameter(key, value);
    1572              :                 return;
    1573              :             }
    1574              :         }
    1575            0 :         throw InvalidArgument(TLF("Invalid lane '%' in key '%' for actuated traffic light '%'", laneID, key, getID()));
    1576           19 :     } else if (key == "show-detectors") {
    1577            4 :         myShowDetectors = StringUtils::toBool(value);
    1578            4 :         Parameterised::setParameter(key, value);
    1579           20 :         for (InductLoopInfo& loopInfo : myInductLoops) {
    1580           16 :             loopInfo.loop->setVisible(myShowDetectors);
    1581              :         }
    1582           15 :     } else if (key == "inactive-threshold") {
    1583            0 :         myInactiveThreshold = string2time(value);
    1584            0 :         Parameterised::setParameter(key, value);
    1585              :     } else {
    1586           15 :         MSSimpleTrafficLightLogic::setParameter(key, value);
    1587              :     }
    1588              : }
    1589              : 
    1590              : 
    1591              : void
    1592            2 : MSActuatedTrafficLightLogic::saveState(OutputDevice& out) const {
    1593            2 :     out.openTag(SUMO_TAG_TLLOGIC);
    1594            2 :     MSSimpleTrafficLightLogic::saveStateAttrs(out);
    1595              : 
    1596              :     std::vector<double> state;
    1597           18 :     for (const InductLoopInfo& loopInfo : myInductLoops) {
    1598           16 :         MSInductLoop* loop = loopInfo.loop;
    1599           16 :         state.push_back(loop->getTimeSinceLastDetection());
    1600              :     }
    1601            2 :     out.writeAttr(SUMO_ATTR_STATE, state);
    1602            2 :     out.closeTag();
    1603            2 : }
    1604              : 
    1605              : 
    1606              : void
    1607            2 : MSActuatedTrafficLightLogic::loadExtraState(const std::string& state) {
    1608            4 :     std::vector<std::string> timeGaps = StringTokenizer(state).getVector();
    1609              :     int i = 0;
    1610           18 :     for (const InductLoopInfo& loopInfo : myInductLoops) {
    1611           16 :         MSInductLoop* loop = loopInfo.loop;
    1612           16 :         if (i < (int)timeGaps.size()) {
    1613           16 :             loop->loadTimeSinceLastDetection(StringUtils::toDouble(timeGaps[i]));
    1614              :         }
    1615           16 :         i++;
    1616              :     }
    1617            2 : }
    1618              : 
    1619              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1