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-2023 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 sequetionaly 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 decribes how much time has to pass until a vehicle is considerred 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 swithing
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 barrers 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 // TODO: What is the difference between splits and max greens?
637 assert(newSplits.size() == 8);
638 for (auto& p : myPhaseObjs) {
639 if (newSplits[p->phaseName - 1] > 0) {
640 // set the phase's nextMaxDuration. This will be implemented when implementTraciChanges is called
641 p->nextMaxDuration = TIME2STEPS(newSplits[p->phaseName - 1]) - p->yellow - p->red;
642 }
643 }
644}
645
646
647void
648NEMALogic::setNewMaxGreens(std::vector<double> newMaxGreens) {
649 for (auto& p : myPhaseObjs) {
650 if (newMaxGreens[p->phaseName - 1] > 0) {
651 // set the phase's nextMaxDuration. This will be implemented when implementTraciChanges is called
652 p->nextMaxDuration = TIME2STEPS(newMaxGreens[p->phaseName - 1]);
653 }
654 }
655}
656
657
658void
659NEMALogic::setNewCycleLength(double newCycleLength) {
660 // set the controller's next cycle length. This will be implemented when implementTraciChanges is called
661 myNextCycleLength = TIME2STEPS(newCycleLength);
662}
663
664
665void
666NEMALogic::setNewOffset(double newOffset) {
667 // set the controller's offset. This will be implemented when implementTraciChanges is called
668 myNextOffset = TIME2STEPS(newOffset);
669}
670
671
672std::vector<int> NEMALogic::readParaFromString(std::string s) {
673 std::vector<int> output;
674 for (char c : s) {
675 if (c >= '0' && c <= '9') {
676 int temp = c - '0';
677 output.push_back(temp);
678 }
679 }
680 return output;
681}
682
685 return myPhase;
686}
687
688int NEMALogic::measureRingDistance(int p1, int p2, int ringNum) {
689 int length = (int)rings[ringNum].size();
690 int d = 0;
691 bool found = false;
692 // Loop around the ring and keep track of the distance from p1 to p2
693 for (int i = 0; i < (length * 2); i++) {
694 if (rings[ringNum][i % length] > 0) {
695 if (found) {
696 d++;
697 if (rings[ringNum][i % length] == p2) {
698 break;
699 }
700 } else if (rings[ringNum][i % length] == p1) {
701 found = true;
702 }
703 }
704 }
705 assert(d > 0);
706 return d;
707}
708
709
712 SUMOTime c = a - b;
713 while (c >= b) {
714 c = c - b;
715 }
716 while (c < 0) {
717 c += b;
718 }
719 return c;
720}
721
722
723void
724NEMALogic::getLaneInfoFromNEMAState(std::string state, StringVector& laneIDs, IntVector& stateIndex) {
725 std::set<std::string> output;
726 for (int i = 0; i < (int)state.size(); i++) {
727 char ch = state[i];
728 // if the ch is 'G', it means that the phase is controlling this lane
729 if (ch == 'G') {
730 stateIndex.push_back(i);
731 for (auto link : myLinks[i]) {
732 laneIDs.push_back(link->getLaneBefore()->getID());
733 }
734 }
735 }
736}
737
738bool NEMALogic::isLeftTurnLane(const MSLane* const lane) const {
739 const std::vector<MSLink*> links = lane->getLinkCont();
740 if (links.size() == 1 && links.front()->getDirection() == LinkDirection::LEFT) {
741 return true;
742 }
743 return false;
744}
745
746bool
747NEMALogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
748 for (int i = 0; i < (int)state.size(); i++) {
749 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
750 for (MSLane* cand : getLanesAt(i)) {
751 for (MSLane* lane : lanes) {
752 if (lane == cand) {
753 return true;
754 }
755 }
756 }
757 }
758 }
759 return false;
760}
761
762
763void
766 for (auto& item : myLaneDetectorMap) {
767 item.second->setVisible(true);
768 }
769}
770
771void
774 for (auto& item : myLaneDetectorMap) {
775 item.second->setVisible(false);
776 }
777}
778
779void
781 myShowDetectors = show;
782 for (auto& item : myLaneDetectorMap) {
783 item.second->setVisible(myShowDetectors);
784 }
785}
786
787int NEMALogic::string2int(std::string s) {
788 std::stringstream ss(s);
789 int ret = 0;
790 ss >> ret;
791 return ret;
792}
793
794
795const std::string
796NEMALogic::getParameter(const std::string& key, const std::string defaultValue) const {
797 if (StringUtils::startsWith(key, "NEMA.")) {
798 if (key == "NEMA.phaseCall") {
799 int activeCalls[8] = {0};
800 for (const auto p : myPhaseObjs) {
801 // This handles the case when the controller has multiple of the same phase call
802 if (!activeCalls[p->phaseName - 1]) {
803 activeCalls[p->phaseName - 1] = 1 * p->lastDetectActive;
804 }
805 }
806 std::string outStr = "";
807 for (int i = 0; i < 8; i++) {
808 outStr += std::to_string(activeCalls[i]);
809 if (i < 7) {
810 outStr += ",";
811 }
812 }
813 return outStr;
814 } else {
815 throw InvalidArgument("Unsupported parameter '" + key + "' for NEMA controller '" + getID() + "'");
816 }
817 } else {
818 return Parameterised::getParameter(key, defaultValue);
819 }
820}
821
822
823void
824NEMALogic::setParameter(const std::string& key, const std::string& value) {
825 queuedTraciChanges = true;
826 if (StringUtils::startsWith(key, "NEMA.")) {
827 if (key == "NEMA.splits" || key == "NEMA.maxGreens") {
828 //splits="2.0 3.0 4.0 5.0 2.0 3.0 4.0 5.0"
829 const std::vector<std::string>& tmp = StringTokenizer(value).getVector();
830 if (tmp.size() != 8) {
831 queuedTraciChanges = false;
832 throw InvalidArgument("Parameter '" + key + "' for NEMA controller '" + getID() + "' requires 8 space or comma separated values");
833 }
834 std::vector<double> timing;
835 for (const std::string& s : tmp) {
836 timing.push_back(StringUtils::toDouble(s));
837 }
838 if (key == "NEMA.maxGreens") {
839 setNewMaxGreens(timing);
840 } else {
841 setNewSplits(timing);
842 }
843 } else if (key == "NEMA.cycleLength") {
845 } else if (key == "NEMA.offset") {
847 } else {
848 queuedTraciChanges = false;
849 throw InvalidArgument("Unsupported parameter '" + key + "' for NEMA controller '" + getID() + "'");
850 }
851 }
852 Parameterised::setParameter(key, value);
853}
854
855void
856NEMALogic::error_handle_not_set(std::string param_variable, std::string param_name) {
857 if (param_variable == "") {
858 throw InvalidArgument("Please set " + param_name + " for NEMA tlLogic '" + getID() + "'");
859 }
860}
861
862void
864 SUMOTime zeroTime[2] = { TIME2STEPS(0), TIME2STEPS(0) };
865 for (int i = 0; i < 2; i++) {
866 SUMOTime runningTime = 0;
867 // loop through the phases for ring 0 and then 1
868 for (auto& p : getPhasesByRing(i)) {
869 runningTime += p->maxDuration + p->getTransitionTime(this);
870 // in 170, the cycle "starts" when the coordinated phase goes to yellow.
871 // See https://ops.fhwa.dot.gov/publications/fhwahop08024/chapter6.html
872 if (p->coordinatePhase) {
873 zeroTime[i] = runningTime;
874 }
875 p->forceOffTime = runningTime - p->getTransitionTime(this);
876 p->greatestStartTime = p->forceOffTime - p->minDuration;
877 }
878 }
879 // find the minimum offset time and then subtract from everything, modecycling where negative
880 // This sets the 0 cycle time as start of yellow on earliest ending coordinated phase
881 SUMOTime minCoordYellow = MIN2(zeroTime[0], zeroTime[1]);
882 for (auto& p : myPhaseObjs) {
883 p->forceOffTime = ModeCycle(p->forceOffTime - minCoordYellow, myCycleLength);
884 p->greatestStartTime = ModeCycle(p->greatestStartTime - minCoordYellow, myCycleLength);
885 }
886
887#ifdef DEBUG_NEMA
888 std::ios_base::fmtflags oldflags = std::cout.flags();
889 std::streamsize oldprecision = std::cout.precision();
890 for (int i = 0; i < 2; i++) {
891 std::cout << "Ring" << i + 1 << " force offs: \t";
892 for (auto& p : rings[i]) {
893 if (p > 0) {
894 PhasePtr pObj = getPhaseObj(p, i);
895 std::cout << std::fixed << std::setprecision(2) << STEPS2TIME(pObj->forceOffTime) << "\t";
896 } else {
897 std::cout << std::to_string(0) << "\t";
898 }
899 }
900 std::cout << std::endl;
901 }
902 std::cout.flags(oldflags);
903 std::cout.precision(oldprecision);
904#endif
905}
906
907void
909 // TS2 "0" cycle time is the start of the "first" coordinated phases.
910 // We can find this "0" point by first constructing the forceOffs in sequential order via the 170 method
912
913 // Switch the Force Off Times to align with TS2 Cycle, which is the *start* of the earliest coordinated phase
914 // The coordinate phases will always be the defaultBarrierPhases[i][0]
915 SUMOTime minCoordTime = MIN2(coordinatePhaseObjs[0]->forceOffTime - coordinatePhaseObjs[0]->maxDuration,
916 coordinatePhaseObjs[1]->forceOffTime - coordinatePhaseObjs[1]->maxDuration);
917
918 // loop through all the phases and subtract this minCoordTime to move the 0 point to the start of the first coordinated phase
919 for (auto& p : myPhaseObjs) {
920 if ((p->forceOffTime - minCoordTime) >= 0) {
921 p->forceOffTime -= (minCoordTime);
922 } else {
923 p->forceOffTime = (myCycleLength + (p->forceOffTime - (minCoordTime)));
924 }
925 p->greatestStartTime = ModeCycle(p->greatestStartTime - minCoordTime, myCycleLength);
926 }
927}
928
929void
931 // get the time in the cycle
933 NEMAPhase* activePhases[2];
934 for (int i = 0; i < 2; i++) {
935 std::vector<NEMAPhase*> ringCopy = getPhasesByRing(i);
936 // sort by the minimum start time in the cycle.
937 std::sort(ringCopy.begin(), ringCopy.end(),
938 [](NEMAPhase * p, NEMAPhase * p1) {
939 return p->greatestStartTime <= p1->greatestStartTime;
940 }
941 );
942 bool found = false;
943 // loop through the sorted phases by time and try to find the phase that should be active given the time in the cycle
944 for (auto& p : ringCopy) {
945 // This handles the wrap around. Checks if the prior phases start time should have already happened.
946 // 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 )
949 if (cycleTime <= ModeCycle(p->greatestStartTime, myCycleLength) && cycleTime > ModeCycle(syntheticPriorStart, myCycleLength)) {
950 found = true;
951 activePhases[i] = p;
952 break;
953 }
954 }
955 if (!found) {
956#ifdef DEBUG_NEMA
957 const std::string error = "I can't find the correct phase for NEMA tlLogic '" + getID() + "' Ring " + toString(i) + " to start in.";
958 WRITE_WARNING(error);
959 WRITE_WARNING(TL("I am starting in the coordinated phases"));
960#endif
961 activePhases[0] = defaultBarrierPhases[0][0];
962 activePhases[1] = defaultBarrierPhases[1][0];
963 }
964 }
965
966 // 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
967 if (activePhases[0]->barrierNum != activePhases[1]->barrierNum) {
968 // give preference to whatever is on the coordinate side of the barrier, one must be if they aren't equal to each other
969 activePhases[0] = activePhases[0]->barrierNum == 0 ? activePhases[0] : defaultBarrierPhases[0][0];
970 activePhases[1] = activePhases[1]->barrierNum == 0 ? activePhases[1] : defaultBarrierPhases[1][0];
971 }
972
973 // force enter the phases and update their expected duration to last until the forceOff
974 activePhases[0]->forceEnter(this);
975 activePhases[1]->forceEnter(this);
976}
977
978void
980 // Modifications where made to 170 algorithm so that it works with both.
982}
983
985NEMALogic::parseControllerType(std::string inputType) {
986 std::string cleanString;
987 for (const char& c : inputType) {
988 if (isalpha(c) || isdigit(c)) {
989 cleanString += (char)::tolower(c);
990 }
991 }
992 if (cleanString == "type170") {
993 return Type170;
994 } else if (cleanString == "ts2") {
995 return TS2;
996 } else {
997 throw InvalidArgument("Please set controllerType for NEMA tlLogic " + myID + " to either Type170 or TS2");
998 }
999}
1000
1001std::vector<NEMAPhase*>
1003 std::vector<NEMAPhase*> phases;
1004 for (auto& p : myPhaseObjs) {
1005 if (p->ringNum == ringNum) {
1006 phases.push_back(p);
1007 }
1008 }
1009 return phases;
1010}
1011
1012void
1016
1017std::map<std::string, double>
1019 std::map<std::string, double> result;
1020 for (auto item : myDetectorLaneMap) {
1021 result[item.first->getID()] = item.first->getCurrentVehicleNumber();
1022 }
1023 return result;
1024}
1025
1028 // return a pointer to the other active phase
1029 return myActivePhaseObjs[!p->ringNum];
1030}
1031
1032NEMAPhase*
1033NEMALogic::getPhaseObj(int phaseNum, int ringNum) {
1034 // This satisfies the case where there is a "duplicate" phase on both ring
1035 std::vector<PhasePtr> iterRing = ringNum >= 0 ? getPhasesByRing(ringNum) : myPhaseObjs;
1036 for (auto& p : iterRing) {
1037 if (p->phaseName == phaseNum) {
1038 return p;
1039 }
1040 }
1041 // the phase must always be found
1042 assert(0);
1043 // To satisfy the compiler
1044 return myPhaseObjs.front();
1045}
1046
1049 NEMAPhase* p = t->getFromPhase();
1050 // if the current phase is not ready to switch or a barrier cross is desired by the other transition
1051 // and t fromPhase is not ready to switch, the default transition is back to myself
1052 if (!p->readyToSwitch ||
1054 return p->getTransition(p->phaseName);
1055 }
1056 // otherwise the default transition is to the default phase on whatever barrier ot wants to transition to
1057 else {
1059 }
1060}
1061
1062void
1064 std::vector<std::vector<PhaseTransitionLogic* >> potentialPhases;
1065
1066 // Get a vector of each phases' potential transitions
1067 for (const auto& p : myActivePhaseObjs) {
1068 potentialPhases.push_back(p->trySwitch(this));
1069 }
1070
1071 // Loop through all combination of transitions, keeping only the valid ones and filling in the gaps where necessary
1072 for (const auto& r1_t : potentialPhases[0]) {
1073 for (const auto& r2_t : potentialPhases[1]) {
1074 // if both transitions go to the same barrier then we are good
1075 if (r1_t->getToPhase()->barrierNum == r2_t->getToPhase()->barrierNum) {
1076 transitions.push_back({ r1_t, r2_t, (float)(r1_t->getDistance(r2_t) + r2_t->getDistance(r1_t)) / 2 });
1077 } else {
1078 // If the rings are different, add a choice where one of them is the default choice for whatever ring it is
1079 // create two choices, one for each of the phase as they are on different rings
1080 if (r1_t->getFromPhase()->readyToSwitch) {
1081 // get the r2 default
1082 PhaseTransitionLogic* r2_t_temp = getDefaultTransition(r2_t, r1_t);
1083 // only add it if it does not cross a barrier!
1084 if (r2_t_temp->getToPhase()->barrierNum == r1_t->getToPhase()->barrierNum) {
1085 transitions.push_back({ r1_t, r2_t_temp, (float)(r2_t_temp->getDistance(r1_t) + r1_t->getDistance(r2_t_temp)) / 2 });
1086 }
1087 }
1088 if (r2_t->getFromPhase()->readyToSwitch) {
1089 // R1 default
1090 PhaseTransitionLogic* r1_t_temp = getDefaultTransition(r1_t, r2_t);
1091 // only add it if it does not cross a barrier!
1092 if (r1_t_temp->getToPhase()->barrierNum == r2_t->getToPhase()->barrierNum) {
1093 transitions.push_back({ r1_t_temp, r2_t, (float)(r2_t->getDistance(r1_t_temp) + r1_t_temp->getDistance(r2_t)) / 2 });
1094 }
1095 }
1096 // If the distances are <= 1, this means that this is the shortest transition possible
1097 // and we can return early without considering any other options
1098 if (!transitions.empty()) {
1099 if (transitions.back().distance < 1) {
1100 return;
1101 }
1102 }
1103 }
1104 }
1105 }
1106}
1107
1108
1109std::string
1111 // FIX with plan to support #10742
1112 std::string out(myPhaseStrLen, 'r');
1113 for (int i = 0; i < myPhaseStrLen; i++) {
1114 bool controlled = false;
1115 std::string phaseChars = "";
1116 for (auto& p : myActivePhaseObjs) {
1117 phaseChars += p->getNEMAChar(i);
1118 if (p->controlledIndex(i)) {
1119 out[i] = p->getNEMAChar(i);
1120 controlled = true;
1121 }
1122 }
1123 // if the index wasn't a controlled one, the prior priority order still stands
1124 if (!controlled) {
1125 for (auto priority_char : lightHeadPriority) {
1126 if (std::count(phaseChars.begin(), phaseChars.end(), priority_char)) {
1127 out[i] = priority_char;
1128 break;
1129 }
1130 }
1131 }
1132 }
1133 return out;
1134}
1135
1136
1139#ifdef DEBUG_NEMA_SWITCH
1140 std::cout << SIMTIME << " trySwitch tls=" << getID() << "\n";
1141#endif
1142 PhaseTransitionLogic* nextPhases[2] = { nullptr, nullptr };
1143
1144 // update the internal time. This is a must. Could have just used a reference to the tmme
1146
1147 // Check the Detectors
1148 for (auto& p : myPhaseObjs) {
1149 p->checkMyDetectors();
1150 }
1151
1152 // Update the timing parameters
1153 for (const auto& p : myActivePhaseObjs) {
1154 p->update(this);
1155 }
1156
1157 // Calculate the Next Phases, but only if atleast one of them is ready to transition
1158 if (myActivePhaseObjs[0]->readyToSwitch || myActivePhaseObjs[1]->readyToSwitch) {
1159 TransitionPairs transitions;
1160 // set the next phases by reference
1161 getNextPhases(transitions);
1162
1163 // Sort the next phases by distance and select the closest.
1164 // TODO: Is there a way to avoid this sort? The transitions are already sorted by distance prior
1165 // to picking the valid ones
1166 if (transitions.size() > 1) {
1167 std::sort(transitions.begin(), transitions.end(),
1168 [](const transitionInfo & i, const transitionInfo & j) {
1169 return i.distance < j.distance;
1170 });
1171 }
1172
1173 // Set the Next Phases = to the transition with least combined distance
1174 nextPhases[0] = transitions.front().p1;
1175 nextPhases[1] = transitions.front().p2;
1176
1177 // Try the exit logic. This doesn't necessarily mean that the phase will exit,
1178 // as it could go into green rest or green transfer, but this is considered an "exit"
1179 for (const auto& p : myActivePhaseObjs) {
1180 if (p->readyToSwitch) {
1181 p->exit(this, nextPhases);
1182 }
1183 }
1184
1185 // This is the only time when something might have happened, so we update the phase strings here
1186 std::string newState = composeLightString();
1187 if (newState != myPhase.getState()) {
1188 myPhase.setState(newState);
1189 myPhase.setName(toString(myActivePhaseObjs[0]->phaseName) + "+" + toString(myActivePhaseObjs[1]->phaseName));
1190 // ensure that SwitchCommand::execute notices a change
1191 myStep = 1 - myStep;
1192
1193 }
1194 }
1195
1196 // clear the phases' detectors
1197 for (auto& p : myPhaseObjs) {
1198 p->clearMyDetectors();
1199 }
1200
1201
1202#ifdef FUZZ_TESTING
1203 // Basic Assertion to ensure that the Barrier is not crossed
1204 assert(myActivePhaseObjs[0]->barrierNum == myActivePhaseObjs[1]->barrierNum);
1205#endif
1206
1207 // return the simulation timestep, as this controller must be checked every simulation step
1208 return DELTA_T;
1209}
1210
1211
1212void
1214 // Implement Traci Updates on the start of ring1 coordinated phase (rising edge of it turning green)
1215 if (queuedTraciChanges) {
1216 for (auto& p : myPhaseObjs) {
1217 p->maxDuration = p->nextMaxDuration;
1218 }
1220 // TODO: Do we need to redo the force offs here? I thins that we do
1222 // now that we have set the cycle length, offset and max duration, we need to update force off times
1224 queuedTraciChanges = false;
1225 }
1226}
1227
1228
1229// ===========================================================================
1230// NEMAPhase Definitions
1231// ===========================================================================
1232NEMAPhase::NEMAPhase(int phaseName, bool isBarrier, bool isGreenRest, bool isCoordinated,
1233 bool minRecall, bool maxRecall, bool fixForceOff, int barrierNum, int ringNum,
1234 IntVector phaseStringInds,
1235 MSPhaseDefinition* phase) :
1236 phaseName(phaseName),
1237 isAtBarrier(isBarrier),
1238 isGreenRest(isGreenRest),
1239 barrierNum(barrierNum),
1240 coordinatePhase(isCoordinated),
1241 minRecall(minRecall),
1242 maxRecall(maxRecall),
1243 fixForceOff(fixForceOff),
1244 ringNum(ringNum),
1245 myCorePhase(phase),
1246 myPhaseStringInds(phaseStringInds) {
1247 // Public
1248 readyToSwitch = false;
1249 greenRestTimer = 0;
1250 forceOffTime = 0;
1251 lastDetectActive = false;
1252
1253 // Private
1254 myInstance = this;
1255 myLastPhaseInstance = nullptr;
1256 sequentialPriorPhase = nullptr;
1258 transitionActive = false;
1259
1260 // Timing Parameters
1262 myStartTime = TIME2STEPS(0.);
1264 myLastEnd = TIME2STEPS(0.);
1265
1266 // set the phase colors
1268}
1269
1271 // Delete the transitions from their alloc
1272 for (auto t : myTransitions) {
1273 delete t;
1274 }
1275}
1276
1277void
1278NEMAPhase::init(NEMALogic* controller, int crossPhaseTarget, int crossPhaseSource, bool latching) {
1279 // switch the durations from steps2time
1281
1282 for (auto p : controller->getPhasesByRing(ringNum)) {
1283 // construct transitions for all potentail movements, including back to myself
1284 myTransitions.push_back(new PhaseTransitionLogic(this, p));
1285 myTransitions.back()->setDistance(controller->measureRingDistance(phaseName, p->phaseName, ringNum));
1286 }
1287
1288 // sort the transitions by distance for speed later. Using plain distance here
1289 std::sort(myTransitions.begin(), myTransitions.end(), [&](const PhaseTransitionLogic * i, const PhaseTransitionLogic * j) {
1290 return i->distance < j->distance;
1291 });
1292
1293 // create the phase detector info
1295 crossPhaseSource > 0 ? controller->getPhaseObj(crossPhaseSource) : nullptr,
1296 crossPhaseTarget > 0 ? controller->getPhaseObj(crossPhaseTarget) : nullptr
1297 );
1298}
1299
1300void
1302 // This could be extended in the future to allow for traci manipulation
1304 red = myCorePhase->red;
1310}
1311
1312
1313// TODO: this can be computed once.
1314char
1317 return myGreenString[i];
1318 } else if (myLightState <= LightState::Red) {
1319 return myRedString[i];
1320 } else {
1321 return myYellowString[i];
1322 }
1323}
1324
1325void
1328 myRedString = "";
1329 myYellowString = "";
1330 for (char ch : myGreenString) {
1331 myRedString += 'r';
1332 if (ch == 'G' || ch == 'g') {
1333 myYellowString += 'y';
1334 } else {
1335 myYellowString += ch;
1336 }
1337 }
1338}
1339
1340void
1343 // remove the active flag on the detector if the detector is not latching or if it is green
1346 }
1347}
1348
1349void
1351 // Check my Detectors, only necessary if it isn't currently marked as on
1353 // If I have a cross phase target and it is active and I am not, save my detector as not active
1354 if (myDetectorInfo.cpdTarget != nullptr) {
1358 return;
1359 }
1360 }
1361 }
1362 // If we make it to this point, check my detector like normal.
1363 for (auto& d : myDetectorInfo.detectors) {
1364 if (d->getCurrentVehicleNumber() > 0) {
1366 return;
1367 }
1368 }
1369 // If my detector is not active, check my cross phase
1370 if ((myDetectorInfo.cpdSource != nullptr) && (myLightState >= LightState::Green)) {
1372 for (auto& d : myDetectorInfo.cpdSource->getDetectors()) {
1373 if (d->getCurrentVehicleNumber() > 0) {
1375 return;
1376 }
1377 }
1378 }
1379 }
1380 }
1381}
1382
1383void
1384NEMAPhase::enter(NEMALogic* controller, NEMAPhase* lastPhase) {
1385#ifdef DEBUG_NEMA_SWITCH
1386 std::cout << SIMTIME << " enter tls=" << controller->getID() << " phase=" << phaseName << "\n";
1387#endif
1388
1389 // Enter the phase
1390 myStartTime = controller->getCurrentTime();
1392 myLastPhaseInstance = lastPhase;
1393 readyToSwitch = false;
1394
1395 // implement the new timing parameters on the first coordinated phase to appear
1396 if (phaseName == controller->coordinatePhaseObjs[ringNum]->phaseName) {
1397 controller->implementTraciChanges();
1398 }
1399
1400 // Handle Green Rest Peculiarities
1401 if (!controller->coordinateMode && isGreenRest) {
1402 // If the controller is in free mode and the phase is a green rest phase, then it should enter as "green rest"
1404 // if the phase has "green rest" capabilities, set it's timer to the dynamic maxGreen
1406 }
1407
1408 // clear the last transition decision
1409 lastTransitionDecision = nullptr;
1410
1411 // Calculate the Max Green Time & Expected Duration here:
1412 if (controller->coordinateMode) {
1413 if (coordinatePhase) {
1414 myExpectedDuration = controller->ModeCycle(forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1415#ifdef FUZZ_TESTING
1417#endif
1418 } else {
1419 maxGreenDynamic = controller->ModeCycle(forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1420 if (!fixForceOff) {
1422 }
1424 }
1425 } else {
1427 }
1428 // Implements the maxRecall functionality
1429 if (maxRecall && !coordinatePhase) {
1431 }
1432 // Set the controller's active phase
1433 controller->setActivePhase(this);
1434}
1435
1436void
1437NEMAPhase::exit(NEMALogic* controller, PhaseTransitionLogic* nextPhases[2]) {
1438 // At the first entry to this transition, the phase will be in green
1439 if (nextPhases[ringNum]->getToPhase() != this) {
1440 // set the last transition decision
1441 lastTransitionDecision = nextPhases[ringNum];
1443 // enter yellow and set that the transition is active
1444 myLastEnd = controller->getCurrentTime();
1446 transitionActive = true;
1447 } else {
1448 if (controller->getCurrentTime() - myLastEnd >= (yellow + red)) {
1449 // red xfer check. This happens at a barrier phase when the other phase is transitioning, but the yellow times are different.
1450 // the phase quicker to the red should enter a redxfer state, which just means that it cannot transition out of the
1451 // phase until the other phase is done. Only enters a redxfer if the next phases is a barrier cross
1452 PhasePtr otherPhase = controller->getOtherPhase(this);
1453 bool barrierCross = nextPhases[ringNum]->getToPhase()->barrierNum != barrierNum;
1454 if (barrierCross && otherPhase->isAtBarrier && isAtBarrier && barrierNum == otherPhase->barrierNum && otherPhase->getTransitionTime(controller) >= DELTA_T) {
1456 } else {
1457 // check if the other phase is in redxfer and if it is, set it to red.
1458 // This needs to be done before the next phase is entered
1459 if (otherPhase->getCurrentState() == LightState::RedXfer) {
1460 otherPhase->exit(controller, nextPhases);
1461 }
1462 // triggers the entry to the next target phase.
1463 readyToSwitch = false;
1464 transitionActive = false;
1465 // Enter into the next phase, setting it to Green
1466 nextPhases[ringNum]->getToPhase()->enter(controller, this);
1467 }
1468 } else if (controller->getCurrentTime() - myLastEnd >= yellow) {
1469 // set the light to red
1471 }
1472 }
1473 } else {
1474 // This is the entry to green rest or green transfer
1475 NEMAPhase* otherPhase = controller->getOtherPhase(this);
1476 readyToSwitch = false;
1477 if ((nextPhases[!ringNum]->getToPhase() == otherPhase && otherPhase->readyToSwitch)
1478 // if the other phase is already in green rest and I am in green transfer but there are no cars on the side streets,
1479 // I should default to being in green rest. Side street check is done by looking at the green rest timer.
1480 || (otherPhase->greenRestTimer >= otherPhase->maxDuration && otherPhase->getCurrentState() == LightState::GreenRest)) {
1481 // the light state is green rest
1483 // set the start time to be current time - the minimum timer
1484 myStartTime = controller->getCurrentTime() - minDuration;
1486 // if the phase has "green rest" capabilities, set it's timer to the dynamic maxGreen
1488 } else {
1489 // green transfer
1491 if (isAtBarrier) {
1492 // In green transfer, the phase will last as long as the other phase.
1493 // this needs to not blindly copy, but instead be a calculation
1494 myExpectedDuration = (otherPhase->myExpectedDuration + otherPhase->myStartTime) - myStartTime;
1495 }
1496 }
1497 }
1498}
1499
1502 if (!transitionActive) {
1503 // if a transition is not active, the transition is just yellow + red time
1504 return (yellow + red);
1505 }
1506 // if a transition is active, then return the time left in the transition
1507 return MAX2(TIME2STEPS(0), ((yellow + red) - (controller->getCurrentTime() - myLastEnd)));
1508}
1509
1513 // add the vehicle extension timer if the detector is active.
1514 // capped by the minimum and maximum duration
1515 return MIN2(MAX2(duration + vehExt, minDuration), maxGreenDynamic);
1516 }
1517 return myExpectedDuration;
1518}
1519
1520void
1522 // If I am in a transition, the rest of the update logic does not matter
1524 // return early
1525 readyToSwitch = true;
1526 return;
1527 }
1528
1529 // Continuation Logic
1530 SUMOTime duration = controller->getCurrentTime() - myStartTime;
1531 // Check the vehicle extension timer as long as not in green transfer and not a coordinated phase
1534 }
1535 // Special Logic for Green Rest, which behaves uniquely
1537 // check all other detectors and see if anything else is active. If so,
1538 // start the green rest timer countdown, which is == to the max duration of the phase
1539 bool vehicleActive = false;
1540 for (auto& p : controller->getPhaseObjs()) {
1541 if ((p->phaseName != phaseName)
1542 && (p->phaseName != controller->getOtherPhase(this)->phaseName)
1543 && p->callActive()) {
1545 vehicleActive = true;
1546 break;
1547 }
1548 }
1549 // catch the rising edge of the sidestreet detection and calculate the maximum timer
1550 if (vehicleActive && (greenRestTimer + DELTA_T >= maxDuration)) {
1552 }
1553
1554 // if there are no other vehicles slide the startTime along
1555 if (!vehicleActive) {
1557 if (duration >= minDuration) {
1558 myStartTime = controller->getCurrentTime() - minDuration;
1561 }
1562 }
1563
1564 // if the green rest timer is exhausted, set ready to switch
1565 if (greenRestTimer < DELTA_T) {
1566 readyToSwitch = true;
1567 // force the counterparty to be ready to switch too. This needs to be latching....
1568 NEMAPhase* otherPhase = controller->getOtherPhase(this);
1569 if (otherPhase->getCurrentState() > LightState::Green) {
1570 otherPhase->readyToSwitch = true;
1571 }
1572 }
1573
1574 // Special Behavior when the Green Rest Circles all the way around in coordinated mode
1575 if (coordinatePhase) {
1576 // This means that we have green rested until I should "start" again. Just call my entry function again.
1577 if (controller->getTimeInCycle() <= ((forceOffTime - maxDuration) + DELTA_T / 2)) {
1578 enter(controller, this);
1579 }
1580 }
1581 }
1582 // Check to see if a switch is desired
1583 if (duration >= myExpectedDuration) {
1584 readyToSwitch = true;
1585 }
1586}
1587
1590 for (auto t : myTransitions) {
1591 if (t->getToPhase()->phaseName == toPhase) {
1592 return t;
1593 }
1594 }
1595 // This point should never be reached
1596 assert(0);
1597 // To satisfy the compiler and return value from all control paths
1598 return myTransitions.front();
1599}
1600
1601std::vector<PhaseTransitionLogic*>
1603 // this function returns the prefered valid transition for the phase
1604 std::vector<PhaseTransitionLogic*> nextTransitions;
1605 if (readyToSwitch) {
1606 // only try to switch if I am ready to switch
1607 for (auto& t : myTransitions) {
1608 // for the transitions check if it is okay
1609 if (t->okay(controller)) {
1610 // if there was already a transition decision, it can be overriden but only if the new transition
1611 // is on the same side of a barrier
1612 if (lastTransitionDecision != nullptr) {
1613 if (t->getToPhase()->barrierNum == lastTransitionDecision->getToPhase()->barrierNum) {
1614 nextTransitions.push_back(t);
1615 break;
1616 }
1617 } else {
1618 nextTransitions.push_back(t);
1619 // break once there is a valid option (they have already been sorted)
1620 break;
1621 }
1622 }
1623 }
1624 }
1625 // Add in the last transition decision if it hasn't been added in yet
1626 if (lastTransitionDecision != nullptr) {
1627 bool found = false;
1628 bool sameBarrier = false;
1629 for (auto& t : nextTransitions) {
1630 if (t == lastTransitionDecision) {
1631 found = true;
1632 break;
1633 }
1634 if (t->getToPhase()->barrierNum == lastTransitionDecision->getToPhase()->barrierNum) {
1635 sameBarrier = true;
1636 break;
1637 }
1638 }
1639 // 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.
1640 if (!found && !sameBarrier) {
1641 nextTransitions.push_back(lastTransitionDecision);
1642 }
1643 }
1644 // Add the transition back to myself, but only in the case when no others have been added
1645 if (nextTransitions.size() < 1) {
1646 nextTransitions.push_back(myTransitions.back());
1647 }
1648
1649 return nextTransitions;
1650}
1651
1652// ===========================================================================
1653// PhaseTransitionLogic Definitions
1654// ===========================================================================
1656 NEMAPhase* fromPhase, NEMAPhase* toPhase) :
1657 distance(0),
1658 fromPhase(fromPhase),
1659 toPhase(toPhase)
1660{}
1661
1662bool
1664 // Picking the correct transition logic to use
1665 // #TODO this could be a case of using function as variable and setting it at PhaseTransitionLogic
1666 // creation time
1667 if (fromPhase == toPhase) {
1668 // for green rest or green transfer, it cannot return to itself if a transition is active
1670 } else if (fromPhase->coordinatePhase) {
1671 // if the from phase is a coordinated phase i.e. {2, 6} in a standard setup
1672 return fromCoord(controller);
1673 } else if (fromPhase->isAtBarrier) {
1674 // if the phase is at a barrier i.e. {2, 6, 4, 8} in a standard setup
1675 return fromBarrier(controller);
1676 } else if (controller->coordinateMode) {
1677 // typical coordinate mode transition,
1678 return coordBase(controller);
1679 } else {
1680 // base transition logic
1681 return freeBase(controller);
1682 }
1683}
1684
1685bool
1687 // Simplest transition logic. Just check if a detector (or recall) is active on that phase and
1688 bool okay = false;
1689 // is a call active on the toPhase?
1690 if (toPhase->callActive()) {
1691 // would the transition be a barrier cross?
1693 PhasePtr otherPhase = controller->getOtherPhase(fromPhase);
1694 // If it is a barrier cross
1695 if (otherPhase->readyToSwitch) {
1696 okay = true;
1697 }
1698 } else {
1699 okay = true;
1700 }
1701 }
1702 return okay;
1703}
1704
1705bool
1707 if (toPhase->coordinatePhase &&
1709 // transitions TO the coordinated phase may always happen, as long as the other phase is okay to switch too
1710 return true;
1711 }
1712 // first check if the free logic is upheld
1713 else if (freeBase(controller)) {
1714 // 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
1715 SUMOTime transitionTime = fromPhase->getTransitionTime(controller);
1716 SUMOTime timeTillForceOff = controller->ModeCycle(toPhase->forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1717 if (toPhase->minDuration + transitionTime <= timeTillForceOff) {
1718 return true;
1719 }
1720 }
1721 return false;
1722}
1723
1724
1725bool
1727 if (freeBase(controller)) {
1729 // same barrier side so we are good.
1730 // 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
1732 for (auto& p : controller->getPhasesByRing(fromPhase->ringNum)) {
1733 if (p->barrierNum != fromPhase->barrierNum && p->callActive()) {
1734 return false;
1735 }
1736 }
1737 }
1738 return true;
1739 } else {
1740 // This is now a barrier cross and we need to make sure that the other phase is also ready to transition
1742 return true;
1743 }
1744 }
1745 }
1746 return false;
1747}
1748
1749
1750bool
1752 if (coordBase(controller)) {
1753 // Determine if the other phase is also ready to switch
1754 if (controller->getOtherPhase(fromPhase)->readyToSwitch) {
1755 // 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
1756 if (controller->isType170()) {
1757 return true;
1758 }
1759 // If the transition is already active, then report that the movement is possible
1761 return true;
1762 }
1763 // now determine if there my prior phase can fit or not. We already know that I can fit.
1764 NEMAPhase* priorPhase = toPhase->getSequentialPriorPhase();
1765 SUMOTime timeTillForceOff = controller->ModeCycle(priorPhase->forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1766 SUMOTime transitionTime = fromPhase->getTransitionTime(controller);
1767 // if the time till the force off is less than the min duration ||
1768 // if it is greater than the cycle length minus the length of the coordinate phase (which the fromPhase automatically is)
1769 if ((priorPhase->minDuration + transitionTime) > timeTillForceOff || timeTillForceOff > (controller->getCurrentCycleLength() - fromPhase->minDuration)) {
1770 return true;
1771 }
1772 }
1773 }
1774 return false;
1775}
1776
1777int
1779 // Returns the other transitions distance in green transfer situations
1780 if ((toPhase == fromPhase) && (otherTrans->toPhase->barrierNum == toPhase->barrierNum)) {
1782 return otherTrans->distance;
1783 }
1784 }
1785 return distance;
1786}
long long int SUMOTime
Definition GUI.h:36
#define INVALID_POSITION
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:271
#define WRITE_WARNING(msg)
Definition MsgHandler.h:270
#define TL(string)
Definition MsgHandler.h:287
#define TLF(string,...)
Definition MsgHandler.h:288
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 permission forbids vehicles.
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_PEDESTRIAN
pedestrian
int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
@ 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
static bool dictionary(const std::string &id, MSLane *lane)
Static (sic!) container methods {.
Definition MSLane.cpp:2325
const std::vector< MSLink * > & getLinkCont() const
returns the container with all links !!!
Definition MSLane.h:707
MSDetectorControl & getDetectorControl()
Returns the detector control.
Definition MSNet.h:443
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition MSNet.cpp:183
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 it's 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.
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
SUMOTime calcVehicleExtension(SUMOTime duration)
}
void init(NEMALogic *controller, int crossPhaseTarget, int crossPhaseSource, bool latching)
initializes the object
PhasePtr myInstance
bool isTransitionActive() const
check if a transition is active
PhaseTransitionLogic * getTransition(int toPhase)
return the PhaseTransitionLogic matching the toPhase
SUMOTime maxDuration
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