LCOV - code coverage report
Current view: top level - src/microsim/traffic_lights - MSActuatedTrafficLightLogic.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 89.6 % 732 656
Test Date: 2026-03-02 16:00:03 Functions: 95.2 % 42 40

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

Generated by: LCOV version 2.0-1