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-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/****************************************************************************/
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 // Set the first to point to the last, wrapping around the ring.
223 myPhaseObjs[lastPhaseIter]->setSequentialPriorPhase(myPhaseObjs[lastPhaseIter + phaseIter - 1]);
224 // index the ring counter
225 ringNum++;
226 }
227
228 //TODO: set the default phases. This could also be set using dual entry in future
229 for (int i = 0; i < 2; i++) {
230 // create the coordinate phase ptr
231 coordinatePhaseObjs[i] = getPhaseObj(coordinatePhases[i], i);
233 // create the other barrier phase ptr
234 PhasePtr b = getPhaseObj(barrierPhases[i], i);
236 // the barrier 1 and barrier 0 default phase must not have the same barrier number
238 throw ProcessError("At traffic signal " + myID + " the barrier and coordinated phases " +
239 std::to_string(b->phaseName) + ", " + std::to_string(coordinatePhaseObjs[i]->barrierNum) +
240 " are located on the same side of a barrier." +
241 " Please check your configuration file");
242 }
243 }
244
245 // Create the PhaseDetectorInfo for each of the phases (needs knowledge of other phases to create)
246 IntVector latchingDetectors = readParaFromString(getParameter("latchingDetectors", ""));
247 std::vector<std::pair<int, int>> cp;
248 for (auto& p : myPhaseObjs) {
249 std::string cps = "crossPhaseSwitching:";
250 int crossPhase = StringUtils::toInt(getParameter(cps.append(std::to_string(p->phaseName)), "0"));
251 if (crossPhase > 0) {
252 cp.push_back({ p->phaseName, crossPhase });
253 }
254 }
255
256 // Knowing the cross phase info, we can add that to the phase
257 for (auto& p : myPhaseObjs) {
258 bool latching = vectorContainsPhase(latchingDetectors, p->phaseName);
259 int cpTarget = 0;
260 int cpSource = 0;
261 for (auto& cp_pair : cp) {
262 if (cp_pair.first == p->phaseName || cp_pair.second == p->phaseName) {
263 cpTarget = cp_pair.first;
264 cpSource = cp_pair.second;
265 }
266 }
267 p->init(this, cpTarget, cpSource, latching);
268 }
269
270 // Calculate Force offs Based on Timing
272
273 if (coordinateMode) {
274 // Calculate the Initial Phases in coordinated operation only.
275 // Otherwise they have already been calculated above
277 } else {
278 // Fall back being the barrier 0 default phases
279 // NEMAPhase* defaultP[2] = {defaultBarrierPhases[0][0], defaultBarrierPhases[1][0]};
280 NEMAPhase* defaultP[2] = { getPhasesByRing(0).front(), getPhasesByRing(1).front() };
281 defaultP[0]->forceEnter(this);
282 defaultP[1]->forceEnter(this);
283 }
284
285
286#ifdef DEBUG_NEMA
287 //print to check the rings and barriers active phase
288 std::cout << "After init, active ring1 phase is " << myActivePhaseObjs[0]->phaseName << std::endl;
289 std::cout << "After init, active ring2 phase is " << myActivePhaseObjs[1]->phaseName << std::endl;
290
291
292 //print to check the phase definition is correct
293 std::cout << "Print to check NEMA phase definitions\n";
294 for (auto& p : myPhaseObjs) {
295 std::cout << "index = " << p->phaseName << "; ";
296 std::cout << "minDur = " << std::to_string(p->minDuration) << "; ";
297 std::cout << "maxDur = " << std::to_string(p->maxDuration) << "; ";
298 std::cout << "vehext = " << std::to_string(p->vehExt) << "; ";
299 std::cout << "yellow = " << std::to_string(p->yellow) << "; ";
300 std::cout << "red = " << std::to_string(p->red) << "; ";
301 std::cout << "state = " << std::to_string((int)p->getCurrentState()) << std::endl;
302 }
303#endif
304
305
306#ifdef DEBUG_NEMA
307 std::cout << "After init, r1/r2 barrier phase = " << defaultBarrierPhases[0][1]->phaseName << " and " << defaultBarrierPhases[1][1]->phaseName << std::endl;
308 std::cout << "After init, r1/r2 coordinate phase = " << defaultBarrierPhases[0][0]->phaseName << " and " << defaultBarrierPhases[1][0]->phaseName << std::endl;
309#endif
310
311
312#ifdef DEBUG_NEMA
313 std::cout << "R1State = " << myActivePhaseObjs[0]->phaseName << " and its state = " << std::to_string((int)myActivePhaseObjs[0]->getCurrentState()) << std::endl;
314 std::cout << "R2State = " << myActivePhaseObjs[1]->phaseName << " and its state = " << std::to_string((int)myActivePhaseObjs[0]->getCurrentState()) << std::endl;
315#endif
316
317 // Set the initial light state
319 myPhase.setName(toString(myActivePhaseObjs[0]->phaseName) + "+" + toString(myActivePhaseObjs[1]->phaseName));
321 myStep = 0;
322
323 //validating timing
325}
326
327bool
328NEMALogic::vectorContainsPhase(std::vector<int> v, int phaseNum) {
329 if (std::find(v.begin(), v.end(), phaseNum) != v.end()) {
330 return true;
331 }
332 return false;
333}
334
335void
337
338 // TODO: Create a parameter for this
340
341 std::string barriers = getParameter("barrierPhases", "");
342 std::string coordinates = getParameter("coordinatePhases", getParameter("barrier2Phases", ""));
343 std::string ring1 = getParameter("ring1", "");
344 std::string ring2 = getParameter("ring2", "");
345
346 fixForceOff = StringUtils::toBool(getParameter("fixForceOff", "false"));
349 whetherOutputState = StringUtils::toBool(getParameter("whetherOutputState", "false"));
350 coordinateMode = StringUtils::toBool(getParameter("coordinate-mode", "false"));
351
352 // set the queued traci changes to false
353 queuedTraciChanges = false;
354
355 //missing parameter error
356 error_handle_not_set(ring1, "ring1");
357 error_handle_not_set(ring2, "ring2");
358 error_handle_not_set(barriers, "barrierPhases");
359 error_handle_not_set(coordinates, "barrier2Phases or coordinatePhases");
360
361 //print to check
362#ifdef DEBUG_NEMA
363 std::cout << "JunctionID = " << myID << std::endl;
364 std::cout << "All parameters after calling constructor are: " << std::endl;
365 std::cout << "myDetectorLength = " << myDetectorLength << std::endl;
366 std::cout << "cycleLength = " << STEPS2TIME(myCycleLength) << std::endl;
367 std::cout << "ring1 = " << ring1 << std::endl;
368 std::cout << "ring2 = " << ring2 << std::endl;
369 std::cout << "barriers = " << barriers << std::endl;
370 std::cout << "coordinates = " << coordinates << std::endl;
371 std::cout << "offset = " << offset << std::endl;
372 std::cout << "cycleSecond = " << getTimeInCycle() << std::endl;
373 std::cout << "whetherOutputState = " << whetherOutputState << std::endl;
374 std::cout << "myShowDetectors = " << myShowDetectors << std::endl;
375 std::cout << "coordinateMode = " << coordinateMode << std::endl;
376 std::cout << "fixForceOff = " << fixForceOff << std::endl;
377 std::cout << "You reach the end of constructor" << std::endl;
378 std::cout << "****************************************\n";
379#endif
380 // Construct the NEMA specific timing data types and initial phases
381 constructTimingAndPhaseDefs(barriers, coordinates, ring1, ring2);
382
383 //init the traffic light
385 assert(myLanes.size() > 0);
386 //iterate through the lanes and build one E2 detector for each lane associated with the traffic light control junction
387 for (const LaneVector& lanes : myLanes) {
388 for (MSLane* const lane : lanes) {
389 //decide the detector length
390 double detector_length = 0;
391 if (isLeftTurnLane(lane)) {
392 detector_length = myDetectorLengthLeftTurnLane;
393 } else {
394 detector_length = myDetectorLength;
395 }
396 if (noVehicles(lane->getPermissions())) {
397 // do not build detectors on green verges or sidewalks
398 continue;
399 }
400 // Build detector and register them in the detector control
401 if (myLaneDetectorMap.find(lane) == myLaneDetectorMap.end()) {
402 MSE2Collector* det = nullptr;
403 const std::string customID = getParameter(lane->getID());
404 if (customID != "") {
406 if (det == nullptr) {
407 throw ProcessError("Unknown laneAreaDetector '" + customID + "' given as custom detector for NEMA tlLogic '" + getID() + "', program '" + getProgramID() + ".");
408 }
409 //set the detector to be visible in gui
411 } else {
412 int phaseNumber = 0;
413 if (myLanePhaseMap.find(lane->getID()) != myLanePhaseMap.end()) {
414 phaseNumber = myLanePhaseMap.find(lane->getID())->second;
415 }
416 int index = lane->getIndex();
417 std::string id = myID + "_" + myProgramID + "_D" + toString(phaseNumber) + "." + toString(index);
418 while (MSNet::getInstance()->getDetectorControl().getTypedDetectors(SUMO_TAG_LANE_AREA_DETECTOR).get(id) != nullptr) {
419 index++;
420 id = myID + "_" + myProgramID + "_D" + toString(phaseNumber) + "." + toString(index);
421 }
422 //createE2Detector() method will lead to bad detector showing in sumo-gui
423 //so it is better to use build2Detector() rather than createE2Detector()
424 nb.buildE2Detector(id, //detectorID
425 lane, //lane to build this detector
426 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
427 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
428 detector_length, //detector length
429 myFile, // detector information output file
430 myFreq, // detector reading interval
431 0, // time-based threshold that describes how much time has to pass until a vehicle is considered as halting
432 0, // speed threshold as halting
433 0, // minimum dist to the next standing vehicle to make this vehicle count as a participant to the jam
434 "",
435 myVehicleTypes, //vehicle types to consider, if it is empty, meaning consider all types of vehicles
436 "", // nextEdges (no filtering by vehicle route)
437 (int)PersonMode::NONE, // detector vehicles, not persons
438 true, // whether to give some slack on positioning
439 myShowDetectors, // whether to show detectors in sumo-gui
440 nullptr, //traffic light that triggers aggregation when switching
441 nullptr); // outgoing lane that associated with the traffic light
442
443 //get the detector to be used in the lane detector map loading
445 }
446
447 //map the detector to lane and lane to detector
448 myLaneDetectorMap[lane] = det;
449 myDetectorLaneMap[det] = lane;
450 myDetectorInfoVector.push_back(DetectorInfo(det, (int)myPhases.size()));
451
452 }
453 }
454 }
455 for (auto item : phase2ControllerLanesMap) {
456 int NEMAPhaseIndex = item.first;
457 std::vector<std::string> laneIDs = item.second;
458 std::vector<MSE2Collector*> detectors;
459 MSE2Collector* detector = nullptr;
460 for (std::string laneID : laneIDs) {
461 MSLane* lane = MSLane::dictionary(laneID);
462 detector = myLaneDetectorMap[lane];
463 detectors.push_back(detector);
464 }
465 // have to try this on both rings, because of the case where both rings have the same phase
466 // See Basic NEMA test
467 for (int i = 0; i < 2; i++) {
468 if (vectorContainsPhase(rings[i], NEMAPhaseIndex)) {
469 getPhaseObj(NEMAPhaseIndex, i)->setDetectors(detectors);
470 }
471 }
472 }
473
474 //Do not delete. SUMO traffic logic check.
475 //SUMO check begin
476 const SVCPermissions motorized = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
477 std::map<int, std::set<MSE2Collector*>> linkToDetectors;
478 std::set<int> actuatedLinks;
479
480 const int numLinks = (int)myLinks.size();
481 std::vector<bool> neverMajor(numLinks, true);
482 for (const MSPhaseDefinition* phase : myPhases) {
483 const std::string& state = phase->getState();
484 for (int i = 0; i < numLinks; i++) {
485 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
486 neverMajor[i] = false;
487 }
488 }
489 }
490 std::vector<bool> oneLane(numLinks, false);
491 for (int i = 0; i < numLinks; i++) {
492 for (MSLane* lane : getLanesAt(i)) {
493 int numMotorized = 0;
494 for (MSLane* l : lane->getEdge().getLanes()) {
495 if ((l->getPermissions() & motorized) != 0) {
496 numMotorized++;
497 }
498 }
499 if (numMotorized == 1) {
500 oneLane[i] = true;
501 break;
502 }
503 }
504 }
505
506 for (const MSPhaseDefinition* phase : myPhases) {
507 const int phaseIndex = (int)myDetectorForPhase.size();
508 std::set<MSE2Collector*> detectors;
509 if (phase->isActuated()) {
510 const std::string& state = phase->getState();
511 std::set<int> greenLinks;
512 std::map<MSE2Collector*, std::set<int>> detectorLinks;
513
514 for (int i = 0; i < numLinks; i++) {
515 if (state[i] == LINKSTATE_TL_GREEN_MAJOR
516 || (state[i] == LINKSTATE_TL_GREEN_MINOR
517 && ((neverMajor[i] // check1a
518 && hasMajor(state, getLanesAt(i))) // check1b
519 || oneLane[i])) // check1c
520 ) {
521 greenLinks.insert(i);
522 actuatedLinks.insert(i);
523 }
524
525 for (MSLane* lane : getLanesAt(i)) {
526 if (myLaneDetectorMap.count(lane) != 0) {
527 detectorLinks[myLaneDetectorMap[lane]].insert(i);
528 }
529 }
530 }
531 for (auto& item : detectorLinks) {
532 MSE2Collector* det = item.first;
533 MSLane* detectorLane = myDetectorLaneMap[det];
534 bool usable = true;
535 // check 1
536 for (int j : item.second) {
537 if (greenLinks.count(j) == 0) {
538 usable = false;
539 }
540 }
541
542 //check 2
543 if (usable) {
544 for (MSLink* link : detectorLane->getLinkCont()) {
545 MSLane* next = link->getLane();
546 if (myLaneDetectorMap.count(next) != 0) {
547 MSE2Collector* nextDet = myLaneDetectorMap[next];
548 for (int j : detectorLinks[nextDet]) {
549 if (greenLinks.count(j) == 0) {
550 usable = false;
551 break;
552 }
553 }
554 }
555 }
556 }
557
558 if (usable) {
559 detectors.insert(item.first);
560 for (int j : item.second) {
561 linkToDetectors[j].insert(item.first);
562 }
563 }
564 }
565 if (detectors.size() == 0) {
566 WRITE_WARNINGF(TL("At NEMA tlLogic '%', actuated phase % has no controlling detector"), getID(), toString(phaseIndex));
567 }
568 }
569 std::vector<DetectorInfo*> detectorInfos;
570 myDetectorForPhase.push_back(detectorInfos);
571 for (MSE2Collector* det : detectors) {
572 for (DetectorInfo& detInfo : myDetectorInfoVector) {
573 if (detInfo.det == det) {
574 myDetectorForPhase.back().push_back(&detInfo);
575 detInfo.servedPhase[phaseIndex] = true;
576 }
577 }
578 }
579 }
580
581 for (int i : actuatedLinks) {
582 if (linkToDetectors[i].size() == 0 && myLinks[i].size() > 0
583 && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
584 WRITE_WARNINGF(TL("At NEMA tlLogic '%, linkIndex % has no controlling detector"), getID(), toString(i));
585 }
586 }
587}
588
589void
591 // check that the cycle length for each ring adds up to the specified cycle length
592 for (int ringIndex = 0; ringIndex < 2; ringIndex++) {
593 SUMOTime cycleLengthCalculated = 0;
594 for (auto& p : getPhasesByRing(ringIndex)) {
595 cycleLengthCalculated += (p->maxDuration + p->yellow + p->red);
596 }
597 if (coordinateMode && (cycleLengthCalculated != myCycleLength)) {
598 int ringNumber = ringIndex + 1;
599 const std::string error = "At NEMA tlLogic '" + getID() + "', Ring " + toString(ringNumber) + " does not add to cycle length.";
600 if (ignoreErrors) {
601 WRITE_WARNING(error);
602 } else {
603 throw ProcessError(error);
604 }
605 }
606 }
607 // check that the barriers sum together
608 SUMOTime cycleLengths[2][2] = { {0, 0}, {0, 0} };
609 for (int ringIndex = 0; ringIndex < 2; ringIndex++) {
610 // TS2 Force Offs don't go in order, so using a different method to check cycle time
611 for (const auto p : getPhasesByRing(ringIndex)) {
612 cycleLengths[ringIndex][p->barrierNum] += p->maxDuration + p->yellow + p->red;
613 }
614 }
615 // Write warnings if the barriers do not sum
616 for (int barrierNum = 0; barrierNum < 2; barrierNum++) {
617 if (cycleLengths[0][barrierNum] != cycleLengths[1][barrierNum]) {
618 const std::string error = "At NEMA tlLogic '" + getID() + "', the phases before barrier " + toString(barrierNum + 1) + " from both rings do not add up. (ring1="
619 + toString(STEPS2TIME(cycleLengths[0][barrierNum])) + ", ring2=" + toString(STEPS2TIME(cycleLengths[1][barrierNum])) + ")";
621 throw ProcessError(error);
622 } else {
623 WRITE_WARNING(error);
624 }
625 }
626 }
627
628 // no offset for non coordinated
629 if (!coordinateMode && offset != 0) {
630 WRITE_WARNINGF(TL("NEMA tlLogic '%' is not coordinated but an offset was set."), getID());
631 }
632}
633
634void
635NEMALogic::setNewSplits(std::vector<double> newSplits) {
636 assert(newSplits.size() == 8);
637 for (auto& p : myPhaseObjs) {
638 if (newSplits[p->phaseName - 1] > 0) {
639 // set the phase's nextMaxDuration. This will be implemented when implementTraciChanges is called
640 p->nextMaxDuration = TIME2STEPS(newSplits[p->phaseName - 1]) - p->yellow - p->red;
641 }
642 }
643}
644
645
646void
647NEMALogic::setNewMaxGreens(std::vector<double> newMaxGreens) {
648 for (auto& p : myPhaseObjs) {
649 if (newMaxGreens[p->phaseName - 1] > 0) {
650 // set the phase's nextMaxDuration. This will be implemented when implementTraciChanges is called
651 p->nextMaxDuration = TIME2STEPS(newMaxGreens[p->phaseName - 1]);
652 }
653 }
654}
655
656
657void
658NEMALogic::setNewCycleLength(double newCycleLength) {
659 // set the controller's next cycle length. This will be implemented when implementTraciChanges is called
660 myNextCycleLength = TIME2STEPS(newCycleLength);
661}
662
663
664void
665NEMALogic::setNewOffset(double newOffset) {
666 // set the controller's offset. This will be implemented when implementTraciChanges is called
667 myNextOffset = TIME2STEPS(newOffset);
668}
669
670
671std::vector<int> NEMALogic::readParaFromString(std::string s) {
672 std::vector<int> output;
673 for (char c : s) {
674 if (c >= '0' && c <= '9') {
675 int temp = c - '0';
676 output.push_back(temp);
677 }
678 }
679 return output;
680}
681
684 return myPhase;
685}
686
687int NEMALogic::measureRingDistance(int p1, int p2, int ringNum) {
688 int length = (int)rings[ringNum].size();
689 int d = 0;
690 bool found = false;
691 // Loop around the ring and keep track of the distance from p1 to p2
692 for (int i = 0; i < (length * 2); i++) {
693 if (rings[ringNum][i % length] > 0) {
694 if (found) {
695 d++;
696 if (rings[ringNum][i % length] == p2) {
697 break;
698 }
699 } else if (rings[ringNum][i % length] == p1) {
700 found = true;
701 }
702 }
703 }
704 assert(d > 0);
705 return d;
706}
707
708
711 SUMOTime c = a - b;
712 while (c >= b) {
713 c = c - b;
714 }
715 while (c < 0) {
716 c += b;
717 }
718 return c;
719}
720
721
722void
723NEMALogic::getLaneInfoFromNEMAState(std::string state, StringVector& laneIDs, IntVector& stateIndex) {
724 std::set<std::string> output;
725 for (int i = 0; i < (int)myLinks.size(); i++) {
726 if (myLinks[i].empty()) {
727 // unused index
728 continue;
729 }
730 char ch = state[i];
731 // if the ch is 'G', it means that the phase is controlling this lane
732 if (ch == 'G') {
733 stateIndex.push_back(i);
734 for (auto link : myLinks[i]) {
735 const MSLane* incoming = link->getLaneBefore();
736 if (incoming->isNormal()) {
737 laneIDs.push_back(incoming->getID());
738 }
739 }
740 }
741 }
742}
743
744bool NEMALogic::isLeftTurnLane(const MSLane* const lane) const {
745 const std::vector<MSLink*> links = lane->getLinkCont();
746 if (links.size() == 1 && links.front()->getDirection() == LinkDirection::LEFT) {
747 return true;
748 }
749 return false;
750}
751
752bool
753NEMALogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
754 for (int i = 0; i < (int)state.size(); i++) {
755 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
756 for (MSLane* cand : getLanesAt(i)) {
757 for (MSLane* lane : lanes) {
758 if (lane == cand) {
759 return true;
760 }
761 }
762 }
763 }
764 }
765 return false;
766}
767
768
769void
772 for (auto& item : myLaneDetectorMap) {
773 item.second->setVisible(true);
774 }
775}
776
777void
780 for (auto& item : myLaneDetectorMap) {
781 item.second->setVisible(false);
782 }
783}
784
785void
787 myShowDetectors = show;
788 for (auto& item : myLaneDetectorMap) {
789 item.second->setVisible(myShowDetectors);
790 }
791}
792
793int NEMALogic::string2int(std::string s) {
794 std::stringstream ss(s);
795 int ret = 0;
796 ss >> ret;
797 return ret;
798}
799
800
801const std::string
802NEMALogic::getParameter(const std::string& key, const std::string defaultValue) const {
803 if (StringUtils::startsWith(key, "NEMA.")) {
804 if (key == "NEMA.phaseCall") {
805 int activeCalls[8] = { 0 };
806 for (const auto p : myPhaseObjs) {
807 // This handles the case when the controller has multiple of the same phase call
808 if (!activeCalls[p->phaseName - 1]) {
809 activeCalls[p->phaseName - 1] = 1 * p->lastDetectActive;
810 }
811 }
812 std::string outStr = "";
813 for (int i = 0; i < 8; i++) {
814 outStr += std::to_string(activeCalls[i]);
815 if (i < 7) {
816 outStr += ",";
817 }
818 }
819 return outStr;
820 } else {
821 throw InvalidArgument("Unsupported parameter '" + key + "' for NEMA controller '" + getID() + "'");
822 }
823 } else {
824 return Parameterised::getParameter(key, defaultValue);
825 }
826}
827
828
829void
830NEMALogic::setParameter(const std::string& key, const std::string& value) {
831 queuedTraciChanges = true;
832 if (StringUtils::startsWith(key, "NEMA.")) {
833 if (key == "NEMA.splits" || key == "NEMA.maxGreens") {
834 //splits="2.0 3.0 4.0 5.0 2.0 3.0 4.0 5.0"
835 const std::vector<std::string>& tmp = StringTokenizer(value).getVector();
836 if (tmp.size() != 8) {
837 queuedTraciChanges = false;
838 throw InvalidArgument("Parameter '" + key + "' for NEMA controller '" + getID() + "' requires 8 space or comma separated values");
839 }
840 std::vector<double> timing;
841 for (const std::string& s : tmp) {
842 timing.push_back(StringUtils::toDouble(s));
843 }
844 if (key == "NEMA.maxGreens") {
845 setNewMaxGreens(timing);
846 } else {
847 setNewSplits(timing);
848 }
849 } else if (key == "NEMA.cycleLength") {
851 } else if (key == "NEMA.offset") {
853 } else {
854 queuedTraciChanges = false;
855 throw InvalidArgument("Unsupported parameter '" + key + "' for NEMA controller '" + getID() + "'");
856 }
857 }
858 Parameterised::setParameter(key, value);
859}
860
861void
862NEMALogic::error_handle_not_set(std::string param_variable, std::string param_name) {
863 if (param_variable == "") {
864 throw InvalidArgument("Please set " + param_name + " for NEMA tlLogic '" + getID() + "'");
865 }
866}
867
868void
870 SUMOTime zeroTime[2] = { TIME2STEPS(0), TIME2STEPS(0) };
871 for (int i = 0; i < 2; i++) {
872 SUMOTime runningTime = 0;
873 // loop through the phases for ring 0 and then 1
874 for (auto& p : getPhasesByRing(i)) {
875 runningTime += p->maxDuration + p->getTransitionTimeStateless();
876 // in 170, the cycle "starts" when the coordinated phase goes to yellow.
877 // See https://ops.fhwa.dot.gov/publications/fhwahop08024/chapter6.html
878 if (p->coordinatePhase) {
879 zeroTime[i] = runningTime;
880 }
881 p->forceOffTime = runningTime - p->getTransitionTimeStateless();
882 p->greatestStartTime = p->forceOffTime - p->minDuration;
883 }
884 }
885 // find the minimum offset time and then subtract from everything, modecycling where negative
886 // This sets the 0 cycle time as start of yellow on earliest ending coordinated phase
887 SUMOTime minCoordYellow = MIN2(zeroTime[0], zeroTime[1]);
888 for (auto& p : myPhaseObjs) {
889 p->forceOffTime = ModeCycle(p->forceOffTime - minCoordYellow, myCycleLength);
890 p->greatestStartTime = ModeCycle(p->greatestStartTime - minCoordYellow, myCycleLength);
891 }
892
893#ifdef DEBUG_NEMA
894 std::ios_base::fmtflags oldflags = std::cout.flags();
895 std::streamsize oldprecision = std::cout.precision();
896 for (int i = 0; i < 2; i++) {
897 std::cout << "Ring" << i + 1 << " force offs: \t";
898 for (auto& p : rings[i]) {
899 if (p > 0) {
900 PhasePtr pObj = getPhaseObj(p, i);
901 std::cout << std::fixed << std::setprecision(2) << STEPS2TIME(pObj->forceOffTime) << "\t";
902 } else {
903 std::cout << std::to_string(0) << "\t";
904 }
905 }
906 std::cout << std::endl;
907 }
908 std::cout.flags(oldflags);
909 std::cout.precision(oldprecision);
910#endif
911}
912
913void
915 // TS2 "0" cycle time is the start of the "first" coordinated phases.
916 // We can find this "0" point by first constructing the forceOffs in sequential order via the 170 method
918
919 // Switch the Force Off Times to align with TS2 Cycle, which is the *start* of the earliest coordinated phase
920 // The coordinate phases will always be the defaultBarrierPhases[i][0]
921 SUMOTime minCoordTime = MIN2(coordinatePhaseObjs[0]->forceOffTime - coordinatePhaseObjs[0]->maxDuration,
922 coordinatePhaseObjs[1]->forceOffTime - coordinatePhaseObjs[1]->maxDuration);
923
924 // loop through all the phases and subtract this minCoordTime to move the 0 point to the start of the first coordinated phase
925 for (auto& p : myPhaseObjs) {
926 if ((p->forceOffTime - minCoordTime) >= 0) {
927 p->forceOffTime -= (minCoordTime);
928 } else {
929 p->forceOffTime = (myCycleLength + (p->forceOffTime - (minCoordTime)));
930 }
931 p->greatestStartTime = ModeCycle(p->greatestStartTime - minCoordTime, myCycleLength);
932 }
933}
934
935void
937 // get the time in the cycle
939 NEMAPhase* activePhases[2];
940 for (int i = 0; i < 2; i++) {
941 std::vector<NEMAPhase*> ringCopy = getPhasesByRing(i);
942 // sort by the minimum start time in the cycle.
943 std::sort(ringCopy.begin(), ringCopy.end(),
944 [](NEMAPhase * p, NEMAPhase * p1) {
945 return p->greatestStartTime <= p1->greatestStartTime;
946 }
947 );
948 bool found = false;
949 // loop through the sorted phases by time and try to find the phase that should be active given the time in the cycle
950 for (auto& p : ringCopy) {
951 // This handles the wrap around. Checks if the prior phases start time should have already happened.
952 // 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 )
955 if (cycleTime <= ModeCycle(p->greatestStartTime, myCycleLength) && cycleTime > ModeCycle(syntheticPriorStart, myCycleLength)) {
956 found = true;
957 activePhases[i] = p;
958 break;
959 }
960 }
961 if (!found) {
962#ifdef DEBUG_NEMA
963 const std::string error = "I can't find the correct phase for NEMA tlLogic '" + getID() + "' Ring " + toString(i) + " to start in.";
964 WRITE_WARNING(error);
965 WRITE_WARNING(TL("I am starting in the coordinated phases"));
966#endif
967 activePhases[0] = defaultBarrierPhases[0][0];
968 activePhases[1] = defaultBarrierPhases[1][0];
969 }
970 }
971
972 // 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
973 if (activePhases[0]->barrierNum != activePhases[1]->barrierNum) {
974 // give preference to whatever is on the coordinate side of the barrier, one must be if they aren't equal to each other
975 activePhases[0] = activePhases[0]->barrierNum == 0 ? activePhases[0] : defaultBarrierPhases[0][0];
976 activePhases[1] = activePhases[1]->barrierNum == 0 ? activePhases[1] : defaultBarrierPhases[1][0];
977 }
978
979 // force enter the phases and update their expected duration to last until the forceOff
980 activePhases[0]->forceEnter(this);
981 activePhases[1]->forceEnter(this);
982}
983
984void
986 // Modifications where made to 170 algorithm so that it works with both.
988}
989
991NEMALogic::parseControllerType(std::string inputType) {
992 std::string cleanString;
993 for (const char& c : inputType) {
994 if (isalpha(c) || isdigit(c)) {
995 cleanString += (char)::tolower(c);
996 }
997 }
998 if (cleanString == "type170") {
999 return Type170;
1000 } else if (cleanString == "ts2") {
1001 return TS2;
1002 } else {
1003 throw InvalidArgument("Please set controllerType for NEMA tlLogic " + myID + " to either Type170 or TS2");
1004 }
1005}
1006
1007std::vector<NEMAPhase*>
1009 std::vector<NEMAPhase*> phases;
1010 for (auto& p : myPhaseObjs) {
1011 if (p->ringNum == ringNum) {
1012 phases.push_back(p);
1013 }
1014 }
1015 return phases;
1016}
1017
1018void
1022
1023std::map<std::string, double>
1025 std::map<std::string, double> result;
1026 for (auto item : myDetectorLaneMap) {
1027 result[item.first->getID()] = item.first->getCurrentVehicleNumber();
1028 }
1029 return result;
1030}
1031
1034 // return a pointer to the other active phase
1035 return myActivePhaseObjs[!p->ringNum];
1036}
1037
1038NEMAPhase*
1039NEMALogic::getPhaseObj(int phaseNum, int ringNum) {
1040 // This satisfies the case where there is a "duplicate" phase on both ring
1041 std::vector<PhasePtr> iterRing = ringNum >= 0 ? getPhasesByRing(ringNum) : myPhaseObjs;
1042 for (auto& p : iterRing) {
1043 if (p->phaseName == phaseNum) {
1044 return p;
1045 }
1046 }
1047 // the phase must always be found
1048 throw ProcessError("At traffic signal '" + myID + "' program '" + myProgramID + "' phase '" + toString(phaseNum) + "' not found in ring '" + toString(ringNum) + "'.");
1049}
1050
1053 NEMAPhase* p = t->getFromPhase();
1054 // if the current phase is not ready to switch or a barrier cross is desired by the other transition
1055 // and t fromPhase is not ready to switch, the default transition is back to myself
1056 if (!p->readyToSwitch ||
1058 return p->getTransition(p->phaseName);
1059 }
1060 // otherwise the default transition is to the default phase on whatever barrier ot wants to transition to
1061 else {
1063 }
1064}
1065
1066void
1068 std::vector<std::vector<PhaseTransitionLogic* >> potentialPhases;
1069
1070 // Get a vector of each phases' potential transitions
1071 for (const auto& p : myActivePhaseObjs) {
1072 potentialPhases.push_back(p->trySwitch(this));
1073 }
1074
1075 // Loop through all combination of transitions, keeping only the valid ones and filling in the gaps where necessary
1076 for (const auto& r1_t : potentialPhases[0]) {
1077 for (const auto& r2_t : potentialPhases[1]) {
1078 // if both transitions go to the same barrier then we are good
1079 if (r1_t->getToPhase()->barrierNum == r2_t->getToPhase()->barrierNum) {
1080 transitions.push_back({ r1_t, r2_t, (float)(r1_t->getDistance(r2_t) + r2_t->getDistance(r1_t)) / 2 });
1081 } else {
1082 // If the rings are different, add a choice where one of them is the default choice for whatever ring it is
1083 // create two choices, one for each of the phase as they are on different rings
1084 if (r1_t->getFromPhase()->readyToSwitch) {
1085 // get the r2 default
1086 PhaseTransitionLogic* r2_t_temp = getDefaultTransition(r2_t, r1_t);
1087 // only add it if it does not cross a barrier!
1088 if (r2_t_temp->getToPhase()->barrierNum == r1_t->getToPhase()->barrierNum) {
1089 transitions.push_back({ r1_t, r2_t_temp, (float)(r2_t_temp->getDistance(r1_t) + r1_t->getDistance(r2_t_temp)) / 2 });
1090 }
1091 }
1092 if (r2_t->getFromPhase()->readyToSwitch) {
1093 // R1 default
1094 PhaseTransitionLogic* r1_t_temp = getDefaultTransition(r1_t, r2_t);
1095 // only add it if it does not cross a barrier!
1096 if (r1_t_temp->getToPhase()->barrierNum == r2_t->getToPhase()->barrierNum) {
1097 transitions.push_back({ r1_t_temp, r2_t, (float)(r2_t->getDistance(r1_t_temp) + r1_t_temp->getDistance(r2_t)) / 2 });
1098 }
1099 }
1100 // If the distances are <= 1, this means that this is the shortest transition possible
1101 // and we can return early without considering any other options
1102 if (!transitions.empty()) {
1103 if (transitions.back().distance < 1) {
1104 return;
1105 }
1106 }
1107 }
1108 }
1109 }
1110}
1111
1112
1113std::string
1115 // FIX with plan to support #10742
1116 std::string out(myPhaseStrLen, 'r');
1117 for (int i = 0; i < myPhaseStrLen; i++) {
1118 bool controlled = false;
1119 std::string phaseChars = "";
1120 for (auto& p : myActivePhaseObjs) {
1121 phaseChars += p->getNEMAChar(i);
1122 if (p->controlledIndex(i)) {
1123 out[i] = p->getNEMAChar(i);
1124 controlled = true;
1125 }
1126 }
1127 // if the index wasn't a controlled one, the prior priority order still stands
1128 if (!controlled) {
1129 for (auto priority_char : lightHeadPriority) {
1130 if (std::count(phaseChars.begin(), phaseChars.end(), priority_char)) {
1131 out[i] = priority_char;
1132 break;
1133 }
1134 }
1135 }
1136 }
1137 return out;
1138}
1139
1140
1143#ifdef DEBUG_NEMA_SWITCH
1144 std::cout << SIMTIME << " trySwitch tls=" << getID() << "\n";
1145#endif
1146 PhaseTransitionLogic* nextPhases[2] = { nullptr, nullptr };
1147
1148 // update the internal time. This is a must. Could have just used a reference to the time
1150
1151 // Check the Detectors
1152 for (auto& p : myPhaseObjs) {
1153 p->checkMyDetectors();
1154 }
1155
1156 // Update the timing parameters
1157 for (const auto& p : myActivePhaseObjs) {
1158 p->update(this);
1159 }
1160
1161 // Calculate the Next Phases, but only if atleast one of them is ready to transition
1162 if (myActivePhaseObjs[0]->readyToSwitch || myActivePhaseObjs[1]->readyToSwitch) {
1163 TransitionPairs transitions;
1164 // set the next phases by reference
1165 getNextPhases(transitions);
1166
1167 // Sort the next phases by distance and select the closest.
1168 // TODO: Is there a way to avoid this sort? The transitions are already sorted by distance prior
1169 // to picking the valid ones
1170 if (transitions.size() > 1) {
1171 std::sort(transitions.begin(), transitions.end(),
1172 [](const transitionInfo & i, const transitionInfo & j) {
1173 return i.distance < j.distance;
1174 });
1175 }
1176
1177 // Set the Next Phases = to the transition with least combined distance
1178 nextPhases[0] = transitions.front().p1;
1179 nextPhases[1] = transitions.front().p2;
1180
1181 // Try the exit logic. This doesn't necessarily mean that the phase will exit,
1182 // as it could go into green rest or green transfer, but this is considered an "exit"
1183 for (const auto& p : myActivePhaseObjs) {
1184 if (p->readyToSwitch) {
1185 p->exit(this, nextPhases);
1186 }
1187 }
1188
1189 // This is the only time when something might have happened, so we update the phase strings here
1190 std::string newState = composeLightString();
1191 if (newState != myPhase.getState()) {
1192 myPhase.setState(newState);
1193 myPhase.setName(toString(myActivePhaseObjs[0]->phaseName) + "+" + toString(myActivePhaseObjs[1]->phaseName));
1194 // ensure that SwitchCommand::execute notices a change
1195 myStep = 1 - myStep;
1196
1197 }
1198 }
1199
1200 // clear the phases' detectors
1201 for (auto& p : myPhaseObjs) {
1202 p->clearMyDetectors();
1203 }
1204
1205
1206#ifdef FUZZ_TESTING
1207 // Basic Assertion to ensure that the Barrier is not crossed
1208 assert(myActivePhaseObjs[0]->barrierNum == myActivePhaseObjs[1]->barrierNum);
1209#endif
1210
1211 // return the simulation timestep, as this controller must be checked every simulation step
1212 return DELTA_T;
1213}
1214
1215
1216void
1218 // Implement Traci Updates on the start of ring1 coordinated phase (rising edge of it turning green)
1219 if (queuedTraciChanges) {
1220 for (auto& p : myPhaseObjs) {
1221 p->maxDuration = p->nextMaxDuration;
1222 }
1225 // now that we have set the cycle length, offset and max duration, we need to update force off times
1227 queuedTraciChanges = false;
1228 }
1229}
1230
1231
1232// ===========================================================================
1233// NEMAPhase Definitions
1234// ===========================================================================
1235NEMAPhase::NEMAPhase(int phaseName, bool isBarrier, bool isGreenRest, bool isCoordinated,
1236 bool minRecall, bool maxRecall, bool fixForceOff, int barrierNum, int ringNum,
1237 IntVector phaseStringInds,
1238 MSPhaseDefinition* phase) :
1239 phaseName(phaseName),
1240 isAtBarrier(isBarrier),
1241 isGreenRest(isGreenRest),
1242 barrierNum(barrierNum),
1243 coordinatePhase(isCoordinated),
1244 minRecall(minRecall),
1245 maxRecall(maxRecall),
1246 fixForceOff(fixForceOff),
1247 ringNum(ringNum),
1248 myCorePhase(phase),
1249 myPhaseStringInds(phaseStringInds) {
1250 // Public
1251 readyToSwitch = false;
1252 greenRestTimer = 0;
1253 forceOffTime = 0;
1254 lastDetectActive = false;
1255
1256 // Private
1257 myInstance = this;
1258 myLastPhaseInstance = nullptr;
1259 sequentialPriorPhase = nullptr;
1261 transitionActive = false;
1262
1263 // Timing Parameters
1265 myStartTime = TIME2STEPS(0.);
1267 myLastEnd = TIME2STEPS(0.);
1268
1269 // set the phase colors
1271}
1272
1274 // Delete the transitions from their alloc
1275 for (auto t : myTransitions) {
1276 delete t;
1277 }
1278}
1279
1280void
1281NEMAPhase::init(NEMALogic* controller, int crossPhaseTarget, int crossPhaseSource, bool latching) {
1282 // switch the durations from steps2time
1284
1285 for (auto p : controller->getPhasesByRing(ringNum)) {
1286 // construct transitions for all potential movements, including back to myself
1287 myTransitions.push_back(new PhaseTransitionLogic(this, p));
1288 myTransitions.back()->setDistance(controller->measureRingDistance(phaseName, p->phaseName, ringNum));
1289 }
1290
1291 // sort the transitions by distance for speed later. Using plain distance here
1292 std::sort(myTransitions.begin(), myTransitions.end(), [&](const PhaseTransitionLogic * i, const PhaseTransitionLogic * j) {
1293 return i->distance < j->distance;
1294 });
1295
1296 // create the phase detector info
1298 crossPhaseSource > 0 ? controller->getPhaseObj(crossPhaseSource) : nullptr,
1299 crossPhaseTarget > 0 ? controller->getPhaseObj(crossPhaseTarget) : nullptr
1300 );
1301}
1302
1303void
1305 // This could be extended in the future to allow for traci manipulation
1307 red = myCorePhase->red;
1313}
1314
1315
1316// TODO: this can be computed once.
1317char
1320 return myGreenString[i];
1321 } else if (myLightState <= LightState::Red) {
1322 return myRedString[i];
1323 } else {
1324 return myYellowString[i];
1325 }
1326}
1327
1328void
1331 myRedString = "";
1332 myYellowString = "";
1333 for (char ch : myGreenString) {
1334 myRedString += 'r';
1335 if (ch == 'G' || ch == 'g') {
1336 myYellowString += 'y';
1337 } else {
1338 myYellowString += ch;
1339 }
1340 }
1341}
1342
1343void
1346 // remove the active flag on the detector if the detector is not latching or if it is green
1349 }
1350}
1351
1352void
1354 // Check my Detectors, only necessary if it isn't currently marked as on
1356 // If I have a cross phase target and it is active and I am not, save my detector as not active
1357 if (myDetectorInfo.cpdTarget != nullptr) {
1361 return;
1362 }
1363 }
1364 }
1365 // If we make it to this point, check my detector like normal.
1366 for (auto& d : myDetectorInfo.detectors) {
1367 if (d->getCurrentVehicleNumber() > 0) {
1369 return;
1370 }
1371 }
1372 // If my detector is not active, check my cross phase
1373 if ((myDetectorInfo.cpdSource != nullptr) && (myLightState >= LightState::Green)) {
1375 for (auto& d : myDetectorInfo.cpdSource->getDetectors()) {
1376 if (d->getCurrentVehicleNumber() > 0) {
1378 return;
1379 }
1380 }
1381 }
1382 }
1383 }
1384}
1385
1386void
1387NEMAPhase::enter(NEMALogic* controller, NEMAPhase* lastPhase) {
1388#ifdef DEBUG_NEMA_SWITCH
1389 std::cout << SIMTIME << " enter tls=" << controller->getID() << " phase=" << phaseName << "\n";
1390#endif
1391
1392 // set the last phase instance to inactive
1393 lastPhase->cleanupExit();
1394
1395 // Enter the phase
1396 myStartTime = controller->getCurrentTime();
1398 myLastPhaseInstance = lastPhase;
1399 readyToSwitch = false;
1400
1401 // implement the new timing parameters on the first coordinated phase to appear
1402 if (phaseName == controller->coordinatePhaseObjs[ringNum]->phaseName) {
1403 controller->implementTraciChanges();
1404 }
1405
1406 // Handle Green Rest Peculiarities
1407 if (!controller->coordinateMode && isGreenRest) {
1408 // If the controller is in free mode and the phase is a green rest phase, then it should enter as "green rest"
1410 // if the phase has "green rest" capabilities, set its timer to the dynamic maxGreen
1412 }
1413
1414 // clear the last transition decision
1415 lastTransitionDecision = nullptr;
1416
1417 // Calculate the Max Green Time & Expected Duration here:
1418 if (controller->coordinateMode) {
1419 if (coordinatePhase) {
1420 myExpectedDuration = controller->ModeCycle(forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1421 } else {
1422 maxGreenDynamic = controller->ModeCycle(forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1423 if (!fixForceOff) {
1425 }
1427 }
1428 } else {
1430 }
1431 // Implements the maxRecall functionality
1432 if (maxRecall && !coordinatePhase) {
1434 }
1435 // Set the controller's active phase
1436 controller->setActivePhase(this);
1437}
1438
1439void NEMAPhase::exit(NEMALogic* controller, PhaseTransitionLogic* nextPhases[2]) {
1440 if (nextPhases[ringNum]->getToPhase() != this) {
1441 // if the next phase is not me, then I need to go into a transition
1442 lastTransitionDecision = nextPhases[ringNum];
1444 // if I am in green, then I need to enter yellow
1445 enterYellow(controller);
1446 return;
1447 }
1448
1449 if (controller->getCurrentTime() - myLastEnd < (yellow + red)) {
1450 if (controller->getCurrentTime() - myLastEnd >= yellow) {
1451 // if I am in yellow, then I need to enter red
1453 }
1454 // I am currently in the Red state but haven't reached max
1455 return;
1456 }
1457
1458 handleRedXferOrNextPhase(controller, nextPhases);
1459 return;
1460 }
1461
1462 handleGreenRestOrTransfer(controller, nextPhases);
1463}
1464
1466 myLastEnd = controller->getCurrentTime();
1468 transitionActive = true;
1469}
1470
1472 PhasePtr otherPhase = controller->getOtherPhase(this);
1473 bool barrierCross = nextPhases[ringNum]->getToPhase()->barrierNum != barrierNum;
1474 bool barrierCrossButOkay = barrierCross && (
1475 nextPhases[ringNum]->getToPhase()->barrierNum == nextPhases[otherPhase->ringNum]->getToPhase()->barrierNum
1476 ) && otherPhase->okay2ForceSwitch(controller);
1477
1478 if (!barrierCross) {
1479 nextPhases[ringNum]->getToPhase()->enter(controller, this);
1480 return;
1481 }
1482
1483 if (barrierCrossButOkay) {
1484 // if the barrier is crossed, but the other phase is going to the same barrier, then I can enter red transfer
1485 // enter the next phase
1486 nextPhases[ringNum]->getToPhase()->enter(controller, this);
1487 // trigger the other phase to enter red transfer
1488 nextPhases[otherPhase->ringNum]->getToPhase()->enter(controller, this);
1489 return;
1490 }
1491
1493 readyToSwitch = true;
1494 transitionActive = false;
1495}
1496
1498 NEMAPhase* otherPhase = controller->getOtherPhase(this);
1499 readyToSwitch = false;
1500 bool isOtherPhaseReady = nextPhases[!ringNum]->getToPhase() == otherPhase && otherPhase->readyToSwitch;
1501 bool isOtherPhaseInGreenRest = otherPhase->greenRestTimer >= otherPhase->maxDuration && otherPhase->getCurrentState() == LightState::GreenRest;
1502
1503 if (isOtherPhaseReady || isOtherPhaseInGreenRest) {
1505 myStartTime = controller->getCurrentTime() - minDuration;
1508 } else {
1510 if (isAtBarrier) {
1511 myExpectedDuration = (otherPhase->myExpectedDuration + otherPhase->myStartTime) - myStartTime;
1512 }
1513 }
1514}
1515
1519 // if in red xfer, I am ready to switch whenevery
1520 return TIME2STEPS(0);
1521 }
1522 if (!transitionActive) {
1523 // if a transition is not active, the transition is just yellow + red time
1525 }
1526 // if a transition is active, then return the time left in the transition
1527 return MAX2(TIME2STEPS(0), ((yellow + red) - (controller->getCurrentTime() - myLastEnd)));
1528}
1529
1533 // add the vehicle extension timer if the detector is active.
1534 // capped by the minimum and maximum duration
1535 return MIN2(MAX2(duration + vehExt, minDuration), maxGreenDynamic);
1536 }
1537 return myExpectedDuration;
1538}
1539
1540void
1542 // If I am in a transition, the rest of the update logic does not matter
1544 // return early
1545 readyToSwitch = true;
1546 return;
1547 }
1548
1549 // Continuation Logic
1550 SUMOTime duration = controller->getCurrentTime() - myStartTime;
1551 // Check the vehicle extension timer as long as not in green transfer and not a coordinated phase
1554 }
1555 // Special Logic for Green Rest, which behaves uniquely
1557 // check all other detectors and see if anything else is active. If so,
1558 // start the green rest timer countdown, which is == to the max duration of the phase
1559 bool vehicleActive = false;
1560 for (auto& p : controller->getPhaseObjs()) {
1561 if ((p->phaseName != phaseName)
1562 && (p->phaseName != controller->getOtherPhase(this)->phaseName)
1563 && p->callActive()) {
1565 vehicleActive = true;
1566 break;
1567 }
1568 }
1569 // catch the rising edge of the sidestreet detection and calculate the maximum timer
1570 if (vehicleActive && (greenRestTimer + DELTA_T >= maxDuration)) {
1572 }
1573
1574 // if there are no other vehicles slide the startTime along
1575 if (!vehicleActive) {
1577 if (duration >= minDuration) {
1578 myStartTime = controller->getCurrentTime() - minDuration;
1581 }
1582 }
1583
1584 // if the green rest timer is exhausted, set ready to switch
1585 if (greenRestTimer < DELTA_T) {
1586 readyToSwitch = true;
1587 // force the counterpart to be ready to switch too. This needs to be latching....
1588 NEMAPhase* otherPhase = controller->getOtherPhase(this);
1589 if (otherPhase->getCurrentState() > LightState::Green) {
1590 otherPhase->readyToSwitch = true;
1591 }
1592 }
1593
1594 // Special Behavior when the Green Rest Circles all the way around in coordinated mode
1595 if (coordinatePhase) {
1596 // This means that we have green rested until I should "start" again. Just call my entry function again.
1597 if (controller->getTimeInCycle() <= ((forceOffTime - maxDuration) + DELTA_T / 2)) {
1598 enter(controller, this);
1599 }
1600 }
1601 }
1602 // Check to see if a switch is desired
1603 if (duration >= myExpectedDuration) {
1604 readyToSwitch = true;
1605 }
1606}
1607
1610 for (auto t : myTransitions) {
1611 if (t->getToPhase()->phaseName == toPhase) {
1612 return t;
1613 }
1614 }
1615 // This point should never be reached
1616 assert(0);
1617 // To satisfy the compiler and return value from all control paths
1618 return myTransitions.front();
1619}
1620
1621std::vector<PhaseTransitionLogic*>
1623 // this function returns the preferred valid transition for the phase
1624 std::vector<PhaseTransitionLogic*> nextTransitions;
1625 if (readyToSwitch) {
1626 // only try to switch if I am ready to switch
1627 for (auto& t : myTransitions) {
1628 // for the transitions check if it is okay
1629 if (t->okay(controller)) {
1630 // if there was already a transition decision, it can be overriden but only if the new transition
1631 // is on the same side of a barrier
1632 if (lastTransitionDecision != nullptr) {
1633 if (t->getToPhase()->barrierNum == lastTransitionDecision->getToPhase()->barrierNum) {
1634 nextTransitions.push_back(t);
1635 break;
1636 }
1637 } else {
1638 nextTransitions.push_back(t);
1639 // break once there is a valid option (they have already been sorted)
1640 break;
1641 }
1642 }
1643 }
1644 }
1645 // Add in the last transition decision if it hasn't been added in yet
1646 if (lastTransitionDecision != nullptr) {
1647 bool found = false;
1648 bool sameBarrier = false;
1649 for (auto& t : nextTransitions) {
1650 if (t == lastTransitionDecision) {
1651 found = true;
1652 break;
1653 }
1654 if (t->getToPhase()->barrierNum == lastTransitionDecision->getToPhase()->barrierNum) {
1655 sameBarrier = true;
1656 break;
1657 }
1658 }
1659 // 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.
1660 if (!found && !sameBarrier) {
1661 nextTransitions.push_back(lastTransitionDecision);
1662 }
1663 }
1664 // Add the transition back to myself, but only in the case when no others have been added
1665 if (nextTransitions.size() < 1) {
1666 nextTransitions.push_back(myTransitions.back());
1667 }
1668
1669 return nextTransitions;
1670}
1671
1672// ===========================================================================
1673// PhaseTransitionLogic Definitions
1674// ===========================================================================
1676 NEMAPhase* fromPhase, NEMAPhase* toPhase) :
1677 distance(0),
1678 fromPhase(fromPhase),
1679 toPhase(toPhase)
1680{}
1681
1682bool
1684 // Picking the correct transition logic to use
1685 // #TODO this could be a case of using function as variable and setting it at PhaseTransitionLogic
1686 // creation time
1687 if (fromPhase == toPhase) {
1688 // for green rest or green transfer, it cannot return to itself if a transition is active
1690 } else if (fromPhase->coordinatePhase) {
1691 // if the from phase is a coordinated phase i.e. {2, 6} in a standard setup
1692 return fromCoord(controller);
1693 } else if (fromPhase->isAtBarrier) {
1694 // if the phase is at a barrier i.e. {2, 6, 4, 8} in a standard setup
1695 return fromBarrier(controller);
1696 } else if (controller->coordinateMode) {
1697 // typical coordinate mode transition,
1698 return coordBase(controller);
1699 } else {
1700 // base transition logic
1701 return freeBase(controller);
1702 }
1703}
1704
1705bool
1707 // Simplest transition logic. Just check if a detector (or recall) is active on that phase and
1708 bool okay = false;
1709 // is a call active on the toPhase?
1710 if (toPhase->callActive()) {
1711 // would the transition be a barrier cross?
1713 PhasePtr otherPhase = controller->getOtherPhase(fromPhase);
1714 // If it is a barrier cross, then the other phase must also be ready to switch
1715 // or have a transition time that is lower than mine currently. DELTA_T is critical here
1716 if (otherPhase->readyToSwitch) {
1717 // #&& otherPhase->getTransitionTime(controller) <= fromPhase->getTransitionTime(controller)) {
1718 okay = true;
1719 }
1720 } else {
1721 okay = true;
1722 }
1723 }
1724 return okay;
1725}
1726
1727bool
1729 if (toPhase->coordinatePhase &&
1731 // transitions TO the coordinated phase may always happen, as long as the other phase is okay to switch too
1732 return true;
1733 }
1734 // first check if the free logic is upheld
1735 else if (freeBase(controller)) {
1736 // 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
1737 SUMOTime transitionTime = fromPhase->getTransitionTime(controller);
1738 SUMOTime timeTillForceOff = controller->ModeCycle(toPhase->forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1739 if (toPhase->minDuration + transitionTime <= timeTillForceOff) {
1740 return true;
1741 }
1742 }
1743 return false;
1744}
1745
1746
1747bool
1749 if (freeBase(controller)) {
1751 // same barrier side so we are good.
1752 // 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
1754 for (auto& p : controller->getPhasesByRing(fromPhase->ringNum)) {
1755 if (p->barrierNum != fromPhase->barrierNum && p->callActive()) {
1756 return false;
1757 }
1758 }
1759 }
1760 return true;
1761 } else {
1762 // This is now a barrier cross and we need to make sure that the other phase is also ready to transition
1764 return true;
1765 }
1766 }
1767 }
1768 return false;
1769}
1770
1771
1772bool
1774 if (coordBase(controller)) {
1775 // Determine if the other phase is also ready to switch
1776 if (controller->getOtherPhase(fromPhase)->readyToSwitch) {
1777 // 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
1778 if (controller->isType170()) {
1779 return true;
1780 }
1781 // If the transition is already active, then report that the movement is possible
1783 return true;
1784 }
1785 // now determine if there my prior phase can fit or not. We already know that I can fit.
1786 NEMAPhase* priorPhase = toPhase->getSequentialPriorPhase();
1787 SUMOTime timeTillForceOff = controller->ModeCycle(priorPhase->forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1788 SUMOTime transitionTime = fromPhase->getTransitionTime(controller);
1789 // if the time till the force off is less than the min duration ||
1790 // if it is greater than the cycle length minus the length of the coordinate phase (which the fromPhase automatically is)
1791 if ((priorPhase->minDuration + transitionTime) > timeTillForceOff || timeTillForceOff > (controller->getCurrentCycleLength() - fromPhase->minDuration)) {
1792 return true;
1793 }
1794 }
1795 }
1796 return false;
1797}
1798
1799int
1801 // Returns the other transitions distance in green transfer situations
1802 if ((toPhase == fromPhase) && (otherTrans->toPhase->barrierNum == toPhase->barrierNum)) {
1804 return otherTrans->distance;
1805 }
1806 }
1807 return distance;
1808}
long long int SUMOTime
Definition GUI.h:36
#define INVALID_POSITION
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:296
#define WRITE_WARNING(msg)
Definition MsgHandler.h:295
#define TL(string)
Definition MsgHandler.h:315
#define TLF(string,...)
Definition MsgHandler.h:317
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: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
@ 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:76
T MAX2(T a, T b)
Definition StdDefs.h:82
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.
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:2551
static bool dictionary(const std::string &id, MSLane *lane)
Static (sic!) container methods {.
Definition MSLane.cpp:2414
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
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
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
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