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