Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
MSActuatedTrafficLightLogic.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
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>
34#include <microsim/MSGlobals.h>
35#include <microsim/MSNet.h>
36#include <microsim/MSLane.h>
37#include <microsim/MSEdge.h>
41
42//#define DEBUG_DETECTORS
43//#define DEBUG_PHASE_SELECTION
44#define DEBUG_COND (getID()=="C")
45
46// ===========================================================================
47// static members
48// ===========================================================================
49const std::vector<std::string> MSActuatedTrafficLightLogic::OPERATOR_PRECEDENCE({
50 "**", "^", "*", "/", "+", "-", "%",
51 "=", "==", "!=", "<", ">", "<=", ">=",
52 "and", "&&", "or", "||",
53});
54
55// ===========================================================================
56// parameter defaults definitions
57// ===========================================================================
58#define DEFAULT_MAX_GAP "3.0"
59#define DEFAULT_PASSING_TIME "1.9"
60#define DEFAULT_DETECTOR_GAP "2.0"
61#define DEFAULT_INACTIVE_THRESHOLD "180"
62#define DEFAULT_CURRENT_PRIORITY 10
63
64#define DEFAULT_LENGTH_WITH_GAP 7.5
65#define DEFAULT_BIKE_LENGTH_WITH_GAP (getDefaultVehicleLength(SVC_BICYCLE) + 0.5)
66
67#define NO_DETECTOR "NO_DETECTOR"
68#define DEFAULT_CONDITION "DEFAULT"
69
70// ===========================================================================
71// method definitions
72// ===========================================================================
74 const std::string& id, const std::string& programID,
75 const SUMOTime offset,
76 const Phases& phases,
77 int step, SUMOTime delay,
78 const Parameterised::Map& parameter,
79 const std::string& basePath,
80 const ConditionMap& conditions,
81 const AssignmentMap& assignments,
82 const FunctionMap& functions) :
83 MSSimpleTrafficLightLogic(tlcontrol, id, programID, offset, TrafficLightType::ACTUATED, phases, step, delay, parameter),
84 myHasMultiTarget(false),
85 myLastTrySwitchTime(0),
86 myConditions(conditions),
87 myAssignments(assignments),
88 myFunctions(functions),
89 myTraCISwitch(false),
90 myDetectorPrefix(id + "_" + programID + "_") {
92 myJamThreshold = StringUtils::toDouble(getParameter("jam-threshold", OptionsCont::getOptions().getValueString("tls.actuated.jam-threshold")));
96 myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
97 myBuildAllDetectors = StringUtils::toBool(getParameter("build-all-detectors", "false"));
98 myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
100 myVehicleTypes = getParameter("vTypes", "");
101
102 if (hasParameter("hide-conditions")) {
103 std::vector<std::string> hidden = StringTokenizer(getParameter("hide-conditions", "")).getVector();
104 std::set<std::string> hiddenSet(hidden.begin(), hidden.end());
105 for (auto item : myConditions) {
106 if (hiddenSet.count(item.first) == 0) {
107 myListedConditions.insert(item.first);
108 }
109 }
110 } else {
111 const bool showAll = getParameter("show-conditions", "") == "";
112 std::vector<std::string> shown = StringTokenizer(getParameter("show-conditions", "")).getVector();
113 std::set<std::string> shownSet(shown.begin(), shown.end());
114 for (auto item : myConditions) {
115 if (showAll || shownSet.count(item.first) != 0) {
116 myListedConditions.insert(item.first);
117 }
118 }
119 }
120 if (hasParameter("extra-detectors")) {
121 const std::string extraIDs = getParameter("extra-detectors", "");
122 for (std::string customID : StringTokenizer(extraIDs).getVector()) {
123 try {
124 myExtraLoops.push_back(retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(customID, extraIDs, true));
125 } catch (ProcessError&) {
126 myExtraE2.push_back(retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(customID, extraIDs, true));
127 }
128 }
129 }
130 myStack.push_back(std::map<std::string, double>());
131}
132
133
135
136void
141 if (myLanes.size() == 0) {
142 // must be an older network
143 WRITE_WARNINGF(TL("Traffic light '%' does not control any links"), getID());
144 }
145 bool warn = true; // warn only once
146 const int numLinks = (int)myLinks.size();
147
148 // Detector position should be computed based on road speed. If the position
149 // is quite far away and the minDur is short this may cause the following
150 // problems:
151 //
152 // 1) high flow failure:
153 // In a standing queue, no vehicle touches the detector.
154 // By the time the queue advances, the detector gap has been exceeded and the phase terminates prematurely
155 //
156 // 2) low flow failure
157 // The standing queue is fully between stop line and detector and there are no further vehicles.
158 // The minDur is too short to let all vehicles pass
159 //
160 // Problem 2) is not so critical because there is less potential for
161 // jamming in a low-flow situation. In contrast, problem 1) should be
162 // avoided as it has big jamming potential. We compute an upper bound for the
163 // detector distance to avoid it
164
165
166 // change values for setting the loops and lanestate-detectors, here
167 //SUMOTime inductLoopInterval = 1; //
168 // build the induct loops
169 std::map<const MSLane*, MSInductLoop*> laneInductLoopMap;
170 std::map<MSInductLoop*, int> inductLoopInfoMap; // retrieve junction entry lane in case loops are placed further upstream (and other properties)
171 int detEdgeIndex = -1;
172 int detLaneIndex = 0;
173 const double detDefaultLength = StringUtils::toDouble(getParameter("detector-length",
174 OptionsCont::getOptions().getValueString("tls.actuated.detector-length")));
175 MSEdge* prevDetEdge = nullptr;
176 for (LaneVector& lanes : myLanes) {
177 for (MSLane* lane : lanes) {
178 const std::string customID = getParameter(lane->getID());
179 if (noVehicles(lane->getPermissions()) && customID == "") {
180 // do not build detectors on green verges or sidewalks
181 continue;
182 }
183 if (laneInductLoopMap.find(lane) != laneInductLoopMap.end()) {
184 // only build one detector per lane
185 continue;
186 }
187 const SUMOTime minDur = getMinimumMinDuration(lane);
188 if (minDur == std::numeric_limits<SUMOTime>::max() && customID == "" && !myBuildAllDetectors) {
189 // only build detector if this lane is relevant for an actuated phase
190 continue;
191 }
192 double length = lane->getLength();
193 double ilpos;
194 double inductLoopPosition;
195 MSInductLoop* loop = nullptr;
196 if (&lane->getEdge() != prevDetEdge) {
197 detEdgeIndex++;
198 detLaneIndex = 0;
199 prevDetEdge = &lane->getEdge();
200 } else {
201 detLaneIndex++;
202 }
203 const bool isBikeLane = (lane->getPermissions() & ~SVC_PEDESTRIAN) == SVC_BICYCLE;
204 const double defaultLength = isBikeLane ? DEFAULT_BIKE_LENGTH_WITH_GAP : DEFAULT_LENGTH_WITH_GAP;
205 if (customID == "") {
206 const double speed = isBikeLane ? DEFAULT_BICYCLE_SPEED : lane->getSpeedLimit();
207 inductLoopPosition = MIN2(
208 myDetectorGap * speed,
209 (STEPS2TIME(minDur) / myPassingTime + 0.5) * defaultLength);
210
211 // check whether the lane is long enough
212 ilpos = length - inductLoopPosition;
213 MSLane* placementLane = lane;
214 while (ilpos < 0 && placementLane->getIncomingLanes().size() == 1
215 && placementLane->getIncomingLanes().front().viaLink->getCorrespondingEntryLink()->getTLLogic() == nullptr) {
216 placementLane = placementLane->getLogicalPredecessorLane();
217 ilpos += placementLane->getLength();
218 }
219 if (ilpos < 0) {
220 ilpos = 0;
221 }
222 // Build the induct loop and set it into the container
223 const double detLength = getDouble("detector-length:" + lane->getID(), detDefaultLength);
224 std::string id = myDetectorPrefix + "D" + toString(detEdgeIndex) + "." + toString(detLaneIndex);
225 loop = static_cast<MSInductLoop*>(nb.createInductLoop(id, placementLane, ilpos, detLength, "", myVehicleTypes, "", (int)PersonMode::NONE, myShowDetectors));
227 } else if (customID == NO_DETECTOR) {
228 continue;
229 } else {
231 if (loop == nullptr) {
232 throw ProcessError(TLF("Unknown inductionLoop '%' given as custom detector for actuated tlLogic '%', program '%.", customID, getID(), getProgramID()));
233 }
234 ilpos = loop->getPosition();
235 inductLoopPosition = length - ilpos;
236 }
237 const double maxGap = getDouble("max-gap:" + lane->getID(), myMaxGap);
238 const double jamThreshold = getDouble("jam-threshold:" + lane->getID(), myJamThreshold);
239 laneInductLoopMap[lane] = loop;
240 inductLoopInfoMap[loop] = (int)myInductLoops.size();
241 myInductLoops.push_back(InductLoopInfo(loop, lane, (int)myPhases.size(), maxGap, jamThreshold));
242
243 if (warn && floor(floor(inductLoopPosition / defaultLength) * myPassingTime) > STEPS2TIME(minDur)) {
244 // warn if the minGap is insufficient to clear vehicles between stop line and detector
245 WRITE_WARNINGF(TL("At actuated tlLogic '%', minDur % is too short for a detector gap of %m."), getID(), time2string(minDur), toString(inductLoopPosition));
246 warn = false;
247 }
248 }
249 }
250 // assign loops to phase index (myInductLoopsForPhase)
251 // 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
252 // greenMinor is ambiguous as vehicles may not be able to drive
253 // Under the following condition we allow actuation from minor link:
254 // check1a : the minor link is minor in all phases
255 // check1b : there is another major link from the same lane in the current phase
256 // check1e : the conflict is only with bikes/pedestrians (i.e. for a right turn, also left turn with no oncoming traffic)
257 // check1f : the conflict is only with a link from the same edge
258 // (Under these conditions we assume that the minor link is unimportant and traffic is mostly for the major link)
259 //
260 // check1c: when the edge has only one lane, we treat greenMinor as green as there would be no actuation otherwise
261 // check1d: for turnarounds 1b is sufficient and we do not require 1a
262 //
263 // 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
264 //
265 // if a jamThreshold is specificed for the loop, all checks are ignored
266
267 // also assign loops to link index for validation:
268 // check if all links from actuated phases (minDur != maxDur) have an inductionloop in at least one phase
269 const SVCPermissions motorized = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
270 std::map<int, std::set<MSInductLoop*> > linkToLoops;
271 std::set<int> actuatedLinks;
272
273 std::vector<bool> neverMajor(numLinks, true);
274 for (const MSPhaseDefinition* phase : myPhases) {
275 const std::string& state = phase->getState();
276 for (int i = 0; i < numLinks; i++) {
277 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
278 neverMajor[i] = false;
279 }
280 }
281 }
282 std::vector<bool> oneLane(numLinks, false);
283 std::vector<bool> turnaround(numLinks, true);
284 for (int i = 0; i < numLinks; i++) {
285 for (MSLane* lane : getLanesAt(i)) {
286 // only count motorized vehicle lanes
287 int numMotorized = 0;
288 for (MSLane* l : lane->getEdge().getLanes()) {
289 if ((l->getPermissions() & motorized) != 0) {
290 numMotorized++;
291 }
292 }
293 if (numMotorized == 1) {
294 oneLane[i] = true;
295 break;
296 }
297 }
298 for (MSLink* link : getLinksAt(i)) {
299 if (!link->isTurnaround()) {
300 turnaround[i] = false;
301 break;
302 }
303 }
304 }
305
306
307 for (const MSPhaseDefinition* phase : myPhases) {
308 const int phaseIndex = (int)myInductLoopsForPhase.size();
309 std::set<MSInductLoop*> loops;
310 if (phase->isActuated()) {
311 const std::string& state = phase->getState();
312 // collect indices of all green links for the phase
313 std::set<int> greenLinks;
314 // green links that could jam
315 std::set<int> greenLinksPermissive;
316 // collect green links for each induction loops (in this phase)
317 std::map<MSInductLoop*, std::set<int> > loopLinks;
318
319 for (int i = 0; i < numLinks; i++) {
320 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
321 greenLinks.insert(i);
322 actuatedLinks.insert(i);
323 } else if (state[i] == LINKSTATE_TL_GREEN_MINOR) {
324 if (((neverMajor[i] || turnaround[i]) // check1a, 1d
325 && hasMajor(state, getLanesAt(i))) // check1b
326 || oneLane[i] // check1c
327 || weakConflict(i, state)) { // check1e, check1f
328 greenLinks.insert(i);
329 if (!turnaround[i]) {
330 actuatedLinks.insert(i);
331 }
332 } else {
333 greenLinksPermissive.insert(i);
334 }
335 }
336#ifdef DEBUG_DETECTORS
337 if (DEBUG_COND) {
338 std::cout << " phase=" << phaseIndex << " i=" << i << " state=" << state[i] << " green=" << greenLinks.count(i) << " oneLane=" << oneLane[i]
339 << " turn=" << turnaround[i] << " loopLanes=";
340 for (MSLane* lane : getLanesAt(i)) {
341 if (laneInductLoopMap.count(lane) != 0) {
342 std::cout << lane->getID() << " ";
343 }
344 }
345 std::cout << "\n";
346 }
347#endif
348 for (MSLane* lane : getLanesAt(i)) {
349 if (laneInductLoopMap.count(lane) != 0) {
350 loopLinks[laneInductLoopMap[lane]].insert(i);
351 }
352 }
353 }
354 for (auto& item : loopLinks) {
355 MSInductLoop* loop = item.first;
356 const InductLoopInfo& info = myInductLoops[inductLoopInfoMap[loop]];
357 const MSLane* loopLane = info.lane;
358 bool usable = true;
359 bool foundUsable = false;
360 // check1
361 for (int j : item.second) {
362 if (greenLinks.count(j) == 0 && (info.jamThreshold <= 0 || greenLinksPermissive.count(j) == 0)) {
363 usable = false;
364#ifdef DEBUG_DETECTORS
365 if (DEBUG_COND) {
366 std::cout << " phase=" << phaseIndex << " check1: loopLane=" << loopLane->getID() << " notGreen=" << j << " oneLane[j]=" << oneLane[j] << "\n";
367 }
368#endif
369 } else {
370 foundUsable = true;
371 }
372 }
373 if (!usable && foundUsable && info.jamThreshold > 0) {
374 // permit green even when the same lane has green and red links (if we have jamDetection)
375 usable = true;
376 }
377 // check2 (skip if we have jam detection)
378 if (usable && info.jamThreshold <= 0) {
379 for (MSLink* link : loopLane->getLinkCont()) {
380 if (link->isTurnaround()) {
381 continue;
382 }
383 const MSLane* next = link->getLane();
384 if (laneInductLoopMap.count(next) != 0) {
385 MSInductLoop* nextLoop = laneInductLoopMap[next];
386 for (int j : loopLinks[nextLoop]) {
387 if (greenLinks.count(j) == 0) {
388 usable = false;
389#ifdef DEBUG_DETECTORS
390 if (DEBUG_COND) std::cout << " phase=" << phaseIndex << " check2: loopLane=" << loopLane->getID()
391 << " nextLane=" << next->getID() << " nextLink=" << j << " nextState=" << state[j] << "\n";
392#endif
393 break;
394 }
395 }
396 }
397 }
398 }
399
400 if (usable) {
401 loops.insert(item.first);
402#ifdef DEBUG_DETECTORS
403 if (DEBUG_COND) {
404 std::cout << " phase=" << phaseIndex << " usableLoops=" << item.first->getID() << " links=" << joinToString(item.second, " ") << "\n";
405 }
406#endif
407 for (int j : item.second) {
408 linkToLoops[j].insert(item.first);
409 }
410 }
411 }
412 if (loops.size() == 0) {
413 WRITE_WARNINGF(TL("At actuated tlLogic '%', actuated phase % has no controlling detector."), getID(), toString(phaseIndex));
414 }
415 }
416#ifdef DEBUG_DETECTORS
417 if (DEBUG_COND) {
418 std::cout << " phase=" << phaseIndex << " loops=" << joinNamedToString(loops, " ") << "\n";
419 }
420 if (DEBUG_COND) {
421 std::cout << " linkToLoops:\n";
422 for (auto item : linkToLoops) {
423 std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
424 }
425 }
426#endif
427 std::vector<InductLoopInfo*> loopInfos;
428 myInductLoopsForPhase.push_back(loopInfos);
429 for (MSInductLoop* loop : loops) {
430 for (InductLoopInfo& loopInfo : myInductLoops) {
431 if (loopInfo.loop == loop) {
432 myInductLoopsForPhase.back().push_back(&loopInfo);
433 loopInfo.servedPhase[phaseIndex] = true;
434 }
435 }
436 }
437 }
438#ifdef DEBUG_DETECTORS
439 if (DEBUG_COND) {
440 std::cout << "final linkToLoops:\n";
441 for (auto item : linkToLoops) {
442 std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
443 }
444 }
445#endif
446 std::vector<int> warnLinks;
447 for (int i : actuatedLinks) {
448 if (linkToLoops[i].size() == 0 && myLinks[i].size() > 0
449 && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
450 if (getParameter(myLinks[i].front()->getLaneBefore()->getID()) != NO_DETECTOR) {
451 warnLinks.push_back(i);
452 }
453 }
454 }
455 if (warnLinks.size() > 0) {
456 WRITE_WARNINGF(TL("At actuated tlLogic '%', linkIndex % has no controlling detector."), getID(), joinToString(warnLinks, ","));
457 }
458 // parse maximum green times for each link (optional)
459 for (const auto& kv : getParametersMap()) {
460 if (StringUtils::startsWith(kv.first, "linkMaxDur:")) {
461 int link = StringUtils::toInt(kv.first.substr(11));
462 if (link < 0 || link >= myNumLinks) {
463 WRITE_ERRORF(TL("Invalid link '%' given as linkMaxDur parameter for actuated tlLogic '%', program '%."), kv.first.substr(11), getID(), getProgramID());
464 continue;
465 }
466 if (myLinkMaxGreenTimes.empty()) {
467 myLinkMaxGreenTimes = std::vector<SUMOTime>(myNumLinks, std::numeric_limits<SUMOTime>::max());
468 }
469 myLinkMaxGreenTimes[link] = string2time(kv.second);
470 } else if (StringUtils::startsWith(kv.first, "linkMinDur:")) {
471 int link = StringUtils::toInt(kv.first.substr(11));
472 if (link < 0 || link >= myNumLinks) {
473 WRITE_ERRORF(TL("Invalid link '%' given as linkMinDur parameter for actuated tlLogic '%', program '%."), kv.first.substr(11), getID(), getProgramID());
474 continue;
475 }
476 if (myLinkMinGreenTimes.empty()) {
477 myLinkMinGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
478 }
479 myLinkMinGreenTimes[link] = string2time(kv.second);
480 }
481 }
482 if (myLinkMaxGreenTimes.size() > 0 || myLinkMinGreenTimes.size() > 0 || mySwitchingRules.size() > 0) {
483 myLinkGreenTimes = std::vector<SUMOTime>(myNumLinks, 0);
484 myLinkRedTimes = std::vector<SUMOTime>(myNumLinks, 0);
485 }
486 //std::cout << SIMTIME << " linkMaxGreenTimes=" << toString(myLinkMaxGreenTimes) << "\n";
487}
488
489
490bool
491MSActuatedTrafficLightLogic::weakConflict(int tlIndex, const std::string& state) const {
492 for (MSLink* link : getLinksAt(tlIndex)) {
493 int linkIndex = link->getIndex();
494 const MSJunction* junction = link->getJunction();
495 for (int i = 0; i < (int)myLinks.size(); i++) {
496 if (i == tlIndex) {
497 continue;
498 }
499 if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
500 for (MSLink* foe : getLinksAt(i)) {
501 // junction logic is based on junction link index rather than tl index
502 int foeIndex = foe->getIndex();
503 const MSJunction* junction2 = foe->getJunction();
504 if (junction == junction2) {
505 const MSJunctionLogic* logic = junction->getLogic();
506 //std::cout << " greenLink=" << i << " isFoe=" << logic->getFoesFor(linkIndex).test(foeIndex) << "\n";
507 if (logic->getFoesFor(linkIndex).test(foeIndex)
508 && (foe->getPermissions() & ~SVC_VULNERABLE) != 0 // check1e
509 && &foe->getLaneBefore()->getEdge() != &link->getLaneBefore()->getEdge()) { // check1f
510 //std::cout << " strongConflict " << tlIndex << " in phase " << state << " with link " << foe->getTLIndex() << "\n";
511 return false;
512 }
513 }
514 }
515
516 }
517 }
518 }
519 //std::cout << " weakConflict " << tlIndex << " in phase " << state << "\n";
520 return true;
521}
522
523
526 step = step < 0 ? myStep : step;
527 const MSPhaseDefinition* p = myPhases[step];
529 ? p->minDuration
530 : TIME2STEPS(evalExpression(myConditions.find("minDur:" + toString(step))->second));
531}
532
535 step = step < 0 ? myStep : step;
536 const MSPhaseDefinition* p = myPhases[step];
538 ? p->maxDuration
539 : TIME2STEPS(evalExpression(myConditions.find("maxDur:" + toString(step))->second));
540}
541
544 step = step < 0 ? myStep : step;
545 const MSPhaseDefinition* p = myPhases[step];
547 ? p->earliestEnd
548 : TIME2STEPS(evalExpression(myConditions.find("earliestEnd:" + toString(step))->second));
549}
550
553 step = step < 0 ? myStep : step;
554 const MSPhaseDefinition* p = myPhases[step];
556 ? p->latestEnd
557 : TIME2STEPS(evalExpression(myConditions.find("latestEnd:" + toString(step))->second));
558}
559
560
561void
564 for (int i = 0; i < (int)myPhases.size(); i++) {
565 MSPhaseDefinition* phase = myPhases[i];
566 const std::string errorSuffix = "' for overriding attribute in phase " + toString(i) + " of tlLogic '" + getID() + "' in program '" + getProgramID() + "'.";
567 if (phase->minDuration == ovrd) {
568 const std::string cond = "minDur:" + toString(i);
569 if (myConditions.count(cond) == 0) {
570 throw ProcessError("Missing condition '" + cond + errorSuffix);
571 }
572 }
573 if (phase->maxDuration == ovrd) {
574 const std::string cond = "maxDur:" + toString(i);
575 if (myConditions.count(cond) == 0) {
576 throw ProcessError("Missing condition '" + cond + errorSuffix);
577 }
578 }
579 if (phase->earliestEnd == ovrd) {
580 const std::string cond = "earliestEnd:" + toString(i);
581 if (myConditions.count(cond) == 0) {
582 throw ProcessError("Missing condition '" + cond + errorSuffix);
583 }
584 }
585 if (phase->latestEnd == ovrd) {
586 const std::string cond = "latestEnd:" + toString(i);
587 if (myConditions.count(cond) == 0) {
588 throw ProcessError("Missing condition '" + cond + errorSuffix);
589 }
590 }
591 }
592}
593
594
595void
597 for (int i = 0; i < (int)myPhases.size(); i++) {
599 MSPhaseDefinition* phase = myPhases[i];
600 std::vector<int> nextPhases = phase->nextPhases;
601 if (nextPhases.size() == 0) {
602 nextPhases.push_back((i + 1) % (int)myPhases.size());
603 } else if (nextPhases.size() > 1) {
604 myHasMultiTarget = true;
605 }
606 for (int next : nextPhases) {
607 if (next >= 0 && next < (int)myPhases.size()) {
608 const MSPhaseDefinition* nextPhase = myPhases[next];
609 if (nextPhase->earlyTarget != "" || nextPhase->finalTarget != "") {
610 sr.enabled = true;
611 // simplifies later code
612 phase->nextPhases = nextPhases;
613 }
614 }
615 }
616 mySwitchingRules.push_back(sr);
617 }
618}
619
620
623 SUMOTime result = std::numeric_limits<SUMOTime>::max();
624 for (int pI = 0; pI < (int)myPhases.size(); pI++) {
625 const MSPhaseDefinition* phase = myPhases[pI];
626 const std::string& state = phase->getState();
627 for (int i = 0; i < (int)state.size(); i++) {
628 if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
629 for (MSLane* cand : getLanesAt(i)) {
630 if (lane == cand) {
631 if (phase->isActuated()) {
632 result = MIN2(result, getMinDur(pI));
633 }
634 }
635 }
636 }
637 }
638 }
639 return result;
640}
641
642bool
643MSActuatedTrafficLightLogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
644 for (int i = 0; i < (int)state.size(); i++) {
645 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
646 for (MSLane* cand : getLanesAt(i)) {
647 for (MSLane* lane : lanes) {
648 if (lane == cand) {
649 return true;
650 }
651 }
652 }
653 }
654 }
655 return false;
656}
657
658
659// ------------ Switching and setting current rows
660void
667
668
669void
672 for (InductLoopInfo& loopInfo : myInductLoops) {
673 loopInfo.loop->setVisible(false);
674 }
675}
676
677void
679 SUMOTime simStep, int step, SUMOTime stepDuration) {
680 // do not change timing if the phase changes
681 if (step >= 0 && step != myStep) {
682 myStep = step;
684 setTrafficLightSignals(simStep);
685 tlcontrol.get(getID()).executeOnSwitchActions();
686 } else if (step < 0) {
687 // TraCI requested new timing
689 mySwitchCommand = new SwitchCommand(tlcontrol, this, stepDuration + simStep);
691 mySwitchCommand, stepDuration + simStep);
692 myTraCISwitch = true;
693 }
694}
695
696
697void
699 const SUMOTime lastSwitch = t - spentDuration;
700 myStep = step;
701 myPhases[myStep]->myLastSwitch = lastSwitch;
702 const SUMOTime nextSwitch = t + getPhase(step).minDuration - spentDuration;
704 mySwitchCommand = new SwitchCommand(tlcontrol, this, nextSwitch);
706 setTrafficLightSignals(lastSwitch);
707 tlcontrol.get(getID()).executeOnSwitchActions();
708}
709
710
713 // checks if the actual phase should be continued
714 // @note any vehicles which arrived during the previous phases which are now waiting between the detector and the stop line are not
715 // considere here. RiLSA recommends to set minDuration in a way that lets all vehicles pass the detector
718
719 if (myLinkGreenTimes.size() > 0) {
720 // constraints exist, record green time durations for each link
721 const std::string& state = getCurrentPhaseDef().getState();
722 SUMOTime lastDuration = SIMSTEP - myLastTrySwitchTime;
723 for (int i = 0; i < myNumLinks; i++) {
724 if (state[i] == 'G' || state[i] == 'g') {
725 myLinkGreenTimes[i] += lastDuration;
726 } else {
727 myLinkGreenTimes[i] = 0;
728 }
729 if (state[i] == 'r' || state[i] == 'u') {
730 myLinkRedTimes[i] += lastDuration;
731 } else {
732 myLinkRedTimes[i] = 0;
733 }
734 }
735 }
737 // decide the next phase
738 const bool multiTarget = myPhases[myStep]->nextPhases.size() > 1 && myPhases[myStep]->nextPhases.front() >= 0;
739 const int origStep = myStep;
740 int nextStep = myStep;
741 SUMOTime actDuration = now - myPhases[myStep]->myLastSwitch;
742
743 if (mySwitchingRules[myStep].enabled) {
744 const bool mustSwitch = MIN2(getMaxDur() - actDuration, getLatest()) <= 0;
745 nextStep = decideNextPhaseCustom(mustSwitch);
746 } else {
747 // default algorithm
748 const double detectionGap = gapControl();
749#ifdef DEBUG_PHASE_SELECTION
750 if (DEBUG_COND) {
751 std::cout << SIMTIME << " p=" << myStep
752 << " trySwitch dGap=" << (detectionGap == std::numeric_limits<double>::max() ? "inf" : toString(detectionGap))
753 << " multi=" << multiTarget << "\n";
754 }
755#endif
756 if (detectionGap < std::numeric_limits<double>::max() && !multiTarget && !myTraCISwitch) {
757 return duration(detectionGap);
758 }
759 if (multiTarget) {
760 nextStep = decideNextPhase();
761 } else {
762 if (myPhases[myStep]->nextPhases.size() == 1 && myPhases[myStep]->nextPhases.front() >= 0) {
763 nextStep = myPhases[myStep]->nextPhases.front();
764 } else {
765 nextStep = (myStep + 1) % (int)myPhases.size();
766 }
767 }
768 }
769
770 myTraCISwitch = false;
771 if (myLinkMinGreenTimes.size() > 0) {
772 SUMOTime linkMinDur = getLinkMinDuration(getTarget(nextStep));
773 if (linkMinDur > 0) {
774 // for multiTarget, the current phase must be extended but if another
775 // targer is chosen, earlier switching than linkMinDur is possible
776 return multiTarget ? TIME2STEPS(1) : linkMinDur;
777 }
778 }
779 myStep = nextStep;
780 assert(myStep <= (int)myPhases.size());
781 assert(myStep >= 0);
782 //stores the time the phase started
783 const SUMOTime prevStart = myPhases[myStep]->myLastSwitch;
784 if (myStep != origStep) {
785 myPhases[origStep]->myLastEnd = now;
786 myPhases[myStep]->myLastSwitch = now;
787 actDuration = 0;
788 }
789 // activate coloring
790 if ((myShowDetectors || myHasMultiTarget) && getCurrentPhaseDef().isGreenPhase()) {
791 for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
792 //std::cout << SIMTIME << " p=" << myStep << " loopinfo=" << loopInfo->loop->getID() << " set lastGreen=" << STEPS2TIME(now) << "\n";
793 if (loopInfo->isJammed()) {
794 loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
795 } else {
796 loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
797 }
798 loopInfo->lastGreenTime = now;
799 }
800 }
801 // set the next event
802#ifdef DEBUG_PHASE_SELECTION
803 if (DEBUG_COND) {
804 std::cout << SIMTIME << " tl=" << getID() << " p=" << myStep
805 << " nextTryMinDur=" << STEPS2TIME(getMinDur() - actDuration)
806 << " nextTryEarliest=" << STEPS2TIME(getEarliest(prevStart)) << "\n";
807 }
808#endif
809 SUMOTime minRetry = myStep != origStep ? 0 : TIME2STEPS(1);
810 return MAX3(minRetry, getMinDur() - actDuration, getEarliest(prevStart));
811}
812
813
814// ------------ "actuated" algorithm methods
816MSActuatedTrafficLightLogic::duration(const double detectionGap) const {
817 assert(getCurrentPhaseDef().isGreenPhase());
818 assert((int)myPhases.size() > myStep);
819 const SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
820 // ensure that minimum duration is kept
821 SUMOTime newDuration = getMinDur() - actDuration;
822 // try to let the last detected vehicle pass the intersection (duration must be positive)
823 newDuration = MAX3(newDuration, TIME2STEPS(myDetectorGap - detectionGap), SUMOTime(1));
824 // cut the decimal places to ensure that phases always have integer duration
825 if (newDuration % 1000 != 0) {
826 const SUMOTime totalDur = newDuration + actDuration;
827 newDuration = (totalDur / 1000 + 1) * 1000 - actDuration;
828 }
829 // ensure that the maximum duration is not exceeded
830 newDuration = MIN3(newDuration, getMaxDur() - actDuration, getLatest());
831 return newDuration;
832}
833
834
835double
837 //intergreen times should not be lengthend
838 assert((int)myPhases.size() > myStep);
839 double result = std::numeric_limits<double>::max();
841 return result;
842 }
843 // switch off active colors
844 if (myShowDetectors) {
845 for (InductLoopInfo& loopInfo : myInductLoops) {
846 if (loopInfo.lastGreenTime < loopInfo.loop->getLastDetectionTime()) {
847 loopInfo.loop->setSpecialColor(&RGBColor::RED);
848 } else {
849 loopInfo.loop->setSpecialColor(nullptr);
850 }
851 }
852 }
854 return result; // end current phase
855 }
856
857 // Checks, if the maxDuration is kept. No phase should last longer than maxDuration.
858 SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
859 if (actDuration >= getCurrentPhaseDef().maxDuration || maxLinkDurationReached() || getLatest() == 0) {
860#ifdef DEBUG_PHASE_SELECTION
861 if (DEBUG_COND) {
862 std::cout << SIMTIME << " actDuration=" << STEPS2TIME(actDuration) << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
863 << " maxLinkDurationReached=" << maxLinkDurationReached() << " latest=" << STEPS2TIME(getLatest()) << "\n";
864 }
865#endif
866 return result; // end current phase
867 }
868
869 // now the gapcontrol starts
870 for (InductLoopInfo* loopInfo : myInductLoopsForPhase[myStep]) {
871 MSInductLoop* loop = loopInfo->loop;
872 if (loopInfo->isJammed()) {
873 loopInfo->loop->setSpecialColor(&RGBColor::ORANGE);
874 } else {
875 loopInfo->loop->setSpecialColor(&RGBColor::GREEN);
876 }
877 const double actualGap = loop->getTimeSinceLastDetection();
878 if (actualGap < loopInfo->maxGap && !loopInfo->isJammed()) {
879 result = MIN2(result, actualGap);
880 }
881 }
882 return result;
883}
884
885int
887 const auto& cands = myPhases[myStep]->nextPhases;
888 // decide by priority
889 // first target is the default when there is no traffic
890 // @note: to keep the current phase, even when there is no traffic, it must be added to 'next' explicitly
891 int result = cands.front();
892 int maxPrio = 0;
893 SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
894 const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && !maxLinkDurationReached() && getLatest() > 0;
895 if (canExtend) {
896 // consider keeping the current phase until maxDur is reached
897 // (only when there is still traffic in that phase)
898 int currentPrio = getPhasePriority(myStep);
899#ifdef DEBUG_PHASE_SELECTION
900 std::cout << SIMTIME << " p=" << myStep << " loops=" << myInductLoopsForPhase[myStep].size() << " currentPrio=" << currentPrio << "\n";
901#endif
902 if (currentPrio > maxPrio) {
903 result = myStep;
904 maxPrio = currentPrio;
905 }
906 }
907 for (int step : cands) {
908 int target = getTarget(step);
909 int prio = getPhasePriority(target);
910#ifdef DEBUG_PHASE_SELECTION
911 if (DEBUG_COND) {
912 std::cout << SIMTIME << " p=" << myStep << " step=" << step << " target=" << target << " loops=" << myInductLoopsForPhase[target].size() << " prio=" << prio << "\n";
913 }
914#endif
915 if (prio > maxPrio && canExtendLinkGreen(target)) {
916 maxPrio = prio;
917 result = step;
918 }
919 }
920 // prevent starvation in phases that are not direct targets
921 for (const InductLoopInfo& loopInfo : myInductLoops) {
922 int prio = getDetectorPriority(loopInfo);
923 if (prio > maxPrio) {
924 result = cands.front();
925 if (result == myStep) {
926 WRITE_WARNING("At actuated tlLogic '" + getID()
927 + "', starvation at e1Detector '" + loopInfo.loop->getID()
928 + "' which cannot be reached from the default phase " + toString(myStep) + ".");
929 }
930 // use default phase to reach other phases
931#ifdef DEBUG_PHASE_SELECTION
932 if (DEBUG_COND) {
933 std::cout << SIMTIME << " p=" << myStep << " loop=" << loopInfo.loop->getID() << " prio=" << prio << " next=" << result << "\n";
934 }
935#endif
936 break;
937 }
938 }
939 return result;
940}
941
942
943int
945 int origStep = step;
946 // if step is a transition, find the upcoming green phase
947 while (!myPhases[step]->isGreenPhase()) {
948 if (myPhases[step]->nextPhases.size() > 0 && myPhases[step]->nextPhases.front() >= 0) {
949 if (myPhases[step]->nextPhases.size() > 1) {
950 WRITE_WARNINGF(TL("At actuated tlLogic '%', transition phase % should not have multiple next phases"), getID(), toString(step));
951 }
952 step = myPhases[step]->nextPhases.front();
953 } else {
954 step = (step + 1) % (int)myPhases.size();
955 }
956 if (step == origStep) {
957 WRITE_WARNING("At actuated tlLogic '" + getID() + "', infinite transition loop from phase " + toString(origStep));
958 return 0;
959 }
960 }
961 return step;
962}
963
964int
966 MSInductLoop* loop = loopInfo.loop;
967 const double actualGap = loop->getTimeSinceLastDetection();
968 if ((actualGap < loopInfo.maxGap && !loopInfo.isJammed())
969 || loopInfo.lastGreenTime < loop->getLastDetectionTime()) {
970 SUMOTime inactiveTime = MSNet::getInstance()->getCurrentTimeStep() - loopInfo.lastGreenTime;
971 // @note. Inactive time could also be tracked regardless of current activity (to increase robustness in case of detection failure
972 if (inactiveTime > myInactiveThreshold) {
973#ifdef DEBUG_PHASE_SELECTION
974 if (DEBUG_COND) {
975 std::cout << " loop=" << loop->getID() << " gap=" << loop->getTimeSinceLastDetection() << " lastGreen=" << STEPS2TIME(loopInfo.lastGreenTime)
976 << " lastDetection=" << STEPS2TIME(loop->getLastDetectionTime()) << " inactive=" << STEPS2TIME(inactiveTime) << "\n";
977 }
978#endif
979 return (int)STEPS2TIME(inactiveTime);
980 } else {
981 // give bonus to detectors that are currently served (if that phase can stil be extended)
982 if (loopInfo.servedPhase[myStep]) {
983 SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
984 const bool canExtend = actDuration < getCurrentPhaseDef().maxDuration && getLatest() > 0;
985#ifdef DEBUG_PHASE_SELECTION
986 if (DEBUG_COND) {
987 std::cout << " loop=" << loop->getID()
988 << " actDuration=" << STEPS2TIME(actDuration)
989 << " maxDur=" << STEPS2TIME(getCurrentPhaseDef().maxDuration)
990 << " getLatest=" << STEPS2TIME(getLatest())
991 << " canExtend=" << canExtend
992 << "\n";
993 }
994#endif
995 if (canExtend) {
997 } else {
998 return 0;
999 }
1000 }
1001 return 1;
1002 }
1003 }
1004 return 0;
1005}
1006
1007int
1009 int result = 0;
1010 for (const InductLoopInfo* loopInfo : myInductLoopsForPhase[step]) {
1011 result += getDetectorPriority(*loopInfo);
1012 }
1013 return result;
1014}
1015
1016
1017void
1019 myShowDetectors = show;
1020 for (InductLoopInfo& loopInfo : myInductLoops) {
1021 loopInfo.loop->setVisible(myShowDetectors);
1022 }
1023}
1024
1025
1026bool
1028 if (myLinkMaxGreenTimes.empty()) {
1029 return false;
1030 }
1031 for (int i = 0; i < myNumLinks; i++) {
1033 //std::cout << SIMTIME << " maxLinkDurationReached i=" << i << "\n";
1034 return true;
1035 }
1036 }
1037 return false;
1038}
1039
1040bool
1042 if (myLinkMaxGreenTimes.empty()) {
1043 return true;
1044 }
1045 const std::string& targetState = myPhases[target]->getState();
1046 for (int i = 0; i < myNumLinks; i++) {
1047 if (myLinkGreenTimes[i] >= myLinkMaxGreenTimes[i] && (
1048 targetState[i] == 'G' || targetState[i] == 'g')) {
1049 //std::cout << SIMTIME << " cannotExtendLinkGreen target=" << target << " i=" << i << "\n";
1050 return false;
1051 }
1052 }
1053 return true;
1054}
1055
1058 SUMOTime result = 0;
1059 if (target != myStep && myLinkMinGreenTimes.size() > 0) {
1060 const std::string& state = myPhases[myStep]->getState();
1061 const std::string& targetState = myPhases[target]->getState();
1062 for (int i = 0; i < myNumLinks; i++) {
1064 && (state[i] == 'G' || state[i] == 'g')
1065 && !(targetState[i] == 'G' || targetState[i] == 'g')) {
1066 result = MAX2(result, myLinkMinGreenTimes[i] - myLinkGreenTimes[i]);
1067 //std::cout << SIMTIME << " getLinkMinDuration myStep=" << myStep << " target=" << target << " i=" << i
1068 // << " greenTime=" << STEPS2TIME(myLinkGreenTimes[i]) << " min=" << STEPS2TIME(myLinkMinGreenTimes[i]) << " result=" << STEPS2TIME(result) << "\n";
1069 }
1070 }
1071 }
1072 return result;
1073}
1074
1075int
1077 for (int next : getCurrentPhaseDef().nextPhases) {
1078 const MSPhaseDefinition* phase = myPhases[next];
1079 const std::string& condition = mustSwitch ? phase->finalTarget : phase->earlyTarget;
1080 //std::cout << SIMTIME << " mustSwitch=" << mustSwitch << " condition=" << condition << "\n";
1081 if (condition != "") {
1082 // backward compatibility if a user redefined DEFAULT_CONDITION
1083 if (condition == DEFAULT_CONDITION && myConditions.count(DEFAULT_CONDITION) == 0) {
1084 if (gapControl() == std::numeric_limits<double>::max()) {
1085 return next;
1086 }
1087 } else if (evalExpression(condition)) {
1088 return next;
1089 }
1090 }
1091 }
1092 return mustSwitch ? getCurrentPhaseDef().nextPhases.back() : myStep;
1093}
1094
1095
1096double
1097MSActuatedTrafficLightLogic::evalExpression(const std::string& condition) const {
1098 const size_t bracketOpen = condition.find('(');
1099 if (bracketOpen != std::string::npos) {
1100 // find matching closing bracket
1101 size_t bracketClose = std::string::npos;
1102 int open = 1;
1103 for (size_t i = bracketOpen + 1; i < condition.size(); i++) {
1104 if (condition[i] == '(') {
1105 open++;
1106 } else if (condition[i] == ')') {
1107 open--;
1108 if (open == 0) {
1109 bracketClose = i;
1110 break;
1111 }
1112 }
1113 }
1114 if (bracketClose == std::string::npos) {
1115 throw ProcessError(TLF("Unmatched parentheses in condition %'", condition));
1116 }
1117 std::string cond2 = condition;
1118 const std::string inBracket = condition.substr(bracketOpen + 1, bracketClose - bracketOpen - 1);
1119 double bracketVal = evalExpression(inBracket);
1120 cond2.replace(bracketOpen, bracketClose - bracketOpen + 1, toString(bracketVal));
1121 try {
1122 return evalExpression(cond2);
1123 } catch (ProcessError& e) {
1124 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1125 }
1126 }
1127 std::vector<std::string> tokens = StringTokenizer(condition).getVector();
1128 //std::cout << SIMTIME << " tokens(" << tokens.size() << ")=" << toString(tokens) << "\n";
1129 if (tokens.size() == 0) {
1130 throw ProcessError(TLF("Invalid empty condition '%'", condition));
1131 } else if (tokens.size() == 1) {
1132 try {
1133 return evalAtomicExpression(tokens[0]);
1134 } catch (ProcessError& e) {
1135 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1136 }
1137 } else if (tokens.size() == 2) {
1138 if (tokens[0] == "not") {
1139 try {
1140 return evalAtomicExpression(tokens[1]) == 0. ? 1. : 0.;
1141 } catch (ProcessError& e) {
1142 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1143 }
1144 } else {
1145 throw ProcessError(TLF("Unsupported condition '%'", condition));
1146 }
1147 } else if (tokens.size() == 3) {
1148 // infix expression
1149 const double a = evalAtomicExpression(tokens[0]);
1150 const double b = evalAtomicExpression(tokens[2]);
1151 const std::string& o = tokens[1];
1152 //std::cout << SIMTIME << " o=" << o << " a=" << a << " b=" << b << "\n";
1153 try {
1154 return evalTernaryExpression(a, o, b, condition);
1155 } catch (ProcessError& e) {
1156 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1157 }
1158 } else {
1159 const int iEnd = (int)tokens.size() - 1;
1160 for (const std::string& o : OPERATOR_PRECEDENCE) {
1161 for (int i = 1; i < iEnd; i++) {
1162 if (tokens[i] == o) {
1163 try {
1164 const double val = evalTernaryExpression(
1165 evalAtomicExpression(tokens[i - 1]), o,
1166 evalAtomicExpression(tokens[i + 1]), condition);
1167 std::vector<std::string> newTokens(tokens.begin(), tokens.begin() + (i - 1));
1168 newTokens.push_back(toString(val));
1169 newTokens.insert(newTokens.end(), tokens.begin() + (i + 2), tokens.end());
1170 return evalExpression(toString(newTokens));
1171 } catch (ProcessError& e) {
1172 throw ProcessError("Error when evaluating expression '" + condition + "':\n " + e.what());
1173 }
1174 }
1175 }
1176 }
1177 throw ProcessError("Parsing expressions with " + toString(tokens.size()) + " elements ('" + condition + "') is not supported");
1178 }
1179 return true;
1180}
1181
1182double
1183MSActuatedTrafficLightLogic::evalTernaryExpression(double a, const std::string& o, double b, const std::string& condition) const {
1184 if (o == "=" || o == "==") {
1185 return (double)(a == b);
1186 } else if (o == "<") {
1187 return (double)(a < b);
1188 } else if (o == ">") {
1189 return (double)(a > b);
1190 } else if (o == "<=") {
1191 return (double)(a <= b);
1192 } else if (o == ">=") {
1193 return (double)(a >= b);
1194 } else if (o == "!=") {
1195 return (double)(a != b);
1196 } else if (o == "or" || o == "||") {
1197 return (double)(a || b);
1198 } else if (o == "and" || o == "&&") {
1199 return (double)(a && b);
1200 } else if (o == "+") {
1201 return a + b;
1202 } else if (o == "-") {
1203 return a - b;
1204 } else if (o == "*") {
1205 return a * b;
1206 } else if (o == "/") {
1207 if (b == 0) {
1208 WRITE_ERRORF(TL("Division by 0 in condition '%'"), condition);
1209 return 0;
1210 }
1211 return a / b;
1212 } else if (o == "%") {
1213 return fmod(a, b);
1214 } else if (o == "**" || o == "^") {
1215 return pow(a, b);
1216 } else {
1217 throw ProcessError("Unsupported operator '" + o + "' in condition '" + condition + "'");
1218 }
1219}
1220
1221double
1222MSActuatedTrafficLightLogic::evalCustomFunction(const std::string& fun, const std::string& arg) const {
1223 std::vector<std::string> args = StringTokenizer(arg, ",").getVector();
1224 const Function& f = myFunctions.find(fun)->second;
1225 if ((int)args.size() != f.nArgs) {
1226 throw ProcessError("Function '" + fun + "' requires " + toString(f.nArgs) + " arguments but " + toString(args.size()) + " were given");
1227 }
1228 std::vector<double> args2;
1229 for (auto a : args) {
1230 args2.push_back(evalExpression(a));
1231 }
1232 myStack.push_back(myStack.back());
1233 myStack.back()["$0"] = 0;
1234 for (int i = 0; i < (int)args2.size(); i++) {
1235 myStack.back()["$" + toString(i + 1)] = args2[i];
1236 }
1237 try {
1238 ConditionMap empty;
1240 } catch (ProcessError& e) {
1241 throw ProcessError("Error when evaluating function '" + fun + "' with args '" + joinToString(args2, ",") + "' (" + e.what() + ")");
1242 }
1243 double result = myStack.back()["$0"];
1244 myStack.pop_back();
1245 return result;
1246}
1247
1248
1249void
1250MSActuatedTrafficLightLogic::executeAssignments(const AssignmentMap& assignments, ConditionMap& conditions, const ConditionMap& forbidden) const {
1251 for (const auto& assignment : assignments) {
1252 if (evalExpression(std::get<1>(assignment))) {
1253 const std::string& id = std::get<0>(assignment);
1254 const double val = evalExpression(std::get<2>(assignment));
1255 ConditionMap::iterator it = conditions.find(id);
1256 if (it != conditions.end()) {
1257 it->second = toString(val);
1258 } else if (forbidden.find(id) != forbidden.end()) {
1259 throw ProcessError(TLF("Modifying global condition '%' is forbidden", id));
1260 } else {
1261 myStack.back()[id] = val;
1262 }
1263 }
1264 }
1265}
1266
1267
1268double
1270 if (expr.size() == 0) {
1271 throw ProcessError(TL("Invalid empty expression"));
1272 } else if (expr[0] == '!') {
1273 return evalAtomicExpression(expr.substr(1)) == 0. ? 1. : 0.;
1274 } else if (expr[0] == '-') {
1275 return -evalAtomicExpression(expr.substr(1));
1276 } else {
1277 // check for 'operator:'
1278 const size_t pos = expr.find(':');
1279 if (pos == std::string::npos) {
1280 auto it = myConditions.find(expr);
1281 if (it != myConditions.end()) {
1282 // symbol lookup
1283 return evalExpression(it->second);
1284 } else {
1285 // look at stack
1286 auto it2 = myStack.back().find(expr);
1287 if (it2 != myStack.back().end()) {
1288 return it2->second;
1289 }
1290 // must be a number
1291 return StringUtils::toDouble(expr);
1292 }
1293 } else {
1294 const std::string fun = expr.substr(0, pos);
1295 const std::string arg = expr.substr(pos + 1);
1296 if (fun == "z") {
1297 return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection();
1298 } else if (fun == "a") {
1299 try {
1300 return retrieveDetExpression<MSInductLoop, SUMO_TAG_INDUCTION_LOOP>(arg, expr, true)->getTimeSinceLastDetection() == 0;
1301 } catch (ProcessError&) {
1302 return retrieveDetExpression<MSE2Collector, SUMO_TAG_LANE_AREA_DETECTOR>(arg, expr, true)->getCurrentVehicleNumber();
1303 }
1304 } else if (fun == "g" || fun == "r") {
1305 try {
1306 int linkIndex = StringUtils::toInt(arg);
1307 if (linkIndex >= 0 && linkIndex < myNumLinks) {
1308 const std::vector<SUMOTime>& times = fun == "g" ? myLinkGreenTimes : myLinkRedTimes;
1309 if (times.empty()) {
1310 return 0;
1311 }
1313 // times are only updated at the start of a phase where
1314 // switching is possible (i.e. not during minDur).
1315 // If somebody is looking at those values in the tracker
1316 // this would be confusing
1317 const LinkState ls = getCurrentPhaseDef().getSignalState(linkIndex);
1318 if ((fun == "g" && (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR))
1319 || (fun == "r" && (ls == LINKSTATE_TL_RED || ls == LINKSTATE_TL_REDYELLOW))) {
1320 const SUMOTime currentGreen = SIMSTEP - myLastTrySwitchTime;
1321 return STEPS2TIME(times[linkIndex] + currentGreen);
1322 } else {
1323 return 0;
1324 }
1325 } else {
1326 return STEPS2TIME(times[linkIndex]);
1327 }
1328 }
1329 } catch (NumberFormatException&) { }
1330 throw ProcessError("Invalid link index '" + arg + "' in expression '" + expr + "'");
1331 } else if (fun == "c") {
1332 return STEPS2TIME(getTimeInCycle());
1333 } else {
1334 if (myFunctions.find(fun) == myFunctions.end()) {
1335 throw ProcessError("Unsupported function '" + fun + "' in expression '" + expr + "'");
1336 }
1337 return evalCustomFunction(fun, arg);
1338 }
1339 }
1340 }
1341}
1342
1343
1344std::map<std::string, double>
1346 std::map<std::string, double> result;
1347 for (auto li : myInductLoops) {
1348 result[li.loop->getID()] = li.loop->getOccupancy() > 0 ? 1 : 0;
1349 }
1350 for (auto loop : myExtraLoops) {
1351 result[loop->getID()] = loop->getOccupancy() > 0 ? 1 : 0;
1352 }
1353 for (auto loop : myExtraE2) {
1354 result[loop->getID()] = loop->getCurrentVehicleNumber();
1355 }
1356 return result;
1357}
1358
1359double
1360MSActuatedTrafficLightLogic::getDetectorState(const std::string laneID) const {
1361 double result = 0.0;
1362 for (auto li : myInductLoops) {
1363 if (li.lane->getID() == laneID) {
1364 result = li.loop->getOccupancy() > 0 ? 1 : 0;
1365 break;
1366 }
1367 }
1368 return result;
1369}
1370
1371std::map<std::string, double>
1373 std::map<std::string, double> result;
1374 for (auto item : myConditions) {
1375 if (myListedConditions.count(item.first) != 0) {
1376 try {
1377 result[item.first] = evalExpression(item.second);
1378 } catch (ProcessError& e) {
1379 WRITE_ERRORF(TL("Error when retrieving conditions '%' for tlLogic '%' (%)"), item.first, getID(), e.what());
1380 }
1381 }
1382 }
1383 return result;
1384}
1385
1386const std::string
1387MSActuatedTrafficLightLogic::getParameter(const std::string& key, const std::string defaultValue) const {
1388 if (StringUtils::startsWith(key, "condition.")) {
1389 const std::string cond = key.substr(10);
1390 auto it = myConditions.find(cond);
1391 if (it != myConditions.end()) {
1392 return toString(evalExpression(it->second));
1393 } else {
1394 throw InvalidArgument("Unknown condition '" + cond + "' for actuated traffic light '" + getID() + "'");
1395 }
1396 } else {
1397 return MSSimpleTrafficLightLogic::getParameter(key, defaultValue);
1398 }
1399}
1400
1401void
1402MSActuatedTrafficLightLogic::setParameter(const std::string& key, const std::string& value) {
1403 // some pre-defined parameters can be updated at runtime
1404 if (key == "detector-gap" || key == "passing-time" || key == "file" || key == "freq" || key == "vTypes"
1405 || key == "build-all-detectors"
1406 || StringUtils::startsWith(key, "linkMaxDur")
1407 || StringUtils::startsWith(key, "linkMinDur")) {
1408 throw InvalidArgument(key + " cannot be changed dynamically for actuated traffic light '" + getID() + "'");
1409 } else if (key == "max-gap") {
1411 // overwrite custom values
1412 for (InductLoopInfo& loopInfo : myInductLoops) {
1413 loopInfo.maxGap = myMaxGap;
1414 }
1415 Parameterised::setParameter(key, value);
1416 } else if (StringUtils::startsWith(key, "max-gap:")) {
1417 const std::string laneID = key.substr(8);
1418 for (InductLoopInfo& loopInfo : myInductLoops) {
1419 if (loopInfo.lane->getID() == laneID) {
1420 loopInfo.maxGap = StringUtils::toDouble(value);
1421 Parameterised::setParameter(key, value);
1422 return;
1423 }
1424 }
1425 throw InvalidArgument("Invalid lane '" + laneID + "' in key '" + key + "' for actuated traffic light '" + getID() + "'");
1426 } else if (key == "jam-threshold") {
1428 // overwrite custom values
1429 for (InductLoopInfo& loopInfo : myInductLoops) {
1430 loopInfo.jamThreshold = myJamThreshold;
1431 }
1432 Parameterised::setParameter(key, value);
1433 } else if (StringUtils::startsWith(key, "jam-threshold:")) {
1434 const std::string laneID = key.substr(14);
1435 for (InductLoopInfo& loopInfo : myInductLoops) {
1436 if (loopInfo.lane->getID() == laneID) {
1437 loopInfo.jamThreshold = StringUtils::toDouble(value);
1438 Parameterised::setParameter(key, value);
1439 return;
1440 }
1441 }
1442 throw InvalidArgument("Invalid lane '" + laneID + "' in key '" + key + "' for actuated traffic light '" + getID() + "'");
1443 } else if (key == "show-detectors") {
1445 Parameterised::setParameter(key, value);
1446 for (InductLoopInfo& loopInfo : myInductLoops) {
1447 loopInfo.loop->setVisible(myShowDetectors);
1448 }
1449 } else if (key == "inactive-threshold") {
1451 Parameterised::setParameter(key, value);
1452 } else {
1454 }
1455}
1456
1457
1458/****************************************************************************/
long long int SUMOTime
Definition GUI.h:36
#define DEFAULT_DETECTOR_GAP
#define DEFAULT_MAX_GAP
#define DEFAULT_PASSING_TIME
#define DEFAULT_BIKE_LENGTH_WITH_GAP
#define DEFAULT_LENGTH_WITH_GAP
#define NO_DETECTOR
#define DEFAULT_INACTIVE_THRESHOLD
#define DEFAULT_CONDITION
#define DEFAULT_CURRENT_PRIORITY
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:296
#define WRITE_ERRORF(...)
Definition MsgHandler.h:305
#define WRITE_WARNING(msg)
Definition MsgHandler.h:295
#define TL(string)
Definition MsgHandler.h:315
#define TLF(string,...)
Definition MsgHandler.h:317
SUMOTime string2time(const std::string &r)
convert string to SUMOTime
Definition SUMOTime.cpp:46
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition SUMOTime.cpp:69
#define STEPS2TIME(x)
Definition SUMOTime.h:55
#define SIMSTEP
Definition SUMOTime.h:61
#define SIMTIME
Definition SUMOTime.h:62
#define TIME2STEPS(x)
Definition SUMOTime.h:57
bool noVehicles(SVCPermissions permissions)
Returns whether an edge with the given permissions forbids vehicles.
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
const double DEFAULT_BICYCLE_SPEED
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_PEDESTRIAN
pedestrian
@ SUMO_TAG_INDUCTION_LOOP
alternative tag for e1 detector
LinkState
The right-of-way state of a link between two lanes used when constructing a NBTrafficLightLogic,...
@ LINKSTATE_TL_REDYELLOW
The link has red light (must brake) but indicates upcoming green.
@ LINKSTATE_TL_GREEN_MAJOR
The link has green light, may pass.
@ LINKSTATE_TL_RED
The link has red light (must brake)
@ LINKSTATE_TL_GREEN_MINOR
The link has green light, has to brake.
T MIN3(T a, T b, T c)
Definition StdDefs.h:89
T MIN2(T a, T b)
Definition StdDefs.h:76
T MAX2(T a, T b)
Definition StdDefs.h:82
T MAX3(T a, T b, T c)
Definition StdDefs.h:96
std::string joinNamedToString(const std::set< T *, C > &ns, const T_BETWEEN &between)
Definition ToString.h:317
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition ToString.h:283
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
static std::string checkForRelativity(const std::string &filename, const std::string &basePath)
Returns the path from a configuration so that it is accessable from the current working directory.
double myDetectorGap
The detector distance in seconds.
FunctionMap myFunctions
The loaded functions.
double myJamThreshold
The minimum continuous occupancy time to mark a detector as jammed.
bool myBuildAllDetectors
Whether all detectors shall be built.
double myMaxGap
The maximum gap to check in seconds.
const std::string getParameter(const std::string &key, const std::string defaultValue="") const override
try to get the value of the given parameter (including prefixed parameters)
std::vector< SwitchingRules > mySwitchingRules
std::vector< std::map< std::string, double > > myStack
The function call stack;.
double evalAtomicExpression(const std::string &expr) const
evaluate atomic expression
int getTarget(int step)
get the green phase following step
SUMOTime trySwitch() override
Switches to the next phase.
SUMOTime myLastTrySwitchTime
last time trySwitch was called
int getDetectorPriority(const InductLoopInfo &loopInfo) const
SUMOTime myFreq
The frequency for aggregating detector output.
SUMOTime getMinimumMinDuration(MSLane *lane) const
get the minimum min duration for all stretchable phases that affect the given lane
std::vector< const MSInductLoop * > myExtraLoops
extra loops for output/tracking
bool myShowDetectors
Whether the detectors shall be shown in the GUI.
std::vector< SUMOTime > myLinkMaxGreenTimes
maximum consecutive time that the given link may remain green
MSActuatedTrafficLightLogic(MSTLLogicControl &tlcontrol, const std::string &id, const std::string &programID, const SUMOTime offset, const MSSimpleTrafficLightLogic::Phases &phases, int step, SUMOTime delay, const Parameterised::Map &parameter, const std::string &basePath, const ConditionMap &conditions=ConditionMap(), const AssignmentMap &assignments=AssignmentMap(), const FunctionMap &functions=FunctionMap())
Constructor.
void loadState(MSTLLogicControl &tlcontrol, SUMOTime t, int step, SUMOTime spentDuration) override
restores the tls state
SUMOTime getMaxDur(int step=-1) const override
AssignmentMap myAssignments
The condition assignments.
std::string myVehicleTypes
Whether detector output separates by vType.
double gapControl()
Return the minimum detection gap of all detectors if the current phase should be extended and double:...
std::vector< std::tuple< std::string, std::string, std::string > > AssignmentMap
std::set< std::string > myListedConditions
the conditions which shall be listed in GUITLLogicPhasesTrackerWindow
double evalExpression(const std::string &condition) const
evaluate custom switching condition
std::vector< SUMOTime > myLinkMinGreenTimes
minimum consecutive time that the given link must remain green
void changeStepAndDuration(MSTLLogicControl &tlcontrol, SUMOTime simStep, int step, SUMOTime stepDuration) override
Changes the current phase and her duration.
bool weakConflict(int linkIndex, const std::string &state) const
whether a given link has only weak mode foes that are green in the given state
static const std::vector< std::string > OPERATOR_PRECEDENCE
bool myHasMultiTarget
Whether any of the phases has multiple targets.
double myPassingTime
The passing time used in seconds.
SUMOTime getLinkMinDuration(int target) const
the minimum duratin for keeping the current phase due to linkMinDur constraints
SUMOTime getMinDur(int step=-1) const override
bool canExtendLinkGreen(int target)
whether the target phase is acceptable in light of linkMaxDur constraints
InductLoopMap myInductLoopsForPhase
A map from phase to induction loops to be used for gap control.
int decideNextPhaseCustom(bool mustSwitch)
select among candidate phases based on detector states and custom switching rules
double evalTernaryExpression(double a, const std::string &o, double b, const std::string &condition) const
evaluate atomic expression
void executeAssignments(const AssignmentMap &assignments, ConditionMap &conditions, const ConditionMap &forbidden=ConditionMap()) const
execute assignemnts of the logic or a custom function
bool myTraCISwitch
whether the next switch time was requested via TraCI
int getPhasePriority(int step) const
count the number of active detectors for the given step
SUMOTime duration(const double detectionGap) const
Returns the minimum duration of the current phase.
void activateProgram() override
called when switching programs
std::vector< InductLoopInfo > myInductLoops
bool maxLinkDurationReached()
whether the current phase cannot be continued due to linkMaxDur constraints
double evalCustomFunction(const std::string &fun, const std::string &arg) const
evaluate function expression
void initAttributeOverride()
initialize custom switching rules
std::map< std::string, double > getConditions() const override
return all named conditions defined for this traffic light
bool hasMajor(const std::string &state, const LaneVector &lanes) const
return whether there is a major link from the given lane in the given phase
std::vector< const MSE2Collector * > myExtraE2
SUMOTime getEarliestEnd(int step=-1) const override
std::map< std::string, Function > FunctionMap
std::map< std::string, double > getDetectorStates() const override
retrieve all detectors used by this program
double getDetectorState(const std::string laneID) const override
retrieve a specific detector used by this program
void setParameter(const std::string &key, const std::string &value) override
Sets a parameter and updates internal constants.
std::vector< SUMOTime > myLinkGreenTimes
consecutive time that the given link index has been green
SUMOTime getLatestEnd(int step=-1) const override
std::string myFile
The output file for generated detectors.
ConditionMap myConditions
The custom switching conditions.
void init(NLDetectorBuilder &nb) override
Initialises the tls with information about incoming lanes.
int decideNextPhase()
select among candidate phases based on detector states
SUMOTime myInactiveThreshold
The time threshold to avoid starved phases.
const NamedObjectCont< MSDetectorFileOutput * > & getTypedDetectors(SumoXMLTag type) const
Returns the list of detectors of the given type.
void add(SumoXMLTag type, MSDetectorFileOutput *d, const std::string &device, SUMOTime interval, SUMOTime begin=-1)
Adds a detector/output combination into the containers.
A road/street connecting two junctions.
Definition MSEdge.h:77
double getSpeedLimit() const
Returns the speed limit of the edge @caution The speed limit of the first lane is retured; should pro...
Definition MSEdge.cpp:1158
virtual void addEvent(Command *operation, SUMOTime execTimeStep=-1)
Adds an Event.
static bool gUseMesoSim
Definition MSGlobals.h:106
An unextended detector measuring at a fixed position on a fixed lane.
double getPosition() const
Returns the position of the detector on the lane.
virtual void setSpecialColor(const RGBColor *)
allows for special color in the gui version
double getTimeSinceLastDetection() const
Returns the time since the last vehicle left the detector.
SUMOTime getLastDetectionTime() const
return last time a vehicle was on the detector
The base class for an intersection.
Definition MSJunction.h:58
virtual const MSJunctionLogic * getLogic() const
Definition MSJunction.h:141
virtual const MSLogicJunction::LinkBits & getFoesFor(int linkIndex) const
Returns the foes for the given link.
Representation of a lane in the micro simulation.
Definition MSLane.h:84
const std::vector< IncomingLaneInfo > & getIncomingLanes() const
Definition MSLane.h:950
double getLength() const
Returns the lane's length.
Definition MSLane.h:606
MSLane * getLogicalPredecessorLane() const
get the most likely precedecessor lane (sorted using by_connections_to_sorter). The result is cached ...
Definition MSLane.cpp:3135
const std::vector< MSLink * > & getLinkCont() const
returns the container with all links !!!
Definition MSLane.h:724
MSDetectorControl & getDetectorControl()
Returns the detector control.
Definition MSNet.h:441
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition MSNet.cpp:185
MSEventControl * getBeginOfTimestepEvents()
Returns the event control for events executed at the begin of a time step.
Definition MSNet.h:471
SUMOTime getCurrentTimeStep() const
Returns the current simulation step.
Definition MSNet.h:320
The definition of a single phase of a tls logic.
SUMOTime maxDuration
The maximum duration of the phase.
LinkState getSignalState(int pos) const
Returns the state of the tls signal at the given position.
static const SUMOTime OVERRIDE_DURATION
SUMOTime latestEnd
The maximum time within the cycle for switching (for coordinated actuation)
SUMOTime minDuration
The minimum duration of the phase.
const std::string & getState() const
Returns the state within this phase.
bool isGreenPhase() const
Returns whether this phase is a pure "green" phase.
std::vector< int > nextPhases
The index of the phase that suceeds this one (or -1)
std::string finalTarget
The condition expression for switching into this phase when the active phase must end.
std::string earlyTarget
The condition expression for an early switch into this phase.
SUMOTime earliestEnd
The minimum time within the cycle for switching (for coordinated actuation)
A fixed traffic light logic.
SUMOTime getLatest() const
the maximum duration for keeping the current phase when considering 'latestEnd'
Phases myPhases
The list of phases this logic uses.
const MSPhaseDefinition & getPhase(int givenstep) const override
Returns the definition of the phase from the given position within the plan.
SUMOTime getEarliest(SUMOTime prevStart) const
the minimum duration for keeping the current phase when considering 'earliestEnd'
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const override
gets a parameter
virtual void setParameter(const std::string &key, const std::string &value) override
Sets a parameter and updates internal constants.
const MSPhaseDefinition & getCurrentPhaseDef() const override
Returns the definition of the current phase.
A class that stores and controls tls and switching of their programs.
TLSLogicVariants & get(const std::string &id) const
Returns the variants of a named tls.
Class realising the switch between the traffic light phases.
void deschedule(MSTrafficLightLogic *tlLogic)
Marks this swicth as invalid (if the phase duration has changed, f.e.)
const LaneVector & getLanesAt(int i) const
Returns the list of lanes that are controlled by the signals at the given position.
std::vector< MSLane * > LaneVector
Definition of the list of arrival lanes subjected to this tls.
SUMOTime getTimeInCycle() const
return time within the current cycle
const std::string & getProgramID() const
Returns this tl-logic's id.
LaneVectorVector myLanes
The list of LaneVectors; each vector contains the incoming lanes that belong to the same link index.
SwitchCommand * mySwitchCommand
The current switch command.
int myNumLinks
number of controlled links
virtual void activateProgram()
called when switching programs
bool setTrafficLightSignals(SUMOTime t) const
Applies the current signal states to controlled links.
std::vector< MSPhaseDefinition * > Phases
Definition of a list of phases, being the junction logic.
const LinkVector & getLinksAt(int i) const
Returns the list of links that are controlled by the signals at the given position.
LinkVectorVector myLinks
The list of LinkVectors; each vector contains the links that belong to the same link index.
virtual void init(NLDetectorBuilder &nb)
Initialises the tls with information about incoming lanes.
Builds detectors for microsim.
virtual MSDetectorFileOutput * createInductLoop(const std::string &id, MSLane *lane, double pos, double length, const std::string name, const std::string &vTypes, const std::string &nextEdges, int detectPersons, bool show)
Creates an instance of an e1 detector using the given values.
const std::string & getID() const
Returns the id.
Definition Named.h:74
T get(const std::string &id) const
Retrieves an item.
static OptionsCont & getOptions()
Retrieves the options.
bool hasParameter(const std::string &key) const
Returns whether the parameter is set.
std::map< std::string, std::string > Map
parameters map
double getDouble(const std::string &key, const double defaultValue) const
Returns the value for a given key converted to a double.
const Parameterised::Map & getParametersMap() const
Returns the inner key/value map.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
static const RGBColor ORANGE
Definition RGBColor.h:191
static const RGBColor GREEN
Definition RGBColor.h:186
static const RGBColor RED
named colors
Definition RGBColor.h:185
std::vector< std::string > getVector()
return vector of strings
static double toDouble(const std::string &sData)
converts a string into the double value described by it by calling the char-type converter
static bool startsWith(const std::string &str, const std::string prefix)
Checks whether a given string starts with the prefix.
static int toInt(const std::string &sData)
converts a string into the integer value described by it by calling the char-type converter,...
static bool toBool(const std::string &sData)
converts a string into the bool value described by it by calling the char-type converter
#define DEBUG_COND