Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
NEMAController.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-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/****************************************************************************/
20// An actuated NEMA-phase-compliant traffic light logic
21/****************************************************************************/
22#include <config.h>
23
24#include <cassert>
25#include <utility>
26#include <vector>
27#include <bitset>
28#include <sstream>
29#include <iostream>
34#include <microsim/MSGlobals.h>
35#include <microsim/MSNet.h>
36#include <microsim/MSLane.h>
37#include <microsim/MSEdge.h>
42#include "NEMAController.h"
43
44
45// ===========================================================================
46// parameter defaults definitions
47// ===========================================================================
48#define INVALID_POSITION std::numeric_limits<double>::max() // tl added
49
50// #define DEBUG_NEMA
51// #define FUZZ_TESTING
52// #define DEBUG_NEMA_SWITCH
53
54// ===========================================================================
55// method definitions
56// ===========================================================================
58 const std::string& id, const std::string& programID,
59 const SUMOTime _offset,
60 const Phases& phases,
61 int step, SUMOTime delay,
62 const std::map<std::string, std::string>& parameter,
63 const std::string& basePath) :
64 MSSimpleTrafficLightLogic(tlcontrol, id, programID, _offset, TrafficLightType::NEMA, phases, step, delay, parameter),
65 myPhase(phases[0]->duration, phases[0]->getState()) {
66 myDetectorLength = StringUtils::toDouble(getParameter("detector-length", "20"));
67 myDetectorLengthLeftTurnLane = StringUtils::toDouble(getParameter("detector-length-leftTurnLane", "20"));
71 myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
72 myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
74 myVehicleTypes = getParameter("vTypes", "");
75 myControllerType = parseControllerType(getParameter("controllerType", "TS2"));
76 ignoreErrors = StringUtils::toBool(getParameter("ignore-errors", "false"));
77 // This should be extended in the future.
78 myNumberRings = 2;
79}
80
82 // delete the phase objects
83 for (auto p : myPhaseObjs) {
84 delete p;
85 }
86}
87
88void
89NEMALogic::constructTimingAndPhaseDefs(std::string& barriers, std::string& coordinates, std::string& ring1, std::string& ring2) {
90
91 // read in the barrier and coordinated phases from the XML
92 std::vector<int> barrierPhases = readParaFromString(barriers);
93 std::vector<int> coordinatePhases = readParaFromString(coordinates);
94
95 // create a {{}, {}} vector of phases
96 rings.push_back(readParaFromString(ring1));
97 rings.push_back(readParaFromString(ring2));
98
99#ifdef DEBUG_NEMA
100 //print to check
101 for (int i = 0; i < (int)rings.size(); i++) {
102 int count = 0;
103 std::cout << "Ring" << i + 1 << " includes phases: \t";
104 for (auto j : rings[i]) {
105 count++;
106 std::cout << j << " ";
107 if (count == 2 || count == 4) {
108 std::cout << " | ";
109 }
110 }
111 std::cout << std::endl;
112 }
113#endif
114
115 // load the recalls, if they exist
116 std::vector<int> VecMinRecall = readParaFromString(getParameter("minRecall", "1,2,3,4,5,6,7,8"));
117 std::vector<int> VecMaxRecall = readParaFromString(getParameter("maxRecall", ""));
118
119#ifdef DEBUG_NEMA
120 std::cout << "minRecall: ";
121 for (int i = 0; i < 8; i++) {
122 std::cout << vectorContainsPhase(VecMinRecall, i + 1) << '\t';
123 }
124 std::cout << std::endl;
125
126 std::cout << "maxRecall: ";
127 for (int i = 0; i < 8; i++) {
128 std::cout << vectorContainsPhase(VecMaxRecall, i + 1) << '\t';
129 }
130 std::cout << std::endl;
131#endif
132
133 // loop through the rings and construct NEMAPhases.
134 // This relies on the phase being in order in the rings parameter in the configuration file
135 int ringNum = 0;
136 int lastPhaseIter = 0;
137 int phaseIter = 0;
138 for (const auto& r : rings) {
139 int ringIter = 0;
140 lastPhaseIter = phaseIter;
141 phaseIter = 0;
142 for (const auto& p : r) {
143 if (p != 0) {
144 // find the phase definition matching the phase integer
145 MSPhaseDefinition* tempPhase = nullptr;
146 for (const auto& pDef : myPhases) {
147 if (string2int(pDef->getName()) == p) {
148 tempPhase = pDef;
149 break;
150 }
151 }
152 // there must be a matching MSPhaseDefinition
153 if (tempPhase == nullptr) {
154 throw ProcessError("At traffic signal '" + myID + "' program '" + myProgramID + "' no phase named '" + toString(p) + "' was found");
155 }
156
157 // create lane specific objects
158 std::string state = tempPhase->getState();
159
160 // check that all phases have the same length. myPhaseStrLen is initially set to -1.
161 if (myPhaseStrLen < 0) {
162 myPhaseStrLen = (int)state.size();
163 } else if (myPhaseStrLen != (int)state.size()) {
164 throw ProcessError(TLF("At NEMA tlLogic '%', different sizes of NEMA phase states. Please check the NEMA XML", getID()));
165 }
166
167 // get the lane-based info
168 StringVector laneIDs;
169 IntVector controlledStateIndexes;
170 getLaneInfoFromNEMAState(state, laneIDs, controlledStateIndexes);
171
172 std::vector<std::string> laneIDs_vector;
173 for (std::string laneID : laneIDs) {
174 laneIDs_vector.push_back(laneID);
175 myLanePhaseMap[laneID] = p;
176 }
177 phase2ControllerLanesMap[p] = laneIDs_vector;
178
179 // Create the Phase Object
180 // find if it is at a barrier
181 bool barrierPhase = vectorContainsPhase(barrierPhases, p) || vectorContainsPhase(coordinatePhases, p);
182 // is it a coordinate phase
183 bool coordinatePhase = vectorContainsPhase(coordinatePhases, p) && coordinateMode;
184 // is there a minimum or max recall
185 bool minRecall = vectorContainsPhase(VecMinRecall, p);
186 bool maxRecall = vectorContainsPhase(VecMaxRecall, p);
187 // A phase can "green rest" only if it has a recall and no other phases on that ring do OR if NO phases have a recall (unique case)
188 bool phaseGreenRest = ((VecMaxRecall.size() + VecMinRecall.size()) < 1);
189 if (!phaseGreenRest) {
190 bool recallActive = minRecall || maxRecall;
191 if (recallActive) {
192 for (const auto& pO : r) {
193 if (pO != p) {
194 if (vectorContainsPhase(VecMinRecall, pO)
195 || vectorContainsPhase(VecMaxRecall, pO)) {
196 recallActive = false;
197 break;
198 }
199 }
200 }
201 // only set the green rest to true if I am the only phase on my ring with a recall
202 phaseGreenRest = recallActive;
203 }
204 }
205 // could add per-phase fixforceoff here
206 // barrierNum is either 0 or 1, depending on mainline side or sidestreet
207 int barrierNum = ringIter / 2;
208
209 // now ready to create the phase
210 myPhaseObjs.push_back(
211 new NEMAPhase(p, barrierPhase, phaseGreenRest, coordinatePhase, minRecall, maxRecall, fixForceOff, barrierNum, ringNum, controlledStateIndexes, tempPhase)
212 );
213
214 // Add a reference to the sequentially prior phase
215 if (phaseIter > 0) {
216 myPhaseObjs.back()->setSequentialPriorPhase(myPhaseObjs[lastPhaseIter + (phaseIter - 1)]);
217 }
218 phaseIter++;
219 }
220 ringIter++;
221 }
222 if (lastPhaseIter >= (int)myPhaseObjs.size()) {
223 throw ProcessError("At traffic signal '" + myID + "', ring " + toString(ringNum + 1) + " contains only '0'");
224 }
225 // Set the first to point to the last, wrapping around the ring.
226 myPhaseObjs[lastPhaseIter]->setSequentialPriorPhase(myPhaseObjs[lastPhaseIter + phaseIter - 1]);
227 // index the ring counter
228 ringNum++;
229 }
230
231 //TODO: set the default phases. This could also be set using dual entry in future
232 for (int i = 0; i < 2; i++) {
233 // create the coordinate phase ptr
234 coordinatePhaseObjs[i] = getPhaseObj(coordinatePhases[i], i);
236 // create the other barrier phase ptr
237 PhasePtr b = getPhaseObj(barrierPhases[i], i);
239 // the barrier 1 and barrier 0 default phase must not have the same barrier number
241 throw ProcessError("At traffic signal " + myID + " the barrier and coordinated phases " +
242 std::to_string(b->phaseName) + ", " + std::to_string(coordinatePhaseObjs[i]->barrierNum) +
243 " are located on the same side of a barrier." +
244 " Please check your configuration file");
245 }
246 }
247
248 // Create the PhaseDetectorInfo for each of the phases (needs knowledge of other phases to create)
249 IntVector latchingDetectors = readParaFromString(getParameter("latchingDetectors", ""));
250 std::vector<std::pair<int, int>> cp;
251 for (auto& p : myPhaseObjs) {
252 std::string cps = "crossPhaseSwitching:";
253 int crossPhase = StringUtils::toInt(getParameter(cps.append(std::to_string(p->phaseName)), "0"));
254 if (crossPhase > 0) {
255 cp.push_back({ p->phaseName, crossPhase });
256 }
257 }
258
259 // Knowing the cross phase info, we can add that to the phase
260 for (auto& p : myPhaseObjs) {
261 bool latching = vectorContainsPhase(latchingDetectors, p->phaseName);
262 int cpTarget = 0;
263 int cpSource = 0;
264 for (auto& cp_pair : cp) {
265 if (cp_pair.first == p->phaseName || cp_pair.second == p->phaseName) {
266 cpTarget = cp_pair.first;
267 cpSource = cp_pair.second;
268 }
269 }
270 p->init(this, cpTarget, cpSource, latching);
271 }
272
273 // Calculate Force offs Based on Timing
275
276 if (coordinateMode) {
277 // Calculate the Initial Phases in coordinated operation only.
278 // Otherwise they have already been calculated above
280 } else {
281 // Fall back being the barrier 0 default phases
282 // NEMAPhase* defaultP[2] = {defaultBarrierPhases[0][0], defaultBarrierPhases[1][0]};
283 NEMAPhase* defaultP[2] = { getPhasesByRing(0).front(), getPhasesByRing(1).front() };
284 defaultP[0]->forceEnter(this);
285 defaultP[1]->forceEnter(this);
286 }
287
288
289#ifdef DEBUG_NEMA
290 //print to check the rings and barriers active phase
291 std::cout << "After init, active ring1 phase is " << myActivePhaseObjs[0]->phaseName << std::endl;
292 std::cout << "After init, active ring2 phase is " << myActivePhaseObjs[1]->phaseName << std::endl;
293
294
295 //print to check the phase definition is correct
296 std::cout << "Print to check NEMA phase definitions\n";
297 for (auto& p : myPhaseObjs) {
298 std::cout << "index = " << p->phaseName << "; ";
299 std::cout << "minDur = " << std::to_string(p->minDuration) << "; ";
300 std::cout << "maxDur = " << std::to_string(p->maxDuration) << "; ";
301 std::cout << "vehext = " << std::to_string(p->vehExt) << "; ";
302 std::cout << "yellow = " << std::to_string(p->yellow) << "; ";
303 std::cout << "red = " << std::to_string(p->red) << "; ";
304 std::cout << "state = " << std::to_string((int)p->getCurrentState()) << std::endl;
305 }
306#endif
307
308
309#ifdef DEBUG_NEMA
310 std::cout << "After init, r1/r2 barrier phase = " << defaultBarrierPhases[0][1]->phaseName << " and " << defaultBarrierPhases[1][1]->phaseName << std::endl;
311 std::cout << "After init, r1/r2 coordinate phase = " << defaultBarrierPhases[0][0]->phaseName << " and " << defaultBarrierPhases[1][0]->phaseName << std::endl;
312#endif
313
314
315#ifdef DEBUG_NEMA
316 std::cout << "R1State = " << myActivePhaseObjs[0]->phaseName << " and its state = " << std::to_string((int)myActivePhaseObjs[0]->getCurrentState()) << std::endl;
317 std::cout << "R2State = " << myActivePhaseObjs[1]->phaseName << " and its state = " << std::to_string((int)myActivePhaseObjs[0]->getCurrentState()) << std::endl;
318#endif
319
320 // Set the initial light state
322 myPhase.setName(toString(myActivePhaseObjs[0]->phaseName) + "+" + toString(myActivePhaseObjs[1]->phaseName));
324 myStep = 0;
325
326 //validating timing
328}
329
330bool
331NEMALogic::vectorContainsPhase(std::vector<int> v, int phaseNum) {
332 if (std::find(v.begin(), v.end(), phaseNum) != v.end()) {
333 return true;
334 }
335 return false;
336}
337
338void
340
341 // TODO: Create a parameter for this
343
344 std::string barriers = getParameter("barrierPhases", "");
345 std::string coordinates = getParameter("coordinatePhases", getParameter("barrier2Phases", ""));
346 std::string ring1 = getParameter("ring1", "");
347 std::string ring2 = getParameter("ring2", "");
348
349 fixForceOff = StringUtils::toBool(getParameter("fixForceOff", "false"));
352 whetherOutputState = StringUtils::toBool(getParameter("whetherOutputState", "false"));
353 coordinateMode = StringUtils::toBool(getParameter("coordinate-mode", "false"));
354
355 // set the queued traci changes to false
356 queuedTraciChanges = false;
357
358 //missing parameter error
359 error_handle_not_set(ring1, "ring1");
360 error_handle_not_set(ring2, "ring2");
361 error_handle_not_set(barriers, "barrierPhases");
362 error_handle_not_set(coordinates, "barrier2Phases or coordinatePhases");
363
364 //print to check
365#ifdef DEBUG_NEMA
366 std::cout << "JunctionID = " << myID << std::endl;
367 std::cout << "All parameters after calling constructor are: " << std::endl;
368 std::cout << "myDetectorLength = " << myDetectorLength << std::endl;
369 std::cout << "cycleLength = " << STEPS2TIME(myCycleLength) << std::endl;
370 std::cout << "ring1 = " << ring1 << std::endl;
371 std::cout << "ring2 = " << ring2 << std::endl;
372 std::cout << "barriers = " << barriers << std::endl;
373 std::cout << "coordinates = " << coordinates << std::endl;
374 std::cout << "offset = " << offset << std::endl;
375 std::cout << "cycleSecond = " << getTimeInCycle() << std::endl;
376 std::cout << "whetherOutputState = " << whetherOutputState << std::endl;
377 std::cout << "myShowDetectors = " << myShowDetectors << std::endl;
378 std::cout << "coordinateMode = " << coordinateMode << std::endl;
379 std::cout << "fixForceOff = " << fixForceOff << std::endl;
380 std::cout << "You reach the end of constructor" << std::endl;
381 std::cout << "****************************************\n";
382#endif
383 // Construct the NEMA specific timing data types and initial phases
384 constructTimingAndPhaseDefs(barriers, coordinates, ring1, ring2);
385
386 //init the traffic light
388 assert(myLanes.size() > 0);
389 //iterate through the lanes and build one E2 detector for each lane associated with the traffic light control junction
390 for (const LaneVector& lanes : myLanes) {
391 for (MSLane* const lane : lanes) {
392 //decide the detector length
393 double detector_length = 0;
394 if (isLeftTurnLane(lane)) {
395 detector_length = myDetectorLengthLeftTurnLane;
396 } else {
397 detector_length = myDetectorLength;
398 }
399 if (noVehicles(lane->getPermissions())) {
400 // do not build detectors on green verges or sidewalks
401 continue;
402 }
403 // Build detector and register them in the detector control
404 if (myLaneDetectorMap.find(lane) == myLaneDetectorMap.end()) {
405 MSE2Collector* det = nullptr;
406 const std::string customID = getParameter(lane->getID());
407 if (customID != "") {
409 if (det == nullptr) {
410 throw ProcessError("Unknown laneAreaDetector '" + customID + "' given as custom detector for NEMA tlLogic '" + getID() + "', program '" + getProgramID() + ".");
411 }
412 //set the detector to be visible in gui
414 } else {
415 int phaseNumber = 0;
416 if (myLanePhaseMap.find(lane->getID()) != myLanePhaseMap.end()) {
417 phaseNumber = myLanePhaseMap.find(lane->getID())->second;
418 }
419 int index = lane->getIndex();
420 std::string id = myID + "_" + myProgramID + "_D" + toString(phaseNumber) + "." + toString(index);
421 while (MSNet::getInstance()->getDetectorControl().getTypedDetectors(SUMO_TAG_LANE_AREA_DETECTOR).get(id) != nullptr) {
422 index++;
423 id = myID + "_" + myProgramID + "_D" + toString(phaseNumber) + "." + toString(index);
424 }
425 //createE2Detector() method will lead to bad detector showing in sumo-gui
426 //so it is better to use build2Detector() rather than createE2Detector()
427 nb.buildE2Detector(id, //detectorID
428 lane, //lane to build this detector
429 INVALID_POSITION, // set the detector location by end point and length, so this one is set to invalue value so this parameter can be passed
430 lane->getLength(), // set the end position of the detector at the end of the lane, which is right at the position of stop bar of a junction
431 detector_length, //detector length
432 myFile, // detector information output file
433 myFreq, // detector reading interval
434 0, // time-based threshold that describes how much time has to pass until a vehicle is considered as halting
435 0, // speed threshold as halting
436 0, // minimum dist to the next standing vehicle to make this vehicle count as a participant to the jam
437 "",
438 myVehicleTypes, //vehicle types to consider, if it is empty, meaning consider all types of vehicles
439 "", // nextEdges (no filtering by vehicle route)
440 (int)PersonMode::NONE, // detector vehicles, not persons
441 true, // whether to give some slack on positioning
442 myShowDetectors, // whether to show detectors in sumo-gui
443 nullptr, //traffic light that triggers aggregation when switching
444 nullptr); // outgoing lane that associated with the traffic light
445
446 //get the detector to be used in the lane detector map loading
448 }
449
450 //map the detector to lane and lane to detector
451 myLaneDetectorMap[lane] = det;
452 myDetectorLaneMap[det] = lane;
453 myDetectorInfoVector.push_back(DetectorInfo(det, (int)myPhases.size()));
454
455 }
456 }
457 }
458 for (auto item : phase2ControllerLanesMap) {
459 int NEMAPhaseIndex = item.first;
460 std::vector<std::string> laneIDs = item.second;
461 std::vector<MSE2Collector*> detectors;
462 MSE2Collector* detector = nullptr;
463 for (std::string laneID : laneIDs) {
464 MSLane* lane = MSLane::dictionary(laneID);
465 detector = myLaneDetectorMap[lane];
466 detectors.push_back(detector);
467 }
468 // have to try this on both rings, because of the case where both rings have the same phase
469 // See Basic NEMA test
470 for (int i = 0; i < 2; i++) {
471 if (vectorContainsPhase(rings[i], NEMAPhaseIndex)) {
472 getPhaseObj(NEMAPhaseIndex, i)->setDetectors(detectors);
473 }
474 }
475 }
476
477 //Do not delete. SUMO traffic logic check.
478 //SUMO check begin
479 const SVCPermissions motorized = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
480 std::map<int, std::set<MSE2Collector*>> linkToDetectors;
481 std::set<int> actuatedLinks;
482
483 const int numLinks = (int)myLinks.size();
484 std::vector<bool> neverMajor(numLinks, true);
485 for (const MSPhaseDefinition* phase : myPhases) {
486 const std::string& state = phase->getState();
487 for (int i = 0; i < numLinks; i++) {
488 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
489 neverMajor[i] = false;
490 }
491 }
492 }
493 std::vector<bool> oneLane(numLinks, false);
494 for (int i = 0; i < numLinks; i++) {
495 for (MSLane* lane : getLanesAt(i)) {
496 int numMotorized = 0;
497 for (MSLane* l : lane->getEdge().getLanes()) {
498 if ((l->getPermissions() & motorized) != 0) {
499 numMotorized++;
500 }
501 }
502 if (numMotorized == 1) {
503 oneLane[i] = true;
504 break;
505 }
506 }
507 }
508
509 for (const MSPhaseDefinition* phase : myPhases) {
510 const int phaseIndex = (int)myDetectorForPhase.size();
511 std::set<MSE2Collector*> detectors;
512 if (phase->isActuated()) {
513 const std::string& state = phase->getState();
514 std::set<int> greenLinks;
515 std::map<MSE2Collector*, std::set<int>> detectorLinks;
516
517 for (int i = 0; i < numLinks; i++) {
518 if (state[i] == LINKSTATE_TL_GREEN_MAJOR
519 || (state[i] == LINKSTATE_TL_GREEN_MINOR
520 && ((neverMajor[i] // check1a
521 && hasMajor(state, getLanesAt(i))) // check1b
522 || oneLane[i])) // check1c
523 ) {
524 greenLinks.insert(i);
525 actuatedLinks.insert(i);
526 }
527
528 for (MSLane* lane : getLanesAt(i)) {
529 if (myLaneDetectorMap.count(lane) != 0) {
530 detectorLinks[myLaneDetectorMap[lane]].insert(i);
531 }
532 }
533 }
534 for (auto& item : detectorLinks) {
535 MSE2Collector* det = item.first;
536 MSLane* detectorLane = myDetectorLaneMap[det];
537 bool usable = true;
538 // check 1
539 for (int j : item.second) {
540 if (greenLinks.count(j) == 0) {
541 usable = false;
542 }
543 }
544
545 //check 2
546 if (usable) {
547 for (MSLink* link : detectorLane->getLinkCont()) {
548 MSLane* next = link->getLane();
549 if (myLaneDetectorMap.count(next) != 0) {
550 MSE2Collector* nextDet = myLaneDetectorMap[next];
551 for (int j : detectorLinks[nextDet]) {
552 if (greenLinks.count(j) == 0) {
553 usable = false;
554 break;
555 }
556 }
557 }
558 }
559 }
560
561 if (usable) {
562 detectors.insert(item.first);
563 for (int j : item.second) {
564 linkToDetectors[j].insert(item.first);
565 }
566 }
567 }
568 if (detectors.size() == 0) {
569 WRITE_WARNINGF(TL("At NEMA tlLogic '%', actuated phase % has no controlling detector"), getID(), toString(phaseIndex));
570 }
571 }
572 std::vector<DetectorInfo*> detectorInfos;
573 myDetectorForPhase.push_back(detectorInfos);
574 for (MSE2Collector* det : detectors) {
575 for (DetectorInfo& detInfo : myDetectorInfoVector) {
576 if (detInfo.det == det) {
577 myDetectorForPhase.back().push_back(&detInfo);
578 detInfo.servedPhase[phaseIndex] = true;
579 }
580 }
581 }
582 }
583
584 for (int i : actuatedLinks) {
585 if (linkToDetectors[i].size() == 0 && myLinks[i].size() > 0
586 && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
587 WRITE_WARNINGF(TL("At NEMA tlLogic '%, linkIndex % has no controlling detector"), getID(), toString(i));
588 }
589 }
590}
591
592void
594 // check that the cycle length for each ring adds up to the specified cycle length
595 for (int ringIndex = 0; ringIndex < 2; ringIndex++) {
596 SUMOTime cycleLengthCalculated = 0;
597 for (auto& p : getPhasesByRing(ringIndex)) {
598 cycleLengthCalculated += (p->maxDuration + p->yellow + p->red);
599 }
600 if (coordinateMode && (cycleLengthCalculated != myCycleLength)) {
601 int ringNumber = ringIndex + 1;
602 const std::string error = "At NEMA tlLogic '" + getID() + "', Ring " + toString(ringNumber) + " does not add to cycle length.";
603 if (ignoreErrors) {
604 WRITE_WARNING(error);
605 } else {
606 throw ProcessError(error);
607 }
608 }
609 }
610 // check that the barriers sum together
611 SUMOTime cycleLengths[2][2] = { {0, 0}, {0, 0} };
612 for (int ringIndex = 0; ringIndex < 2; ringIndex++) {
613 // TS2 Force Offs don't go in order, so using a different method to check cycle time
614 for (const auto p : getPhasesByRing(ringIndex)) {
615 cycleLengths[ringIndex][p->barrierNum] += p->maxDuration + p->yellow + p->red;
616 }
617 }
618 // Write warnings if the barriers do not sum
619 for (int barrierNum = 0; barrierNum < 2; barrierNum++) {
620 if (cycleLengths[0][barrierNum] != cycleLengths[1][barrierNum]) {
621 const std::string error = "At NEMA tlLogic '" + getID() + "', the phases before barrier " + toString(barrierNum + 1) + " from both rings do not add up. (ring1="
622 + toString(STEPS2TIME(cycleLengths[0][barrierNum])) + ", ring2=" + toString(STEPS2TIME(cycleLengths[1][barrierNum])) + ")";
624 throw ProcessError(error);
625 } else {
626 WRITE_WARNING(error);
627 }
628 }
629 }
630
631 // no offset for non coordinated
632 if (!coordinateMode && offset != 0) {
633 WRITE_WARNINGF(TL("NEMA tlLogic '%' is not coordinated but an offset was set."), getID());
634 }
635}
636
637void
638NEMALogic::setNewSplits(std::vector<double> newSplits) {
639 assert(newSplits.size() == 8);
640 for (auto& p : myPhaseObjs) {
641 if (newSplits[p->phaseName - 1] > 0) {
642 // set the phase's nextMaxDuration. This will be implemented when implementTraciChanges is called
643 p->nextMaxDuration = TIME2STEPS(newSplits[p->phaseName - 1]) - p->yellow - p->red;
644 }
645 }
646}
647
648
649void
650NEMALogic::setNewMaxGreens(std::vector<double> newMaxGreens) {
651 for (auto& p : myPhaseObjs) {
652 if (newMaxGreens[p->phaseName - 1] > 0) {
653 // set the phase's nextMaxDuration. This will be implemented when implementTraciChanges is called
654 p->nextMaxDuration = TIME2STEPS(newMaxGreens[p->phaseName - 1]);
655 }
656 }
657}
658
659
660void
661NEMALogic::setNewCycleLength(double newCycleLength) {
662 // set the controller's next cycle length. This will be implemented when implementTraciChanges is called
663 myNextCycleLength = TIME2STEPS(newCycleLength);
664}
665
666
667void
668NEMALogic::setNewOffset(double newOffset) {
669 // set the controller's offset. This will be implemented when implementTraciChanges is called
670 myNextOffset = TIME2STEPS(newOffset);
671}
672
673
674std::vector<int> NEMALogic::readParaFromString(std::string s) {
675 std::vector<int> output;
676 for (char c : s) {
677 if (c >= '0' && c <= '9') {
678 int temp = c - '0';
679 output.push_back(temp);
680 }
681 }
682 return output;
683}
684
687 return myPhase;
688}
689
690
691void
695
696
697int NEMALogic::measureRingDistance(int p1, int p2, int ringNum) {
698 int length = (int)rings[ringNum].size();
699 int d = 0;
700 bool found = false;
701 // Loop around the ring and keep track of the distance from p1 to p2
702 for (int i = 0; i < (length * 2); i++) {
703 if (rings[ringNum][i % length] > 0) {
704 if (found) {
705 d++;
706 if (rings[ringNum][i % length] == p2) {
707 break;
708 }
709 } else if (rings[ringNum][i % length] == p1) {
710 found = true;
711 }
712 }
713 }
714 assert(d > 0);
715 return d;
716}
717
718
721 SUMOTime c = a - b;
722 while (c >= b) {
723 c = c - b;
724 }
725 while (c < 0) {
726 c += b;
727 }
728 return c;
729}
730
731
732void
733NEMALogic::getLaneInfoFromNEMAState(std::string state, StringVector& laneIDs, IntVector& stateIndex) {
734 std::set<std::string> output;
735 for (int i = 0; i < (int)myLinks.size(); i++) {
736 if (myLinks[i].empty()) {
737 // unused index
738 continue;
739 }
740 char ch = state[i];
741 // if the ch is 'G', it means that the phase is controlling this lane
742 if (ch == 'G') {
743 stateIndex.push_back(i);
744 for (auto link : myLinks[i]) {
745 const MSLane* incoming = link->getLaneBefore();
746 if (incoming->isNormal()) {
747 laneIDs.push_back(incoming->getID());
748 }
749 }
750 }
751 }
752}
753
754bool NEMALogic::isLeftTurnLane(const MSLane* const lane) const {
755 const std::vector<MSLink*> links = lane->getLinkCont();
756 if (links.size() == 1 && links.front()->getDirection() == LinkDirection::LEFT) {
757 return true;
758 }
759 return false;
760}
761
762bool
763NEMALogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
764 for (int i = 0; i < (int)state.size(); i++) {
765 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
766 for (MSLane* cand : getLanesAt(i)) {
767 for (MSLane* lane : lanes) {
768 if (lane == cand) {
769 return true;
770 }
771 }
772 }
773 }
774 }
775 return false;
776}
777
778
779void
782 for (auto& item : myLaneDetectorMap) {
783 item.second->setVisible(true);
784 }
785}
786
787void
790 for (auto& item : myLaneDetectorMap) {
791 item.second->setVisible(false);
792 }
793}
794
795void
797 myShowDetectors = show;
798 for (auto& item : myLaneDetectorMap) {
799 item.second->setVisible(myShowDetectors);
800 }
801}
802
803int NEMALogic::string2int(std::string s) {
804 std::stringstream ss(s);
805 int ret = 0;
806 ss >> ret;
807 return ret;
808}
809
810
811const std::string
812NEMALogic::getParameter(const std::string& key, const std::string defaultValue) const {
813 if (StringUtils::startsWith(key, "NEMA.")) {
814 if (key == "NEMA.phaseCall") {
815 int activeCalls[8] = { 0 };
816 for (const auto p : myPhaseObjs) {
817 // This handles the case when the controller has multiple of the same phase call
818 if (!activeCalls[p->phaseName - 1]) {
819 activeCalls[p->phaseName - 1] = 1 * p->lastDetectActive;
820 }
821 }
822 std::string outStr = "";
823 for (int i = 0; i < 8; i++) {
824 outStr += std::to_string(activeCalls[i]);
825 if (i < 7) {
826 outStr += ",";
827 }
828 }
829 return outStr;
830 } else {
831 throw InvalidArgument("Unsupported parameter '" + key + "' for NEMA controller '" + getID() + "'");
832 }
833 } else {
834 return Parameterised::getParameter(key, defaultValue);
835 }
836}
837
838
839void
840NEMALogic::setParameter(const std::string& key, const std::string& value) {
841 queuedTraciChanges = true;
842 if (StringUtils::startsWith(key, "NEMA.")) {
843 if (key == "NEMA.splits" || key == "NEMA.maxGreens") {
844 //splits="2.0 3.0 4.0 5.0 2.0 3.0 4.0 5.0"
845 const std::vector<std::string>& tmp = StringTokenizer(value).getVector();
846 if (tmp.size() != 8) {
847 queuedTraciChanges = false;
848 throw InvalidArgument("Parameter '" + key + "' for NEMA controller '" + getID() + "' requires 8 space or comma separated values");
849 }
850 std::vector<double> timing;
851 for (const std::string& s : tmp) {
852 timing.push_back(StringUtils::toDouble(s));
853 }
854 if (key == "NEMA.maxGreens") {
855 setNewMaxGreens(timing);
856 } else {
857 setNewSplits(timing);
858 }
859 } else if (key == "NEMA.cycleLength") {
861 } else if (key == "NEMA.offset") {
863 } else {
864 queuedTraciChanges = false;
865 throw InvalidArgument("Unsupported parameter '" + key + "' for NEMA controller '" + getID() + "'");
866 }
867 }
868 Parameterised::setParameter(key, value);
869}
870
871void
872NEMALogic::error_handle_not_set(std::string param_variable, std::string param_name) {
873 if (param_variable == "") {
874 throw InvalidArgument("Please set " + param_name + " for NEMA tlLogic '" + getID() + "'");
875 }
876}
877
878void
880 SUMOTime zeroTime[2] = { TIME2STEPS(0), TIME2STEPS(0) };
881 for (int i = 0; i < 2; i++) {
882 SUMOTime runningTime = 0;
883 // loop through the phases for ring 0 and then 1
884 for (auto& p : getPhasesByRing(i)) {
885 runningTime += p->maxDuration + p->getTransitionTimeStateless();
886 // in 170, the cycle "starts" when the coordinated phase goes to yellow.
887 // See https://ops.fhwa.dot.gov/publications/fhwahop08024/chapter6.html
888 if (p->coordinatePhase) {
889 zeroTime[i] = runningTime;
890 }
891 p->forceOffTime = runningTime - p->getTransitionTimeStateless();
892 p->greatestStartTime = p->forceOffTime - p->minDuration;
893 }
894 }
895 // find the minimum offset time and then subtract from everything, modecycling where negative
896 // This sets the 0 cycle time as start of yellow on earliest ending coordinated phase
897 SUMOTime minCoordYellow = MIN2(zeroTime[0], zeroTime[1]);
898 for (auto& p : myPhaseObjs) {
899 p->forceOffTime = ModeCycle(p->forceOffTime - minCoordYellow, myCycleLength);
900 p->greatestStartTime = ModeCycle(p->greatestStartTime - minCoordYellow, myCycleLength);
901 }
902
903#ifdef DEBUG_NEMA
904 std::ios_base::fmtflags oldflags = std::cout.flags();
905 std::streamsize oldprecision = std::cout.precision();
906 for (int i = 0; i < 2; i++) {
907 std::cout << "Ring" << i + 1 << " force offs: \t";
908 for (auto& p : rings[i]) {
909 if (p > 0) {
910 PhasePtr pObj = getPhaseObj(p, i);
911 std::cout << std::fixed << std::setprecision(2) << STEPS2TIME(pObj->forceOffTime) << "\t";
912 } else {
913 std::cout << std::to_string(0) << "\t";
914 }
915 }
916 std::cout << std::endl;
917 }
918 std::cout.flags(oldflags);
919 std::cout.precision(oldprecision);
920#endif
921}
922
923void
925 // TS2 "0" cycle time is the start of the "first" coordinated phases.
926 // We can find this "0" point by first constructing the forceOffs in sequential order via the 170 method
928
929 // Switch the Force Off Times to align with TS2 Cycle, which is the *start* of the earliest coordinated phase
930 // The coordinate phases will always be the defaultBarrierPhases[i][0]
931 SUMOTime minCoordTime = MIN2(coordinatePhaseObjs[0]->forceOffTime - coordinatePhaseObjs[0]->maxDuration,
932 coordinatePhaseObjs[1]->forceOffTime - coordinatePhaseObjs[1]->maxDuration);
933
934 // loop through all the phases and subtract this minCoordTime to move the 0 point to the start of the first coordinated phase
935 for (auto& p : myPhaseObjs) {
936 if ((p->forceOffTime - minCoordTime) >= 0) {
937 p->forceOffTime -= (minCoordTime);
938 } else {
939 p->forceOffTime = (myCycleLength + (p->forceOffTime - (minCoordTime)));
940 }
941 p->greatestStartTime = ModeCycle(p->greatestStartTime - minCoordTime, myCycleLength);
942 }
943}
944
945void
947 // get the time in the cycle
949 NEMAPhase* activePhases[2];
950 for (int i = 0; i < 2; i++) {
951 std::vector<NEMAPhase*> ringCopy = getPhasesByRing(i);
952 // sort by the minimum start time in the cycle.
953 std::sort(ringCopy.begin(), ringCopy.end(),
954 [](NEMAPhase * p, NEMAPhase * p1) {
955 return p->greatestStartTime <= p1->greatestStartTime;
956 }
957 );
958 bool found = false;
959 // loop through the sorted phases by time and try to find the phase that should be active given the time in the cycle
960 for (auto& p : ringCopy) {
961 // This handles the wrap around. Checks if the prior phases start time should have already happened.
962 // If it should have happened and it's not to the start time of me yet, start in my phase ( will have to be in my phase longer than max time likely )
965 if (cycleTime <= ModeCycle(p->greatestStartTime, myCycleLength) && cycleTime > ModeCycle(syntheticPriorStart, myCycleLength)) {
966 found = true;
967 activePhases[i] = p;
968 break;
969 }
970 }
971 if (!found) {
972#ifdef DEBUG_NEMA
973 const std::string error = "I can't find the correct phase for NEMA tlLogic '" + getID() + "' Ring " + toString(i) + " to start in.";
974 WRITE_WARNING(error);
975 WRITE_WARNING(TL("I am starting in the coordinated phases"));
976#endif
977 activePhases[0] = defaultBarrierPhases[0][0];
978 activePhases[1] = defaultBarrierPhases[1][0];
979 }
980 }
981
982 // ensure that the two found phases are on the same side of the barrier. If they aren't, just override with the default barrier phases
983 if (activePhases[0]->barrierNum != activePhases[1]->barrierNum) {
984 // give preference to whatever is on the coordinate side of the barrier, one must be if they aren't equal to each other
985 activePhases[0] = activePhases[0]->barrierNum == 0 ? activePhases[0] : defaultBarrierPhases[0][0];
986 activePhases[1] = activePhases[1]->barrierNum == 0 ? activePhases[1] : defaultBarrierPhases[1][0];
987 }
988
989 // force enter the phases and update their expected duration to last until the forceOff
990 activePhases[0]->forceEnter(this);
991 activePhases[1]->forceEnter(this);
992}
993
994void
996 // Modifications where made to 170 algorithm so that it works with both.
998}
999
1001NEMALogic::parseControllerType(std::string inputType) {
1002 std::string cleanString;
1003 for (const char& c : inputType) {
1004 if (isalpha(c) || isdigit(c)) {
1005 cleanString += (char)::tolower(c);
1006 }
1007 }
1008 if (cleanString == "type170") {
1009 return Type170;
1010 } else if (cleanString == "ts2") {
1011 return TS2;
1012 } else {
1013 throw InvalidArgument("Please set controllerType for NEMA tlLogic " + myID + " to either Type170 or TS2");
1014 }
1015}
1016
1017std::vector<NEMAPhase*>
1019 std::vector<NEMAPhase*> phases;
1020 for (auto& p : myPhaseObjs) {
1021 if (p->ringNum == ringNum) {
1022 phases.push_back(p);
1023 }
1024 }
1025 return phases;
1026}
1027
1028void
1032
1033std::map<std::string, double>
1035 std::map<std::string, double> result;
1036 for (auto item : myDetectorLaneMap) {
1037 result[item.first->getID()] = item.first->getCurrentVehicleNumber();
1038 }
1039 return result;
1040}
1041
1044 // return a pointer to the other active phase
1045 return myActivePhaseObjs[!p->ringNum];
1046}
1047
1048NEMAPhase*
1049NEMALogic::getPhaseObj(int phaseNum, int ringNum) {
1050 // This satisfies the case where there is a "duplicate" phase on both ring
1051 std::vector<PhasePtr> iterRing = ringNum >= 0 ? getPhasesByRing(ringNum) : myPhaseObjs;
1052 for (auto& p : iterRing) {
1053 if (p->phaseName == phaseNum) {
1054 return p;
1055 }
1056 }
1057 // the phase must always be found
1058 throw ProcessError("At traffic signal '" + myID + "' program '" + myProgramID + "' phase '" + toString(phaseNum) + "' not found in ring '" + toString(ringNum) + "'.");
1059}
1060
1063 NEMAPhase* p = t->getFromPhase();
1064 // if the current phase is not ready to switch or a barrier cross is desired by the other transition
1065 // and t fromPhase is not ready to switch, the default transition is back to myself
1066 if (!p->readyToSwitch ||
1068 return p->getTransition(p->phaseName);
1069 }
1070 // otherwise the default transition is to the default phase on whatever barrier ot wants to transition to
1071 else {
1073 }
1074}
1075
1076void
1078 std::vector<std::vector<PhaseTransitionLogic* >> potentialPhases;
1079
1080 // Get a vector of each phases' potential transitions
1081 for (const auto& p : myActivePhaseObjs) {
1082 potentialPhases.push_back(p->trySwitch(this));
1083 }
1084
1085 // Loop through all combination of transitions, keeping only the valid ones and filling in the gaps where necessary
1086 for (const auto& r1_t : potentialPhases[0]) {
1087 for (const auto& r2_t : potentialPhases[1]) {
1088 // if both transitions go to the same barrier then we are good
1089 if (r1_t->getToPhase()->barrierNum == r2_t->getToPhase()->barrierNum) {
1090 transitions.push_back({ r1_t, r2_t, (float)(r1_t->getDistance(r2_t) + r2_t->getDistance(r1_t)) / 2 });
1091 } else {
1092 // If the rings are different, add a choice where one of them is the default choice for whatever ring it is
1093 // create two choices, one for each of the phase as they are on different rings
1094 if (r1_t->getFromPhase()->readyToSwitch) {
1095 // get the r2 default
1096 PhaseTransitionLogic* r2_t_temp = getDefaultTransition(r2_t, r1_t);
1097 // only add it if it does not cross a barrier!
1098 if (r2_t_temp->getToPhase()->barrierNum == r1_t->getToPhase()->barrierNum) {
1099 transitions.push_back({ r1_t, r2_t_temp, (float)(r2_t_temp->getDistance(r1_t) + r1_t->getDistance(r2_t_temp)) / 2 });
1100 }
1101 }
1102 if (r2_t->getFromPhase()->readyToSwitch) {
1103 // R1 default
1104 PhaseTransitionLogic* r1_t_temp = getDefaultTransition(r1_t, r2_t);
1105 // only add it if it does not cross a barrier!
1106 if (r1_t_temp->getToPhase()->barrierNum == r2_t->getToPhase()->barrierNum) {
1107 transitions.push_back({ r1_t_temp, r2_t, (float)(r2_t->getDistance(r1_t_temp) + r1_t_temp->getDistance(r2_t)) / 2 });
1108 }
1109 }
1110 // If the distances are <= 1, this means that this is the shortest transition possible
1111 // and we can return early without considering any other options
1112 if (!transitions.empty()) {
1113 if (transitions.back().distance < 1) {
1114 return;
1115 }
1116 }
1117 }
1118 }
1119 }
1120}
1121
1122
1123std::string
1125 // FIX with plan to support #10742
1126 std::string out(myPhaseStrLen, 'r');
1127 for (int i = 0; i < myPhaseStrLen; i++) {
1128 bool controlled = false;
1129 std::string phaseChars = "";
1130 for (auto& p : myActivePhaseObjs) {
1131 phaseChars += p->getNEMAChar(i);
1132 if (p->controlledIndex(i)) {
1133 out[i] = p->getNEMAChar(i);
1134 controlled = true;
1135 }
1136 }
1137 // if the index wasn't a controlled one, the prior priority order still stands
1138 if (!controlled) {
1139 for (auto priority_char : lightHeadPriority) {
1140 if (std::count(phaseChars.begin(), phaseChars.end(), priority_char)) {
1141 out[i] = priority_char;
1142 break;
1143 }
1144 }
1145 }
1146 }
1147 return out;
1148}
1149
1150
1153#ifdef DEBUG_NEMA_SWITCH
1154 std::cout << SIMTIME << " trySwitch tls=" << getID() << "\n";
1155#endif
1156 PhaseTransitionLogic* nextPhases[2] = { nullptr, nullptr };
1157
1158 // update the internal time. This is a must. Could have just used a reference to the time
1160
1161 // Check the Detectors
1162 for (auto& p : myPhaseObjs) {
1163 p->checkMyDetectors();
1164 }
1165
1166 // Update the timing parameters
1167 for (const auto& p : myActivePhaseObjs) {
1168 p->update(this);
1169 }
1170
1171 // Calculate the Next Phases, but only if atleast one of them is ready to transition
1172 if (myActivePhaseObjs[0]->readyToSwitch || myActivePhaseObjs[1]->readyToSwitch) {
1173 TransitionPairs transitions;
1174 // set the next phases by reference
1175 getNextPhases(transitions);
1176
1177 // Sort the next phases by distance and select the closest.
1178 // TODO: Is there a way to avoid this sort? The transitions are already sorted by distance prior
1179 // to picking the valid ones
1180 if (transitions.size() > 1) {
1181 std::sort(transitions.begin(), transitions.end(),
1182 [](const transitionInfo & i, const transitionInfo & j) {
1183 return i.distance < j.distance;
1184 });
1185 }
1186
1187 // Set the Next Phases = to the transition with least combined distance
1188 nextPhases[0] = transitions.front().p1;
1189 nextPhases[1] = transitions.front().p2;
1190
1191 // Try the exit logic. This doesn't necessarily mean that the phase will exit,
1192 // as it could go into green rest or green transfer, but this is considered an "exit"
1193 for (const auto& p : myActivePhaseObjs) {
1194 if (p->readyToSwitch) {
1195 p->exit(this, nextPhases);
1196 }
1197 }
1198
1199 // This is the only time when something might have happened, so we update the phase strings here
1200 std::string newState = composeLightString();
1201 if (newState != myPhase.getState()) {
1202 myPhase.setState(newState);
1203 myPhase.setName(toString(myActivePhaseObjs[0]->phaseName) + "+" + toString(myActivePhaseObjs[1]->phaseName));
1204 // ensure that SwitchCommand::execute notices a change
1205 myStep = 1 - myStep;
1206
1207 }
1208 }
1209
1210 // clear the phases' detectors
1211 for (auto& p : myPhaseObjs) {
1212 p->clearMyDetectors();
1213 }
1214
1215
1216#ifdef FUZZ_TESTING
1217 // Basic Assertion to ensure that the Barrier is not crossed
1218 assert(myActivePhaseObjs[0]->barrierNum == myActivePhaseObjs[1]->barrierNum);
1219#endif
1220
1221 // return the simulation timestep, as this controller must be checked every simulation step
1222 return DELTA_T;
1223}
1224
1225
1226void
1228 // Implement Traci Updates on the start of ring1 coordinated phase (rising edge of it turning green)
1229 if (queuedTraciChanges) {
1230 for (auto& p : myPhaseObjs) {
1231 p->maxDuration = p->nextMaxDuration;
1232 }
1235 // now that we have set the cycle length, offset and max duration, we need to update force off times
1237 queuedTraciChanges = false;
1238 }
1239}
1240
1241
1242// ===========================================================================
1243// NEMAPhase Definitions
1244// ===========================================================================
1245NEMAPhase::NEMAPhase(int phaseName, bool isBarrier, bool isGreenRest, bool isCoordinated,
1246 bool minRecall, bool maxRecall, bool fixForceOff, int barrierNum, int ringNum,
1247 IntVector phaseStringInds,
1248 MSPhaseDefinition* phase) :
1249 phaseName(phaseName),
1250 isAtBarrier(isBarrier),
1251 isGreenRest(isGreenRest),
1252 barrierNum(barrierNum),
1253 coordinatePhase(isCoordinated),
1254 minRecall(minRecall),
1255 maxRecall(maxRecall),
1256 fixForceOff(fixForceOff),
1257 ringNum(ringNum),
1258 myCorePhase(phase),
1259 myPhaseStringInds(phaseStringInds) {
1260 // Public
1261 readyToSwitch = false;
1262 greenRestTimer = 0;
1263 forceOffTime = 0;
1264 lastDetectActive = false;
1265
1266 // Private
1267 myInstance = this;
1268 myLastPhaseInstance = nullptr;
1269 sequentialPriorPhase = nullptr;
1271 transitionActive = false;
1272
1273 // Timing Parameters
1275 myStartTime = TIME2STEPS(0.);
1277 myLastEnd = TIME2STEPS(0.);
1278
1279 // set the phase colors
1281}
1282
1284 // Delete the transitions from their alloc
1285 for (auto t : myTransitions) {
1286 delete t;
1287 }
1288}
1289
1290void
1291NEMAPhase::init(NEMALogic* controller, int crossPhaseTarget, int crossPhaseSource, bool latching) {
1292 // switch the durations from steps2time
1294
1295 for (auto p : controller->getPhasesByRing(ringNum)) {
1296 // construct transitions for all potential movements, including back to myself
1297 myTransitions.push_back(new PhaseTransitionLogic(this, p));
1298 myTransitions.back()->setDistance(controller->measureRingDistance(phaseName, p->phaseName, ringNum));
1299 }
1300
1301 // sort the transitions by distance for speed later. Using plain distance here
1302 std::sort(myTransitions.begin(), myTransitions.end(), [&](const PhaseTransitionLogic * i, const PhaseTransitionLogic * j) {
1303 return i->distance < j->distance;
1304 });
1305
1306 // create the phase detector info
1308 crossPhaseSource > 0 ? controller->getPhaseObj(crossPhaseSource) : nullptr,
1309 crossPhaseTarget > 0 ? controller->getPhaseObj(crossPhaseTarget) : nullptr
1310 );
1311}
1312
1313void
1315 // This could be extended in the future to allow for traci manipulation
1317 red = myCorePhase->red;
1323}
1324
1325
1326// TODO: this can be computed once.
1327char
1330 return myGreenString[i];
1331 } else if (myLightState <= LightState::Red) {
1332 return myRedString[i];
1333 } else {
1334 return myYellowString[i];
1335 }
1336}
1337
1338void
1341 myRedString = "";
1342 myYellowString = "";
1343 for (char ch : myGreenString) {
1344 myRedString += 'r';
1345 if (ch == 'G' || ch == 'g') {
1346 myYellowString += 'y';
1347 } else {
1348 myYellowString += ch;
1349 }
1350 }
1351}
1352
1353void
1356 // remove the active flag on the detector if the detector is not latching or if it is green
1359 }
1360}
1361
1362void
1364 // Check my Detectors, only necessary if it isn't currently marked as on
1366 // If I have a cross phase target and it is active and I am not, save my detector as not active
1367 if (myDetectorInfo.cpdTarget != nullptr) {
1371 return;
1372 }
1373 }
1374 }
1375 // If we make it to this point, check my detector like normal.
1376 for (auto& d : myDetectorInfo.detectors) {
1377 if (d->getCurrentVehicleNumber() > 0) {
1379 return;
1380 }
1381 }
1382 // If my detector is not active, check my cross phase
1383 if ((myDetectorInfo.cpdSource != nullptr) && (myLightState >= LightState::Green)) {
1385 for (auto& d : myDetectorInfo.cpdSource->getDetectors()) {
1386 if (d->getCurrentVehicleNumber() > 0) {
1388 return;
1389 }
1390 }
1391 }
1392 }
1393 }
1394}
1395
1396void
1397NEMAPhase::enter(NEMALogic* controller, NEMAPhase* lastPhase) {
1398#ifdef DEBUG_NEMA_SWITCH
1399 std::cout << SIMTIME << " enter tls=" << controller->getID() << " phase=" << phaseName << "\n";
1400#endif
1401
1402 // set the last phase instance to inactive
1403 lastPhase->cleanupExit();
1404
1405 // Enter the phase
1406 myStartTime = controller->getCurrentTime();
1408 myLastPhaseInstance = lastPhase;
1409 readyToSwitch = false;
1410
1411 // implement the new timing parameters on the first coordinated phase to appear
1412 if (phaseName == controller->coordinatePhaseObjs[ringNum]->phaseName) {
1413 controller->implementTraciChanges();
1414 }
1415
1416 // Handle Green Rest Peculiarities
1417 if (!controller->coordinateMode && isGreenRest) {
1418 // If the controller is in free mode and the phase is a green rest phase, then it should enter as "green rest"
1420 // if the phase has "green rest" capabilities, set its timer to the dynamic maxGreen
1422 }
1423
1424 // clear the last transition decision
1425 lastTransitionDecision = nullptr;
1426
1427 // Calculate the Max Green Time & Expected Duration here:
1428 if (controller->coordinateMode) {
1429 if (coordinatePhase) {
1430 myExpectedDuration = controller->ModeCycle(forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1431 } else {
1432 maxGreenDynamic = controller->ModeCycle(forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1433 if (!fixForceOff) {
1435 }
1437 }
1438 } else {
1440 }
1441 // Implements the maxRecall functionality
1442 if (maxRecall && !coordinatePhase) {
1444 }
1445 // Set the controller's active phase
1446 controller->setActivePhase(this);
1447}
1448
1449void NEMAPhase::exit(NEMALogic* controller, PhaseTransitionLogic* nextPhases[2]) {
1450 if (nextPhases[ringNum]->getToPhase() != this) {
1451 // if the next phase is not me, then I need to go into a transition
1452 lastTransitionDecision = nextPhases[ringNum];
1454 // if I am in green, then I need to enter yellow
1455 enterYellow(controller);
1456 return;
1457 }
1458
1459 if (controller->getCurrentTime() - myLastEnd < (yellow + red)) {
1460 if (controller->getCurrentTime() - myLastEnd >= yellow) {
1461 // if I am in yellow, then I need to enter red
1463 }
1464 // I am currently in the Red state but haven't reached max
1465 return;
1466 }
1467
1468 handleRedXferOrNextPhase(controller, nextPhases);
1469 return;
1470 }
1471
1472 handleGreenRestOrTransfer(controller, nextPhases);
1473}
1474
1476 myLastEnd = controller->getCurrentTime();
1478 transitionActive = true;
1479}
1480
1482 PhasePtr otherPhase = controller->getOtherPhase(this);
1483 bool barrierCross = nextPhases[ringNum]->getToPhase()->barrierNum != barrierNum;
1484 bool barrierCrossButOkay = barrierCross && (
1485 nextPhases[ringNum]->getToPhase()->barrierNum == nextPhases[otherPhase->ringNum]->getToPhase()->barrierNum
1486 ) && otherPhase->okay2ForceSwitch(controller);
1487
1488 if (!barrierCross) {
1489 nextPhases[ringNum]->getToPhase()->enter(controller, this);
1490 return;
1491 }
1492
1493 if (barrierCrossButOkay) {
1494 // if the barrier is crossed, but the other phase is going to the same barrier, then I can enter red transfer
1495 // enter the next phase
1496 nextPhases[ringNum]->getToPhase()->enter(controller, this);
1497 // trigger the other phase to enter red transfer
1498 nextPhases[otherPhase->ringNum]->getToPhase()->enter(controller, this);
1499 return;
1500 }
1501
1503 readyToSwitch = true;
1504 transitionActive = false;
1505}
1506
1508 NEMAPhase* otherPhase = controller->getOtherPhase(this);
1509 readyToSwitch = false;
1510 bool isOtherPhaseReady = nextPhases[!ringNum]->getToPhase() == otherPhase && otherPhase->readyToSwitch;
1511 bool isOtherPhaseInGreenRest = otherPhase->greenRestTimer >= otherPhase->maxDuration && otherPhase->getCurrentState() == LightState::GreenRest;
1512
1513 if (isOtherPhaseReady || isOtherPhaseInGreenRest) {
1515 myStartTime = controller->getCurrentTime() - minDuration;
1518 } else {
1520 if (isAtBarrier) {
1521 myExpectedDuration = (otherPhase->myExpectedDuration + otherPhase->myStartTime) - myStartTime;
1522 }
1523 }
1524}
1525
1529 // if in red xfer, I am ready to switch whenevery
1530 return TIME2STEPS(0);
1531 }
1532 if (!transitionActive) {
1533 // if a transition is not active, the transition is just yellow + red time
1535 }
1536 // if a transition is active, then return the time left in the transition
1537 return MAX2(TIME2STEPS(0), ((yellow + red) - (controller->getCurrentTime() - myLastEnd)));
1538}
1539
1543 // add the vehicle extension timer if the detector is active.
1544 // capped by the minimum and maximum duration
1545 return MIN2(MAX2(duration + vehExt, minDuration), maxGreenDynamic);
1546 }
1547 return myExpectedDuration;
1548}
1549
1550void
1552 // If I am in a transition, the rest of the update logic does not matter
1554 // return early
1555 readyToSwitch = true;
1556 return;
1557 }
1558
1559 // Continuation Logic
1560 SUMOTime duration = controller->getCurrentTime() - myStartTime;
1561 // Check the vehicle extension timer as long as not in green transfer and not a coordinated phase
1564 }
1565 // Special Logic for Green Rest, which behaves uniquely
1567 // check all other detectors and see if anything else is active. If so,
1568 // start the green rest timer countdown, which is == to the max duration of the phase
1569 bool vehicleActive = false;
1570 for (auto& p : controller->getPhaseObjs()) {
1571 if ((p->phaseName != phaseName)
1572 && (p->phaseName != controller->getOtherPhase(this)->phaseName)
1573 && p->callActive()) {
1575 vehicleActive = true;
1576 break;
1577 }
1578 }
1579 // catch the rising edge of the sidestreet detection and calculate the maximum timer
1580 if (vehicleActive && (greenRestTimer + DELTA_T >= maxDuration)) {
1582 }
1583
1584 // if there are no other vehicles slide the startTime along
1585 if (!vehicleActive) {
1587 if (duration >= minDuration) {
1588 myStartTime = controller->getCurrentTime() - minDuration;
1591 }
1592 }
1593
1594 // if the green rest timer is exhausted, set ready to switch
1595 if (greenRestTimer < DELTA_T) {
1596 readyToSwitch = true;
1597 // force the counterpart to be ready to switch too. This needs to be latching....
1598 NEMAPhase* otherPhase = controller->getOtherPhase(this);
1599 if (otherPhase->getCurrentState() > LightState::Green) {
1600 otherPhase->readyToSwitch = true;
1601 }
1602 }
1603
1604 // Special Behavior when the Green Rest Circles all the way around in coordinated mode
1605 if (coordinatePhase) {
1606 // This means that we have green rested until I should "start" again. Just call my entry function again.
1607 if (controller->getTimeInCycle() <= ((forceOffTime - maxDuration) + DELTA_T / 2)) {
1608 enter(controller, this);
1609 }
1610 }
1611 }
1612 // Check to see if a switch is desired
1613 if (duration >= myExpectedDuration) {
1614 readyToSwitch = true;
1615 }
1616}
1617
1620 for (auto t : myTransitions) {
1621 if (t->getToPhase()->phaseName == toPhase) {
1622 return t;
1623 }
1624 }
1625 // This point should never be reached
1626 assert(0);
1627 // To satisfy the compiler and return value from all control paths
1628 return myTransitions.front();
1629}
1630
1631std::vector<PhaseTransitionLogic*>
1633 // this function returns the preferred valid transition for the phase
1634 std::vector<PhaseTransitionLogic*> nextTransitions;
1635 if (readyToSwitch) {
1636 // only try to switch if I am ready to switch
1637 for (auto& t : myTransitions) {
1638 // for the transitions check if it is okay
1639 if (t->okay(controller)) {
1640 // if there was already a transition decision, it can be overriden but only if the new transition
1641 // is on the same side of a barrier
1642 if (lastTransitionDecision != nullptr) {
1643 if (t->getToPhase()->barrierNum == lastTransitionDecision->getToPhase()->barrierNum) {
1644 nextTransitions.push_back(t);
1645 break;
1646 }
1647 } else {
1648 nextTransitions.push_back(t);
1649 // break once there is a valid option (they have already been sorted)
1650 break;
1651 }
1652 }
1653 }
1654 }
1655 // Add in the last transition decision if it hasn't been added in yet
1656 if (lastTransitionDecision != nullptr) {
1657 bool found = false;
1658 bool sameBarrier = false;
1659 for (auto& t : nextTransitions) {
1660 if (t == lastTransitionDecision) {
1661 found = true;
1662 break;
1663 }
1664 if (t->getToPhase()->barrierNum == lastTransitionDecision->getToPhase()->barrierNum) {
1665 sameBarrier = true;
1666 break;
1667 }
1668 }
1669 // but only need to add it if it is not in the list AND if nothing in the list is the same barrier as it was.
1670 if (!found && !sameBarrier) {
1671 nextTransitions.push_back(lastTransitionDecision);
1672 }
1673 }
1674 // Add the transition back to myself, but only in the case when no others have been added
1675 if (nextTransitions.size() < 1) {
1676 nextTransitions.push_back(myTransitions.back());
1677 }
1678
1679 return nextTransitions;
1680}
1681
1682// ===========================================================================
1683// PhaseTransitionLogic Definitions
1684// ===========================================================================
1686 NEMAPhase* fromPhase, NEMAPhase* toPhase) :
1687 distance(0),
1688 fromPhase(fromPhase),
1689 toPhase(toPhase)
1690{}
1691
1692bool
1694 // Picking the correct transition logic to use
1695 // #TODO this could be a case of using function as variable and setting it at PhaseTransitionLogic
1696 // creation time
1697 if (fromPhase == toPhase) {
1698 // for green rest or green transfer, it cannot return to itself if a transition is active
1700 } else if (fromPhase->coordinatePhase) {
1701 // if the from phase is a coordinated phase i.e. {2, 6} in a standard setup
1702 return fromCoord(controller);
1703 } else if (fromPhase->isAtBarrier) {
1704 // if the phase is at a barrier i.e. {2, 6, 4, 8} in a standard setup
1705 return fromBarrier(controller);
1706 } else if (controller->coordinateMode) {
1707 // typical coordinate mode transition,
1708 return coordBase(controller);
1709 } else {
1710 // base transition logic
1711 return freeBase(controller);
1712 }
1713}
1714
1715bool
1717 // Simplest transition logic. Just check if a detector (or recall) is active on that phase and
1718 bool okay = false;
1719 // is a call active on the toPhase?
1720 if (toPhase->callActive()) {
1721 // would the transition be a barrier cross?
1723 PhasePtr otherPhase = controller->getOtherPhase(fromPhase);
1724 // If it is a barrier cross, then the other phase must also be ready to switch
1725 // or have a transition time that is lower than mine currently. DELTA_T is critical here
1726 if (otherPhase->readyToSwitch) {
1727 // #&& otherPhase->getTransitionTime(controller) <= fromPhase->getTransitionTime(controller)) {
1728 okay = true;
1729 }
1730 } else {
1731 okay = true;
1732 }
1733 }
1734 return okay;
1735}
1736
1737bool
1739 if (toPhase->coordinatePhase &&
1741 // transitions TO the coordinated phase may always happen, as long as the other phase is okay to switch too
1742 return true;
1743 }
1744 // first check if the free logic is upheld
1745 else if (freeBase(controller)) {
1746 // Then check if the "to phase" can fit, which means that there is enough time to fit the current transition + the minimum time of the next phase
1747 SUMOTime transitionTime = fromPhase->getTransitionTime(controller);
1748 SUMOTime timeTillForceOff = controller->ModeCycle(toPhase->forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1749 if (toPhase->minDuration + transitionTime <= timeTillForceOff) {
1750 return true;
1751 }
1752 }
1753 return false;
1754}
1755
1756
1757bool
1759 if (freeBase(controller)) {
1761 // same barrier side so we are good.
1762 // Check if green transfer is active. If so, we need to make sure that there are no calls on the other side of the barrier
1764 for (auto& p : controller->getPhasesByRing(fromPhase->ringNum)) {
1765 if (p->barrierNum != fromPhase->barrierNum && p->callActive()) {
1766 return false;
1767 }
1768 }
1769 }
1770 return true;
1771 } else {
1772 // This is now a barrier cross and we need to make sure that the other phase is also ready to transition
1774 return true;
1775 }
1776 }
1777 }
1778 return false;
1779}
1780
1781
1782bool
1784 if (coordBase(controller)) {
1785 // Determine if the other phase is also ready to switch
1786 if (controller->getOtherPhase(fromPhase)->readyToSwitch) {
1787 // Dr. Wang had the Type-170 code setup in a way that it could transition whenever - meaning that it didn't matter if the prior phase could fit or not
1788 if (controller->isType170()) {
1789 return true;
1790 }
1791 // If the transition is already active, then report that the movement is possible
1793 return true;
1794 }
1795 // now determine if there my prior phase can fit or not. We already know that I can fit.
1796 NEMAPhase* priorPhase = toPhase->getSequentialPriorPhase();
1797 SUMOTime timeTillForceOff = controller->ModeCycle(priorPhase->forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1798 SUMOTime transitionTime = fromPhase->getTransitionTime(controller);
1799 // if the time till the force off is less than the min duration ||
1800 // if it is greater than the cycle length minus the length of the coordinate phase (which the fromPhase automatically is)
1801 if ((priorPhase->minDuration + transitionTime) > timeTillForceOff || timeTillForceOff > (controller->getCurrentCycleLength() - fromPhase->minDuration)) {
1802 return true;
1803 }
1804 }
1805 }
1806 return false;
1807}
1808
1809int
1811 // Returns the other transitions distance in green transfer situations
1812 if ((toPhase == fromPhase) && (otherTrans->toPhase->barrierNum == toPhase->barrierNum)) {
1814 return otherTrans->distance;
1815 }
1816 }
1817 return distance;
1818}
long long int SUMOTime
Definition GUI.h:36
#define INVALID_POSITION
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:287
#define WRITE_WARNING(msg)
Definition MsgHandler.h:286
#define TL(string)
Definition MsgHandler.h:304
#define TLF(string,...)
Definition MsgHandler.h:306
std::vector< std::string > StringVector
Definition of a vector of strings.
Definition Option.h:42
std::vector< int > IntVector
Definition of a vector of ints.
Definition Option.h:37
SUMOTime DELTA_T
Definition SUMOTime.cpp:38
#define STEPS2TIME(x)
Definition SUMOTime.h:58
#define SIMSTEP
Definition SUMOTime.h:64
#define SIMTIME
Definition SUMOTime.h:65
#define TIME2STEPS(x)
Definition SUMOTime.h:60
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
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_PEDESTRIAN
pedestrian
@ SUMO_TAG_LANE_AREA_DETECTOR
alternative tag for e2 detector
@ LEFT
The link is a (hard) left direction.
@ LINKSTATE_TL_GREEN_MAJOR
The link has green light, may pass.
@ LINKSTATE_TL_GREEN_MINOR
The link has green light, has to brake.
@ SUMO_ATTR_CYCLETIME
T MIN2(T a, T b)
Definition StdDefs.h:80
T MAX2(T a, T b)
Definition StdDefs.h:86
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 accessible from the current working directory.
const NamedObjectCont< MSDetectorFileOutput * > & getTypedDetectors(SumoXMLTag type) const
Returns the list of detectors of the given type.
An areal detector corresponding to a sequence of consecutive lanes.
virtual void setVisible(bool)
Representation of a lane in the micro simulation.
Definition MSLane.h:84
bool isNormal() const
Definition MSLane.cpp:2632
static bool dictionary(const std::string &id, MSLane *lane)
Static (sic!) container methods {.
Definition MSLane.cpp:2495
const std::vector< MSLink * > & getLinkCont() const
returns the container with all links !!!
Definition MSLane.h:735
MSDetectorControl & getDetectorControl()
Returns the detector control.
Definition MSNet.h:455
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition MSNet.cpp:187
The definition of a single phase of a tls logic.
SUMOTime maxDuration
The maximum duration of the phase.
SUMOTime vehext
for NEMA phase
SUMOTime minDuration
The minimum duration of the phase.
SUMOTime yellow
for NEMA phase
SUMOTime red
for NEMA phase
SUMOTime myLastSwitch
Stores the timestep of the last on-switched of the phase.
const std::string & getState() const
Returns the state within this phase.
void setState(const std::string &_state)
void setName(const std::string &_name)
A fixed traffic light logic.
Phases myPhases
The list of phases this logic uses.
A class that stores and controls tls and switching of their programs.
SUMOTime myOffset
the offset parameter of the current program
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 myDefaultCycleTime
The cycle time (without changes)
const std::string & getProgramID() const
Returns this tl-logic's id.
const std::string myProgramID
The id of the logic.
LaneVectorVector myLanes
The list of LaneVectors; each vector contains the incoming lanes that belong to the same link index.
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.
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.
A NEMA (adaptive) traffic light logic based on E2Detector.
SUMOTime cycleRefPoint
std::map< std::string, double > getDetectorStates() const override
retrieve all detectors used by this program
int measureRingDistance(int p1, int p2, int ringNum)
return the ring distance between two phases
SUMOTime getTimeInCycle() const
override Function to Simplify Accessing Offset Cycle Time
void getLaneInfoFromNEMAState(std::string state, StringVector &laneIDs, IntVector &stateIndex)
returns the IDs of the phase's controlled lanes. Found by looking for the "G" in the light state stri...
void getNextPhases(TransitionPairs &transitions)
extends the transitions vector with valid Transitions given the current traffic light state
void setNewMaxGreens(std::vector< double > newMaxGreens)
Set the max green of all phases.
void calculateForceOffs170()
calculate the forceOffs for a Type 170 style offset From https://ops.fhwa.dot.gov/publications/fhwaho...
void calculateInitialPhases()
directs the controller to the correct calculate phases function
void resetLastSwitch(SUMOTime t)
const std::string lightHeadPriority
constant for storing the priority order for light heads. Iterates left to right and stops when finds ...
const std::string getParameter(const std::string &key, const std::string defaultValue="") const override
try to get the value of the given parameter. Parameters prefixed with 'NEMA.' control functionality
std::vector< DetectorInfo > myDetectorInfoVector
storing the detector info in a vector
SUMOTime myNextCycleLength
the next cycle length (set by traci)
void init(NLDetectorBuilder &nb) override
Initialises the tls with information about incoming lanes.
void calculateInitialPhasesTS2()
calculate the initial phases for the TS2 style controller to start in
std::string myVehicleTypes
Whether detector output separates by vType.
std::string myFile
The output file for generated detectors.
double myDetectorLength
store the generated detector length
SUMOTime getCurrentCycleLength()
Get the current cycle length.
std::vector< std::vector< int > > rings
SUMOTime myFreq
The frequency for aggregating detector output.
DetectorLaneMap myDetectorLaneMap
A map from detectors to lanes.
SUMOTime trySwitch() override
overrides the MSSimpleTrafficLightLogic trySwitch method
bool isType170(void) const
checks if the controller is of type170
void validate_timing()
validates the NEMA timing. Writes warnings if ignoreError set to true else throws ProcessError
bool queuedTraciChanges
flag to keep track of whether a timing change has been requested via traci
NEMALogic(MSTLLogicControl &tlcontrol, const std::string &id, const std::string &programID, const SUMOTime offset, const MSSimpleTrafficLightLogic::Phases &phases, int step, SUMOTime delay, const std::map< std::string, std::string > &parameter, const std::string &basePath)
Constructor.
void activateProgram() override
called when switching programs
void setShowDetectors(bool show)
bool whetherOutputState
void constructTimingAndPhaseDefs(std::string &barriers, std::string &coordinates, std::string &ring1, std::string &ring2)
constructs phase using the configuration file
int myPhaseStrLen
stores the length of phase string for the controller "GGrrrrs" = 6. Must be the same length for all p...
int myNumberRings
stores controllers # of rings
PhasePtr defaultBarrierPhases[2][2]
an array to store the phases located at a barrier for each ring
SUMOTime getCurrentTime(void) const
Wrapper Function to Simplify Accessing Time.
std::vector< PhasePtr > myPhaseObjs
a vector that stores a pointer to the instantiated NEMAPhase objects
void setParameter(const std::string &key, const std::string &value) override
try to set the given parameter. Parameters prefixed with 'NEMA.' control functionality
void error_handle_not_set(std::string param_variable, std::string param_name)
throw an InvalidArgument error if the param_name is not set
void implementTraciChanges(void)
implement any pending traci changes This function is called once per cycle
PhasePtr getPhaseObj(int phaseNum, int ringNum=-1)
get the phase object matching the phaseNum If ringNum is passed, it will only search for the phase in...
void setNewOffset(double newOffset)
Set the new offset for the controller.
std::vector< PhasePtr > getPhasesByRing(int ringNum)
get all phases for a given ring
controllerType parseControllerType(std::string inputType)
parse the controllerType from the tllogic description
SUMOTime myCycleLength
the coordinated cycle length
SUMOTime offset
the controller's offset
void calculateInitialPhases170()
calculate the initial phases for Type 170
SUMOTime myNextOffset
the next offset to implement
SUMOTime ModeCycle(SUMOTime a, SUMOTime b)
Calculates the modulus a / b, normally used to calculate the cycle time between two times....
void calculateForceOffs()
directs the code to the correct force off function accorifing to its cabinet type
std::vector< int > readParaFromString(std::string s)
converts a comma separated string into a integer vector "1,2,3,4" -> {1,2,3,4}
std::vector< PhasePtr > getPhaseObjs(void)
get a vector of all phase objects
bool myShowDetectors
Whether the detectors shall be shown in the GUI.
detectorMap myDetectorForPhase
int string2int(std::string s)
convert a string to an integer
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
bool coordinateMode
whether the controller is in coordinated mode or not
PhasePtr myActivePhaseObjs[2]
variable to store the active phases
void deactivateProgram() override
void setCurrentTime(void)
Set the simTime.
~NEMALogic()
Destructor.
MSPhaseDefinition myPhase
virtual phase that holds the current state
PhaseTransitionLogic * getDefaultTransition(PhaseTransitionLogic *t, PhaseTransitionLogic *ot)
return the default transition for t give its and the ot's state
bool vectorContainsPhase(std::vector< int > v, int phaseNum)
check if a vector contains an element
void calculateForceOffsTS2()
calculate the forceOffs for a TS2 style offset From https://ops.fhwa.dot.gov/publications/fhwahop0802...
PhasePtr getOtherPhase(PhasePtr p)
Get the opposite active phase.
double myDetectorLengthLeftTurnLane
store the left turn lane detestor length
controllerType myControllerType
bool isLeftTurnLane(const MSLane *const lane) const
decide whether the detector is for left turn lane if it is, use the detector length for left turn lan...
LaneDetectorMap myLaneDetectorMap
A map from lanes to detectors.
PhasePtr coordinatePhaseObjs[2]
a store of the coordinated phase objects. Only used meaningfully when the controller is in coordinate...
std::map< std::string, int > myLanePhaseMap
A map from lanes names to phases.
std::string composeLightString()
iterates over the two active phases (myActivePhaseObjs) and merges the two active phases
void setNewCycleLength(double newCycleLength)
set the new cycle length for the controller
void setActivePhase(PhasePtr phase)
set the active phase
std::vector< transitionInfo > TransitionPairs
const MSPhaseDefinition & getCurrentPhaseDef() const override
Returns myPhase, which doesn't correspond to a NEMA phase, but rather the composite light string.
std::map< int, std::vector< std::string > > phase2ControllerLanesMap
void setNewSplits(std::vector< double > newSplits)
Set the new splits of all phases.
One phase in the NEMAController.
bool lastDetectActive
store the last detect check for traci purposes
std::string myYellowString
void setDetectors(std::vector< MSE2Collector * > detectors)
sets the detectors for the phase
SUMOTime myLastEnd
SUMOTime vehExt
SUMOTime nextMaxDuration
NEMAPhase(int phaseName, bool isBarrier, bool isGreenRest, bool isCoordinated, bool minRecall, bool maxRecall, bool fixForceOff, int barrierNum, int ringNum, IntVector phaseStringInds, MSPhaseDefinition *phase)
Construct a new NEMAPhase object.
void cleanupExit(void)
public method to set whether phase is active or not
MSPhaseDefinition * myCorePhase
A reference to the core phase of which NEMAPhase wraps.
LightState myLightState
std::string myGreenString
LightState getCurrentState() const
gets the current light state
SUMOTime myExpectedDuration
void setMyNEMAStates(void)
this function replaces getNEMAStates calculation at every call It sets my myGreenString,...
~NEMAPhase()
Destructor.
PhasePtr getSequentialPriorPhase(void)
get the prior phase
bool coordinatePhase
SUMOTime greenRestTimer
a count down timer to track green rest transition time
void clearMyDetectors(void)
Clear My Detectors. Called on all phases at every step.
void checkMyDetectors(void)
Check Detectors. Called on all phases at every step.
SUMOTime yellow
SUMOTime minDuration
PhaseDetectorInfo myDetectorInfo
PhasePtr sequentialPriorPhase
SUMOTime greatestStartTime
std::vector< PhaseTransitionLogic * > trySwitch(NEMALogic *controller)
calculate a vector of potention next phases
std::vector< MSE2Collector * > getDetectors() const
returns a vector of the phases detectors
char getNEMAChar(int i)
Return the ryg light string for the phase.
SUMOTime forceOffTime
stores the force off time in coordinated mode
SUMOTime red
void recalculateTiming(void)
accessory function to recalculate timing
void handleRedXferOrNextPhase(NEMALogic *controller, PhaseTransitionLogic *nextPhases[2])
handles the transition into a red xfer state, which is roughly the same as green rest
SUMOTime getTransitionTimeStateless(void)
Get the Transition time given.
SUMOTime calcVehicleExtension(SUMOTime duration)
}
bool okay2ForceSwitch(NEMALogic *controller)
simple internal check to see if done okay to transition
void init(NEMALogic *controller, int crossPhaseTarget, int crossPhaseSource, bool latching)
initializes the object
PhasePtr myInstance
bool isTransitionActive() const
check if a transition is active
void enterYellow(NEMALogic *controller)
handles the transition into yellow
PhaseTransitionLogic * getTransition(int toPhase)
return the PhaseTransitionLogic matching the toPhase
SUMOTime maxDuration
void handleGreenRestOrTransfer(NEMALogic *controller, PhaseTransitionLogic *nextPhases[2])
handles the transition into a green rest state
bool readyToSwitch
flag to for the supervisory controller to denote whether phase is ready to switch or not.
bool transitionActive
variable to store whether a transition is active or not
std::vector< PhaseTransitionLogic * > myTransitions
stores a sorted list of potential transitions
SUMOTime myStartTime
void forceEnter(NEMALogic *controller)
Force Enter. This Should only be called at initialization time.
PhasePtr myLastPhaseInstance
bool callActive(void)
simple method to check if there is either a recall or an active detector
std::string myRedString
void update(NEMALogic *controller)
update is called on the active phases by the NEMAController at every time step
SUMOTime getTransitionTime(NEMALogic *controller)
Get the Transition Time.
PhaseTransitionLogic * lastTransitionDecision
pointer to save the last transition
void enter(NEMALogic *controller, PhasePtr lastPhase)
handles entry to the phase during simulation Sets the color to green and determines maximum duration
SUMOTime maxGreenDynamic
void exit(NEMALogic *controller, PhaseTransitionLogic *nextPhases[2])
handles the transition out of a phase into the next (puts the phase through (G -> Y -> R) transition
Builds detectors for microsim.
Parameterised * buildE2Detector(const std::string &id, MSLane *lane, double pos, double endPos, double length, const std::string &device, SUMOTime frequency, SUMOTime haltingTimeThreshold, double haltingSpeedThreshold, double jamDistThreshold, const std::string name, const std::string &vTypes, const std::string &nextEdges, int detectPersons, bool friendlyPos, bool showDetector, MSTLLogicControl::TLSLogicVariants *tlls=0, MSLane *toLane=0)
Builds a new E2 detector and adds it to the net's detector control. Also performs some consistency ch...
std::string myID
The name of the object.
Definition Named.h:125
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.
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
This class handles the transition logic between two phases.
int getDistance(PhaseTransitionLogic *otherTrans)
return the ring distance that this transition represents
bool okay(NEMALogic *controller)
This function is the main PhaseTransitionLogic function It is called by the fromPhase to check if a t...
bool freeBase(NEMALogic *controller)
this represents the bare minimum logic, that the toPhase has an active detector and that the fromPhas...
bool coordBase(NEMALogic *controller)
represents the bare minimum coordinate mode logic. Requires that the toPhase can fit its minimum gree...
bool fromBarrier(NEMALogic *controller)
If the fromPhase is at a barrier, then this function will be called to check whether the transition i...
bool fromCoord(NEMALogic *controller)
if the fromPhase is a coordinated phase, then this logic will be checked
PhaseTransitionLogic(PhasePtr fromPhase, PhasePtr toPhase)
Construct a new Phase Transition Logic object.
PhasePtr getFromPhase(void) const
get the from phase
PhasePtr getToPhase(void) const
get the to phase
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
stores information about the phase's detector(s)
bool detectActive
where any of my detectors are active or not
std::vector< MSE2Collector * > detectors
a vector of pointers to the phase's detectors
PhasePtr cpdSource
the cross-phase switching source for myself (1 if 6 should check 1 if 6 is green and I am phase 6)
PhasePtr cpdTarget
the cross-phase switching target for myself (6 if 6 should check 1 if 6 is green and I am phase 1)
bool latching
whether the detectors are latching or not