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