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.org/sumo
3 // Copyright (C) 2001-2022 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 /****************************************************************************/
19 // An actuated NEMA-phase-compliant traffic light logic
20 /****************************************************************************/
21 #include <config.h>
22 
23 #include <cassert>
24 #include <utility>
25 #include <vector>
26 #include <bitset>
30 #include <microsim/MSGlobals.h>
31 #include <microsim/MSNet.h>
32 #include "MSTrafficLightLogic.h"
33 #include "NEMAController.h"
34 #include <microsim/MSLane.h>
35 #include <microsim/MSEdge.h>
40 #include <sstream>
41 #include <iostream>
42 // ===========================================================================
43 // parameter defaults definitions
44 // ===========================================================================
45 
46 
47 #define INVALID_POSITION std::numeric_limits<double>::max() // tl added
48 
49 // #define DEBUG_NEMA
50 
51 // ===========================================================================
52 // method definitions
53 // ===========================================================================
55  const std::string& id, const std::string& programID,
56  const SUMOTime _offset,
57  const Phases& phases,
58  int /*step*/, SUMOTime /*delay*/,
59  const std::map<std::string, std::string>& parameter,
60  const std::string& basePath) :
61  MSSimpleTrafficLightLogic(tlcontrol, id, programID, _offset, TrafficLightType::NEMA, phases, 0, phases.front()->minDuration + SIMSTEP, parameter),
62  myPhase(phases[0]->duration, phases[0]->getState()) {
63  myDetectorLength = StringUtils::toDouble(getParameter("detector-length", "20"));
64  myDetectorLengthLeftTurnLane = StringUtils::toDouble(getParameter("detector-length-leftTurnLane", "20"));
65  myCycleLength = (StringUtils::toDouble(getParameter("total-cycle-length", getParameter("cycle-length", getParameter(toString(SUMO_ATTR_CYCLETIME), "60")))));
68  myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
69  myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
71  myVehicleTypes = getParameter("vTypes", "");
72  ring1 = getParameter("ring1", "");
73  ring2 = getParameter("ring2", "");
74 
75  std::vector<int> VecMinRecall = readParaFromString(getParameter("minRecall", "1,2,3,4,5,6,7,8"));
76  for (int i = 0; i < (int)VecMinRecall.size(); i++) {
77  minRecalls[VecMinRecall[i] - 1] = true;
78  recall[VecMinRecall[i] - 1] = true;
79  }
80 
81  std::vector<int> VecMaxRecall = readParaFromString(getParameter("maxRecall", ""));
82  for (int i = 0; i < (int)VecMaxRecall.size(); i++) {
83  maxRecalls[VecMaxRecall[i] - 1] = true;
84  recall[VecMaxRecall[i] - 1] = true;
85  }
86 
87 #ifdef DEBUG_NEMA
88  std::cout << "minRecall: ";
89  for (int i = 0; i < 8; i++) {
90  std::cout << minRecalls[i] << '\t';
91  }
92  std::cout << std::endl;
93 
94  std::cout << "maxRecall: ";
95  for (int i = 0; i < 8; i++) {
96  std::cout << maxRecalls[i] << '\t';
97  }
98  std::cout << std::endl;
99 #endif
100  barriers = getParameter("barrierPhases", "");
101  coordinates = getParameter("coordinatePhases", getParameter("barrier2Phases", ""));
102  fixForceOff = StringUtils::toBool(getParameter("fixForceOff", "false"));
103  offset = STEPS2TIME(_offset);
105  whetherOutputState = StringUtils::toBool(getParameter("whetherOutputState", "false"));
106  coordinateMode = StringUtils::toBool(getParameter("coordinate-mode", "false"));
107  greenTransfer = StringUtils::toBool(getParameter("greenTransfer", "true"));
108 
109  //missing parameter error
110  error_handle_not_set(ring1, "ring1");
111  error_handle_not_set(ring2, "ring2");
112  error_handle_not_set(barriers, "barrierPhases");
113  error_handle_not_set(coordinates, "barrier2Phases or coordinatePhases");
114 
115  //print to check
116 #ifdef DEBUG_NEMA
117  std::cout << "JunctionID = " << myID << std::endl;
118  std::cout << "All parameters after calling constructor are: " << std::endl;
119  std::cout << "myDetectorLength = " << myDetectorLength << std::endl;
120  std::cout << "cycleLength = " << myCycleLength << std::endl;
121  std::cout << "ring1 = " << ring1 << std::endl;
122  std::cout << "ring2 = " << ring2 << std::endl;
123  std::cout << "barriers = " << barriers << std::endl;
124  std::cout << "coordinates = " << coordinates << std::endl;
125  std::cout << "offset = " << offset << std::endl;
126  std::cout << "whetherOutputState = " << whetherOutputState << std::endl;
127  std::cout << "myShowDetectors = " << myShowDetectors << std::endl;
128  std::cout << "coordinateMode = " << coordinateMode << std::endl;
129  std::cout << "fixForceOff = " << fixForceOff << std::endl;
130  std::cout << "greenTransfer = " << greenTransfer << std::endl;
131  std::cout << "You reach the end of constructor" << std::endl;
132  std::cout << "****************************************\n";
133 #endif
134 
135  // construct the phaseDetectorMapping. In the future this could hold more parameters, such as lock in time or delay
136  for (auto p : readParaFromString(ring1)) {
137  // #TODO clean up this string mess
138  std::string cps = "crossPhaseSwitching:";
139  int crossPhase = StringUtils::toInt(getParameter(cps.append(std::to_string(p)), "0"));
140  phase2DetectorMap[p] = phaseDetectorInfo(crossPhase);
141  }
142  for (auto p : readParaFromString(ring2)) {
143  std::string cps = "crossPhaseSwitching:";
144  int crossPhase = StringUtils::toInt(getParameter(cps.append(std::to_string(p)), "0"));
145  phase2DetectorMap[p] = phaseDetectorInfo(crossPhase);
146  }
147  // Construct the Cross Mapping
148  for (auto const& phaseDetectInfo : phase2DetectorMap) {
149  if (phaseDetectInfo.second.cpdSource > 0) {
150  phase2DetectorMap.find(phaseDetectInfo.second.cpdSource) -> second.cpdTarget = phaseDetectInfo.first;
151  }
152  }
153 
154 }
155 
157 
158 void
160  //init the base path for output state
161  outputStateFilePath = outputStateFilePath + "/" + myID + "_state_output";
162  // std::cout << "outputStaetFilePath = " << outputStateFilePath << std::endl;
163  //init cycleRefPoint
164  cycleRefPoint = 0;
165 
166  //init outputStateFile
167  if (whetherOutputState) {
169  outputStateFile << "Output state changes:\n";
170  outputStateFile.close();
171  }
172  //init phaseStartTime and phaseExpectedDuration
173  int phaseNumber = 8;
174  for (int i = 0; i < phaseNumber; i++) {
175  phaseStartTime[i] = 0;
176  phaseExpectedDuration[i] = 0;
177  }
178 
179  //print to check
180  //init minGreen, maxGreen, vehExt, red, and yellow
181  for (MSPhaseDefinition* phase : myPhases) {
182  int NEMAPhase = string2int(phase->getName());
183  int i = NEMAPhase - 1;
184  // std::string indexFromName = phase->getName();
185  // std::stringstream ss(indexFromName);
186  // int NEMAPhase = 0;
187  // ss << NEMAPhase;
188 #ifdef DEBUG_NEMA
189  std::cout << "NEMAIndex = " << NEMAPhase << ": ";
190 #endif
191  minGreen[i] = STEPS2TIME(phase->minDuration);
192  maxGreen[i] = STEPS2TIME(phase->maxDuration);
193  nextMaxGreen[i] = maxGreen[i];
194  vehExt[i] = STEPS2TIME(phase->vehext);
195  yellowTime[i] = STEPS2TIME(phase->yellow);
196  redTime[i] = STEPS2TIME(phase->red);
197  //map state G index to laneIDs
198  std::string state = phase->getState();
199  std::set<std::string> laneIDs = getLaneIDsFromNEMAState(state);
200  std::vector<std::string> laneIDs_vector;
201  for (std::string laneID : laneIDs) {
202  laneIDs_vector.push_back(laneID);
203  }
204  phase2ControllerLanesMap[NEMAPhase] = laneIDs_vector;
205 #ifdef DEBUG_NEMA
206  std::cout << "minGreen = " << minGreen[i] << "; maxGreen = " << maxGreen[i] << "; vehext = " << vehExt[i] << "; yellow = " << yellowTime[i] << "; redTime = " << redTime[i] << std::endl;
207 #endif
208  }
209 
210 
211 #ifdef DEBUG_NEMA
212  //print to check the phase2ControllerLanesMap
213  for (auto item : phase2ControllerLanesMap) {
214  std::cout << "NEMA phase index = " << item.first << " have lanes: ";
215  for (auto id : item.second) {
216  std::cout << id << " ";
217  }
218  std::cout << std::endl;
219  }
220 #endif
221 
222 
223  //init rings
224  rings.push_back(readParaFromString(ring1));
225  rings.push_back(readParaFromString(ring2));
226 
227 
228 
229 #ifdef DEBUG_NEMA
230  //print to check
231  for (int i = 0; i < (int)rings.size(); i++) {
232  int count = 0;
233  std::cout << "Ring" << i + 1 << " includes phases: \t";
234  for (auto j : rings[i]) {
235  count++;
236  std::cout << j << " ";
237  if (count == 2 || count == 4) {
238  std::cout << " | ";
239  }
240  }
241  std::cout << std::endl;
242  }
243 #endif
244 
245  //init barriers
248 
249  //init the active index for rings and barriers
250  activeRing1Index = 0;
251  activeRing2Index = 0;
252  activeRing1Phase = 0;
253  activeRing2Phase = 0;
254 
255  for (int i = 0; (int)rings[0].size(); i++) {
256  if (rings[0][i] != 0) {
257 #ifdef DEBUG_NEMA
258  std::cout << "rings[0][" << i << "] = " << rings[0][i] << std::endl;
259 #endif
260  activeRing1Index = i;
262  break;
263  }
264  }
265  for (int i = 0; (int)rings[1].size(); i++) {
266  if (rings[1][i] != 0) {
267 #ifdef DEBUG_NEMA
268  std::cout << "rings[1][" << i << "] = " << rings[1][i] << std::endl;
269 #endif
270  activeRing2Index = i;
272  break;
273  }
274  }
275  int initialIndexRing [2] = { activeRing1Index, activeRing2Index};
276  // calculate force offs
277  for (int ringNumber = 0; ringNumber < 2; ringNumber++) {
278  int length = (int)rings[ringNumber].size();
279  int aPhaseNumber = rings[ringNumber][initialIndexRing[ringNumber]];
280  int aPhaseIndex = aPhaseNumber - 1;
281  int nPhaseIndex = aPhaseIndex; //next phase
282  int nPhaseNumber = aPhaseNumber;
283  forceOffs[aPhaseNumber - 1] = maxGreen[aPhaseNumber - 1];
284 
285 #ifdef DEBUG_NEMA
286  std::cout << "Phase " << aPhaseNumber << ": force off " << forceOffs[aPhaseNumber - 1] << std::endl;
287 #endif
288  for (int i = initialIndexRing[ringNumber] + 1; i < length; i++) {
289  nPhaseNumber = rings[ringNumber][i];
290  nPhaseIndex = nPhaseNumber - 1;
291  // std::cout <<" ring "<<ringNumber <<" i: "<<i<< " phase: "<<nPhaseNumber<< std::endl;
292  if (nPhaseNumber != 0) {
293  forceOffs[nPhaseIndex] = forceOffs[aPhaseIndex] + maxGreen[nPhaseIndex] + yellowTime[aPhaseIndex] + redTime[aPhaseIndex];
294  aPhaseNumber = nPhaseNumber;
295  aPhaseIndex = nPhaseIndex;
296 
297 #ifdef DEBUG_NEMA
298  std::cout << "- Phase " << aPhaseNumber << ": force off " << forceOffs[aPhaseIndex] << std::endl;
299 #endif
300  }
301  }
302  }
303 
304  // calculate initial phases based on in cycle clock
305  for (int ringNumber = 0; ringNumber < 2; ringNumber++) {
306  int length = (int)rings[ringNumber].size();
307  for (int i = initialIndexRing[ringNumber]; i < length; i++) {
308  int aPhaseIndex = rings[ringNumber][i] - 1;
309  if (aPhaseIndex != -1) {
310  phaseCutOffs[aPhaseIndex] = forceOffs[aPhaseIndex] - minGreen[aPhaseIndex];
311 #ifdef DEBUG_NEMA
312  std::cout << "Phase " << aPhaseIndex + 1 << " cut off is " << phaseCutOffs[aPhaseIndex] << std::endl;
313 #endif
314  }
315  }
316  }
317 
318  // find the current in cycle time
320  double currentTimeInSecond = STEPS2TIME(now);
321  double currentInCycleTime = ModeCycle(currentTimeInSecond - cycleRefPoint - offset, myCycleLength);
322 
323  // find the initial phases
324  for (int ringNumber = 0; ringNumber < 2; ringNumber++) {
325  int length = (int)rings[ringNumber].size();
326  int aPhaseIndex = -1;
327  bool found = false;
328  for (int i = initialIndexRing[ringNumber]; i < length; i++) {
329  aPhaseIndex = rings[ringNumber][i] - 1;
330  if (aPhaseIndex != -1) {
331  if (currentInCycleTime < phaseCutOffs[aPhaseIndex]) {
332 #ifdef DEBUG_NEMA
333  std::cout << "current in cycle time=" << currentInCycleTime << " phase: " << aPhaseIndex << std::endl;
334 #endif
335  found = true;
336  break;
337  }
338  }
339  }
340  if (!found) {
341  aPhaseIndex = rings[ringNumber][initialIndexRing[ringNumber]] - 1; // if the break didn't get triggered, go back to the beginning.
342  }
343 #ifdef DEBUG_NEMA
344  std::cout << "current in cycle time=" << currentInCycleTime << " ring " << ringNumber << " aphase: " << aPhaseIndex + 1 << std::endl;
345 #endif
346  if (ringNumber == 0) {
347  activeRing1Index = aPhaseIndex;
349  } else {
350  activeRing2Index = aPhaseIndex;
352  }
353  }
354 
355 
356 
357 #ifdef DEBUG_NEMA
358  //print to check the rings and barriers active phase
359  std::cout << "After init, active ring1 phase is " << activeRing1Phase << std::endl;
360  std::cout << "After init, active ring2 phase is " << activeRing2Phase << std::endl;
361 
362 
363  //print to check the phase definition is correct
364  std::cout << "Print to check NEMA phase definitions\n";
365  for (auto p : myPhases) {
366  std::cout << "index = " << p->getName() << "; ";
367  std::cout << "duration (useless) = " << time2string(p->duration) << "; ";
368  std::cout << "minDur = " << time2string(p->minDuration) << "; ";
369  std::cout << "maxDur = " << time2string(p->maxDuration) << "; ";
370  std::cout << "vehext = " << time2string(p->vehext) << "; ";
371  std::cout << "yellow = " << time2string(p->yellow) << "; ";
372  std::cout << "red = " << time2string(p->red) << "; ";
373  std::cout << "state = " << p->getState() << std::endl;
374  }
375 #endif
376 
377  //init the traffic light
379  assert(myLanes.size() > 0);
380  //iterate through the lanes and build one E2 detector for each lane associated with the traffic light control junction
381  for (const LaneVector& lanes : myLanes) {
382  for (MSLane* const lane : lanes) {
383  //decide the detector length
384  double detector_length = 0;
385  if (isLeftTurnLane(lane)) {
386  detector_length = myDetectorLengthLeftTurnLane;
387  } else {
388  detector_length = myDetectorLength;
389  }
390  if (noVehicles(lane->getPermissions())) {
391  // do not build detectors on green verges or sidewalks
392  continue;
393  }
394  // Build detector and register them in the detector control
395  if (myLaneDetectorMap.find(lane) == myLaneDetectorMap.end()) {
396  MSE2Collector* det = nullptr;
397  const std::string customID = getParameter(lane->getID());
398  if (customID != "") {
400  if (det == nullptr) {
401  WRITE_ERROR("Unknown laneAreaDetector '" + customID + "' given as custom detector for NEMA tlLogic '" + getID() + "', program '" + getProgramID() + ".");
402  continue;
403  }
404  //set the detector to be visible in gui
406  } else {
407  std::string id = "TLS_" + myID + "_" + myProgramID + "_E2DetectorOnLane_" + lane->getID();
408  // std::cout << "The detectorID = " << id << std::endl;
409  //createE2Detector() method will lead to bad detector showing in sumo-gui
410  //so it is better to use build2Detector() rather than createE2Detector()
411  // det = nb.createE2Detector(id, DU_TL_CONTROL, lane, INVALID_POSITION, lane->getLength(), myDetectorLength, 0, 0, 0, myVehicleTypes, myShowDetectors);
412  // MSNet::getInstance()->getDetectorControl().add(SUMO_TAG_LANE_AREA_DETECTOR, det, myFile, myFreq);
413  nb.buildE2Detector(id, //detectorID
414  lane, //lane to build this detector
415  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
416  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
417  detector_length, //detector length
418  myFile, // detector information output file
419  myFreq, // detector reading interval
420  0, // time-based threshold that decribes how much time has to pass until a vehicle is considerred as halting
421  0, // speed threshold as halting
422  0, // minimum dist to the next standing vehicle to make this vehicle count as a participant to the jam
423  myVehicleTypes, //vehicle types to consider, if it is empty, meaning consider all types of vehicles
424  false, // detector position check. More details could be found on SUMO web
425  myShowDetectors, // whether to show detectors in sumo-gui
426  0, //traffic light that triggers aggregation when swithing
427  0); // outgoing lane that associated with the traffic light
428 
429  //get the detector to be used in the lane detector map loading
431  }
432  // print to check
433  // std::cout << "E2Detector " << det->getID() << " is built on laneID = " << lane->getID() << std::endl;
434 
435  //map the detector to lane and lane to detector
436  myLaneDetectorMap[lane] = det;
437  myDetectorLaneMap[det] = lane;
438  myDetectorInfoVector.push_back(DetectorInfo(det, (int)myPhases.size()));
439 
440  }
441  }
442  }
443  //map NEMA phase to detectors
444  // std::cout << "start of NEMA phase to detector map building " << std::endl;
445  for (auto item : phase2ControllerLanesMap) {
446  int NEMAPhaseIndex = item.first;
447  std::vector<std::string> laneIDs = item.second;
448  std::vector<MSE2Collector*> detectors;
449  MSE2Collector* detector = nullptr;
450  for (std::string laneID : laneIDs) {
451  MSLane* lane = MSLane::dictionary(laneID);
452  detector = myLaneDetectorMap[lane];
453  detectors.push_back(detector);
454  }
455  phase2DetectorMap.find(NEMAPhaseIndex) -> second.detectors = detectors;
456  }
457 #ifdef DEBUG_NEMA
458  // print to check phase2DetectorMap
459  std::cout << "Print to check phase2DetectorMap" << std::endl;
460  for (auto item : phase2DetectorMap) {
461  std::cout << "The NEMA phase index = " << item.first << " has detectors: \n";
462  for (auto det : item.second.detectors) {
463  std::cout << '\t' << det->getID() << std::endl;
464  }
465  }
466 #endif
467 
470 
471  // set the next phase to current for initialization
474 
475  // std::cout << "After init, R1State = " << R1State << std::endl;
476  // std::cout << "After init, R2State = " << R2State << std::endl;
477 
478  R1RYG = GREEN;
479  R2RYG = GREEN;
480 
481  wait4R1Green = false;
482  wait4R2Green = false;
483 
486 
489 
490  // Create the barrier to phase mapping;
493 
494 
495 #ifdef DEBUG_NEMA
496  std::cout << "After init, r1/r2 barrier phase = " << r1barrier << " and " << r2barrier << std::endl;
497  std::cout << "After init, r1/r2 coordinate phase = " << r1coordinatePhase << " and " << r2coordinatePhase << std::endl;
498 #endif
499 
500 
501  currentState = "";
502  // currentR1State = myPhases[R1State - 1]->getState();
503  // currentR2State = myPhases[R2State - 1]->getState();
504  for (const MSPhaseDefinition* const p : myPhases) {
505  if (R1State == string2int(p->getName())) {
506  currentR1State = p->getState();
507  }
508  if (R2State == string2int(p->getName())) {
509  currentR2State = p->getState();
510  }
511  }
512 #ifdef DEBUG_NEMA
513  std::cout << "R1State = " << R1State << " and its state = " << currentR1State << std::endl;
514  std::cout << "R2State = " << R2State << " and its state = " << currentR2State << std::endl;
515 #endif
516 
517  //Do not delete. SUMO traffic logic check.
518  //SUMO check begin
519  const SVCPermissions motorized = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
520  std::map<int, std::set<MSE2Collector*>> linkToDetectors;
521  std::set<int> actuatedLinks;
522 
523  const int numLinks = (int)myLinks.size();
524  std::vector<bool> neverMajor(numLinks, true);
525  for (const MSPhaseDefinition* phase : myPhases) {
526  const std::string& state = phase->getState();
527  for (int i = 0; i < numLinks; i++) {
528  if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
529  neverMajor[i] = false;
530  }
531  }
532  }
533  std::vector<bool> oneLane(numLinks, false);
534  for (int i = 0; i < numLinks; i++) {
535  for (MSLane* lane : getLanesAt(i)) {
536  int numMotorized = 0;
537  for (MSLane* l : lane->getEdge().getLanes()) {
538  if ((l->getPermissions() & motorized) != 0) {
539  numMotorized++;
540  }
541  }
542  if (numMotorized == 1) {
543  oneLane[i] = true;
544  break;
545  }
546  }
547  }
548 
549  for (const MSPhaseDefinition* phase : myPhases) {
550  const int phaseIndex = (int)myDetectorForPhase.size();
551  std::set<MSE2Collector*> detectors;
552  if (phase->isActuted()) {
553  const std::string& state = phase->getState();
554  std::set<int> greenLinks;
555  std::map<MSE2Collector*, std::set<int>> detectorLinks;
556 
557  for (int i = 0; i < numLinks; i++) {
558  if (state[i] == LINKSTATE_TL_GREEN_MAJOR
559  || (state[i] == LINKSTATE_TL_GREEN_MINOR
560  && ((neverMajor[i] // check1a
561  && hasMajor(state, getLanesAt(i))) // check1b
562  || oneLane[i])) // check1c
563  ) {
564  greenLinks.insert(i);
565  actuatedLinks.insert(i);
566  }
567 
568  for (MSLane* lane : getLanesAt(i)) {
569  if (myLaneDetectorMap.count(lane) != 0) {
570  detectorLinks[myLaneDetectorMap[lane]].insert(i);
571  }
572  }
573  }
574  for (auto& item : detectorLinks) {
575  MSE2Collector* det = item.first;
576  MSLane* detectorLane = myDetectorLaneMap[det];
577  bool usable = true;
578  // check 1
579  for (int j : item.second) {
580  if (greenLinks.count(j) == 0) {
581  usable = false;
582  }
583  }
584 
585  //check 2
586  if (usable) {
587  for (MSLink* link : detectorLane->getLinkCont()) {
588  MSLane* next = link->getLane();
589  if (myLaneDetectorMap.count(next) != 0) {
590  MSE2Collector* nextDet = myLaneDetectorMap[next];
591  for (int j : detectorLinks[nextDet]) {
592  if (greenLinks.count(j) == 0) {
593  usable = false;
594  break;
595  }
596  }
597  }
598  }
599  }
600 
601  if (usable) {
602  detectors.insert(item.first);
603  for (int j : item.second) {
604  linkToDetectors[j].insert(item.first);
605  }
606  }
607  }
608  if (detectors.size() == 0) {
609  WRITE_WARNINGF("At NEMA tlLogic '%', actuated phase % has no controlling detector", getID(), toString(phaseIndex));
610  }
611  }
612  std::vector<DetectorInfo*> detectorInfos;
613  myDetectorForPhase.push_back(detectorInfos);
614  for (MSE2Collector* det : detectors) {
615  for (DetectorInfo& detInfo : myDetectorInfoVector) {
616  if (detInfo.det == det) {
617  myDetectorForPhase.back().push_back(&detInfo);
618  detInfo.servedPhase[phaseIndex] = true;
619  }
620  }
621  }
622  }
623 
624  for (int i : actuatedLinks) {
625  if (linkToDetectors[i].size() == 0 && myLinks[i].size() > 0
626  && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
627  WRITE_WARNINGF("At NEMA tlLogic '%, linkIndex % has no controlling detector", getID(), toString(i));
628  }
629  }
630 
631  std::string state1 = transitionState(currentR1State, GREEN);
632  std::string state2 = transitionState(currentR2State, GREEN);
633  myPhase.setState(combineStates(state1, state2));
635 
636 
637  //validating timing
638  validate_timing();
639 #ifdef DEBUG_NEMA
640  //std::cout << "reach the end of init()\n";
641 #endif
642 }
643 
644 void
646  const bool ignoreErrors = StringUtils::toBool(getParameter("ignore-errors", "false"));
647  //check cycle length
648  for (int ringIndex = 0; ringIndex <= 1; ringIndex++) {
649  int lastPhasePositionIndex = (int)rings[ringIndex].size() - 1;
650  int lastPhase = rings[ringIndex][lastPhasePositionIndex];
651  if (lastPhase == 0) { //should be enough
652  lastPhase = rings[ringIndex][lastPhasePositionIndex - 1];
653  }
654  int lastPhaseIndex = lastPhase - 1;
655  double cycleLengthCalculated = forceOffs[lastPhaseIndex] + yellowTime[lastPhaseIndex] + redTime[lastPhaseIndex];
656  if (coordinateMode && cycleLengthCalculated != myCycleLength) {
657  int ringNumber = ringIndex + 1;
658  const std::string error = "At NEMA tlLogic '" + getID() + "', Ring " + toString(ringNumber) + " does not add to cycle length.";
659  if (ignoreErrors) {
660  WRITE_WARNING(error);
661  } else {
662  throw ProcessError(error);
663  }
664  }
665  }
666  // check barriers
667  double ring1barrier1_length = forceOffs[r1barrier - 1] + yellowTime[r1barrier - 1] + redTime[r1barrier - 1];
668  double ring2barrier1_length = forceOffs[r2barrier - 1] + yellowTime[r2barrier - 1] + redTime[r2barrier - 1];
669  if (ring1barrier1_length != ring2barrier1_length) {
670  const std::string error = "At NEMA tlLogic '" + getID() + "', the phases before barrier 1 from both rings do not add up. (ring1="
671  + toString(ring1barrier1_length) + ", ring2=" + toString(ring2barrier1_length) + ")";
672  if (coordinateMode && !ignoreErrors) {
673  throw ProcessError(error);
674  } else {
675  WRITE_WARNING(error);
676  }
677  }
678  double ring1barrier2_length = forceOffs[r2coordinatePhase - 1] + yellowTime[r2coordinatePhase - 1] + redTime[r2coordinatePhase - 1];
679  double ring2barrier2_length = forceOffs[r1coordinatePhase - 1] + yellowTime[r1coordinatePhase - 1] + redTime[r1coordinatePhase - 1];
680  if (ring1barrier2_length != ring2barrier2_length) {
681  const std::string error = "At NEMA tlLogic '" + getID() + "', the phases before barrier 2 from both rings do not add up. (ring1="
682  + toString(ring1barrier2_length) + ", ring2=" + toString(ring2barrier2_length) + ")";
683  if (coordinateMode && !ignoreErrors) {
684  throw ProcessError(error);
685  } else {
686  WRITE_WARNING(error);
687  }
688  }
689  // no offset for non coordinated
690  if (!coordinateMode && offset != 0) {
691  WRITE_WARNINGF("NEMA tlLogic '%' is not coordinated but an offset was set.", getID());
692  }
693 }
694 
695 void
696 NEMALogic::setNewSplits(std::vector<double> newSplits) {
697  assert(newSplits.size() == 8);
698  for (int i = 0; i < 8; i++) {
699  nextMaxGreen[i] = newSplits[i] - yellowTime[i] - redTime[i];
700  }
701 }
702 
703 
704 void
705 NEMALogic::setNewMaxGreens(std::vector<double> newMaxGreens) {
706  for (int i = 0; i < 8; i++) {
707  nextMaxGreen[i] = newMaxGreens[i];
708  }
709 }
710 
711 
712 void
713 NEMALogic::setNewCycleLength(double newCycleLength) {
714  myNextCycleLength = newCycleLength;
715 }
716 
717 
718 void
719 NEMALogic::setNewOffset(double newOffset) {
720  myNextOffset = newOffset;
721 }
722 
723 //helper methods
724 
725 std::vector<int> NEMALogic::readParaFromString(std::string s) {
726  std::vector<int> output;
727  for (char c : s) {
728  if (c >= '0' && c <= '9') {
729  int temp = c - '0';
730  output.push_back(temp);
731  }
732  }
733  return output;
734 }
735 
736 std::vector<std::string> NEMALogic::string2vector(std::string s) {
737  std::vector<std::string> output;
738  std::stringstream ss(s);
739  while (ss.good()) {
740  std::string substr;
741  std::getline(ss, substr, ',');
742  output.push_back(substr);
743  }
744 #ifdef DEBUG_NEMA
745  //print to check
746  for (auto i : output) {
747  std::cout << i << std::endl;
748  }
749 #endif
750  return output;
751 }
752 
753 std::string NEMALogic::combineStates(std::string state1, std::string state2) {
754  std::string output = "";
755  if (state1.size() != state2.size()) {
756  throw ProcessError("At NEMA tlLogic '" + getID() + "', different sizes of NEMA phase states. Please check the NEMA XML");
757  }
758  for (int i = 0; i < (int)state1.size(); i++) {
759  char ch1 = state1[i];
760  char ch2 = state2[i];
761 
762  // check through this order. 'G' overwrite 'g'.
763  if (ch1 == 'G' || ch2 == 'G') {
764  output += 'G';
765  } else if (ch1 == 'g' || ch2 == 'g') {
766  output += 'g';
767  } else if (ch1 == 's' || ch2 == 's') {
768  output += 's';
769  } else if (ch1 == 'y' || ch2 == 'y') {
770  output += 'y';
771  } else if (ch1 == 'u' || ch2 == 'u') {
772  output += 'u';
773  } else if (ch1 == 'O' || ch2 == 'O') {
774  output += 'O';
775  } else if (ch1 == 'o' || ch2 == 'o') {
776  output += 'o';
777  } else {
778  output += 'r';
779  }
780  }
781  return output;
782 }
783 
784 bool NEMALogic::isDetectorActivated(int phaseNumber, int depth = 0) const {
785  if (phase2DetectorMap.find(phaseNumber) == phase2DetectorMap.end()) {
786  return false;
787  } else {
788  auto const& detectInfo = phase2DetectorMap.find(phaseNumber)->second;
789  if ((phaseNumber != R1State) && (phaseNumber != R2State) && depth < 1) {
790  // If I am not the active phase & my target is an active phase, don't report when I am called for my own phase
791  if ((detectInfo.cpdTarget == R1State && R1RYG >= GREEN) || (detectInfo.cpdTarget == R2State && R2RYG >= GREEN)) {
792  return false;
793  }
794  }
795  for (auto det : detectInfo.detectors) {
796  if (det->getCurrentVehicleNumber() > 0) {
797  return true;
798  }
799  }
800  if (detectInfo.cpdSource > 0 && depth < 1) {
801  return isDetectorActivated(detectInfo.cpdSource, depth + 1);
802  }
803  return false;
804  }
805 }
806 
807 
808 std::map<std::string, double>
810  std::map<std::string, double> result;
811  for (auto item : myDetectorLaneMap) {
812  result[item.first->getID()] = item.first->getCurrentVehicleNumber();
813  }
814  return result;
815 }
816 
817 
818 const MSPhaseDefinition&
820  return myPhase;
821 }
822 
823 SUMOTime
825  const std::string newState = NEMA_control();
826  if (newState != myPhase.getState()) {
827  myPhase.setState(newState);
828  // ensure that SwitchCommand::execute notices a change
829  myStep = 1 - myStep;
830  }
831  //std::cout << SIMTIME << " " << myPhase.getState() << "\n";
832  return TIME2STEPS(TS);
833 }
834 
835 
836 std::string
838  std::string outputState = "";
839  //controller starts
841  double currentTimeInSecond = STEPS2TIME(now);
842 
843 #ifdef DEBUG_NEMA
844  //print to check
845  //I didn't use getTimeInCycle(). This is because the cycle reference point may change in the future.
846  double currentInCycleTime = ModeCycle(currentTimeInSecond - cycleRefPoint - offset, myCycleLength);
847  std::cout << "current time in cycle:\t" << currentInCycleTime << "\t" << "phases: " << R1State << '\t' << R2State << std::endl;
848 #endif
849  //int R1Phase = activeRing1Phase;
850  int R1Phase = R1State;
851  int R1Index = R1Phase - 1;
852 
853  double durationR1 = currentTimeInSecond - phaseStartTime[R1Index];
854  double phaseStartTimeInCycleR1 = ModeCycle(phaseStartTime[R1Index] - cycleRefPoint - offset, myCycleLength);
855  //ensure minGreen for each phase
856  if (maxRecalls[R1Index]) {
857  phaseExpectedDuration[R1Index] = maxGreen[R1Index];
858  } else {
859  phaseExpectedDuration[R1Index] = MAX2(phaseExpectedDuration[R1Index], minGreen[R1Index]);
860  }
861  if (((R1Phase != r1coordinatePhase) || (vehExt[R1Index] > 0 && !coordinateMode)) && (R1RYG == GREEN || R1RYG == GREENREST)) {
862  if (isDetectorActivated(R1Phase)) {
863  phaseExpectedDuration[R1Index] = MAX2(phaseExpectedDuration[R1Index], durationR1 + vehExt[R1Index]);
864  if (fixForceOff) {
865  phaseExpectedDuration[R1Index] = MIN2(phaseExpectedDuration[R1Index], ModeCycle(forceOffs[R1Index] - phaseStartTimeInCycleR1, myCycleLength));
866 #ifdef DEBUG_NEMA
867  std::cout << "R1 phase " << R1State << " forceOff " << forceOffs[R1Index] << "\tphase start " << phaseStartTimeInCycleR1 << std::endl;
868 #endif
869  } else {
870  phaseExpectedDuration[R1Index] = MIN2(phaseExpectedDuration[R1Index], maxGreen[R1Index]);
871  }
872  }
873  }
874 
875  int R2Phase = R2State;
876  int R2Index = R2Phase - 1;
877  double durationR2 = currentTimeInSecond - phaseStartTime[R2Index];
878  double phaseStartTimeInCycleR2 = ModeCycle(phaseStartTime[R2Index] - cycleRefPoint - offset, myCycleLength);
879 
880  if (maxRecalls[R2Index]) {
881  phaseExpectedDuration[R2Index] = maxGreen[R2Index];
882  } else {
883  phaseExpectedDuration[R2Index] = MAX2(phaseExpectedDuration[R2Index], minGreen[R2Index]);
884  }
885  if ((((R2Phase != r2coordinatePhase && R2Phase >= 5) || ((R2Phase >= 5 && vehExt[R2Index] > 0) && !coordinateMode)) && (R2RYG == GREEN || R2RYG == GREENREST))) {
886  if (isDetectorActivated(R2Phase)) {
887  phaseExpectedDuration[R2Index] = MAX2(phaseExpectedDuration[R2Index], durationR2 + vehExt[R2Index]);
888  if (fixForceOff) {
889  phaseExpectedDuration[R2Index] = MIN2(phaseExpectedDuration[R2Index], ModeCycle(forceOffs[R2Index] - phaseStartTimeInCycleR2, myCycleLength));
890 #ifdef DEBUG_NEMA
891  std::cout << "R2 phase " << R1State << " forceOff " << forceOffs[R2Index] << "\tphase start " << phaseStartTimeInCycleR2 << std::endl;
892 #endif
893  } else {
894  phaseExpectedDuration[R2Index] = MIN2(phaseExpectedDuration[R2Index], maxGreen[R2Index]);
895  }
896  }
897  }
898 
899  bool EndCurrentPhaseR1 = false;
900  bool EndCurrentPhaseR2 = false;
901  if (durationR1 >= phaseExpectedDuration[R1Index]) {
902  EndCurrentPhaseR1 = true;
903  }
904  if (durationR2 >= phaseExpectedDuration[R2Index]) {
905  EndCurrentPhaseR2 = true;
906  }
907  // Green rest can always transition, even if it is at the barrier
908  if (EndCurrentPhaseR1 && (R1Phase == r1barrier)) {
909  if ((!EndCurrentPhaseR2 || R2RYG < GREEN) && R1RYG != GREENREST) {
910  EndCurrentPhaseR1 = false;
911  }
912  }
913  if (EndCurrentPhaseR1 && (R1Phase == r1coordinatePhase)) {
914  if ((!EndCurrentPhaseR2 || R2RYG < GREEN) && R1RYG != GREENREST) {
915  EndCurrentPhaseR1 = false;
916  }
917  }
918  if (EndCurrentPhaseR2 && (R2Phase == r2barrier)) {
919  if ((!EndCurrentPhaseR1 || R1RYG < GREEN) && R2RYG != GREENREST) {
920  EndCurrentPhaseR2 = false;
921  }
922  }
923  if (EndCurrentPhaseR2 && (R2Phase == r2coordinatePhase)) {
924  if ((!EndCurrentPhaseR1 || R1RYG < GREEN) && R2RYG != GREENREST) {
925  EndCurrentPhaseR2 = false;
926  }
927  }
928 
929  // Falling Edge of Green
930  if (EndCurrentPhaseR1 && (!wait4R1Green)) {
931  phaseEndTimeR1 = currentTimeInSecond;
932  phaseExpectedDuration[R1Index] = 0;
933  wait4R1Green = true;
934  }
935  if (EndCurrentPhaseR2 && (!wait4R2Green)) {
936  phaseEndTimeR2 = currentTimeInSecond;
937  phaseExpectedDuration[R2Index] = 0;
938  wait4R2Green = true;
939  }
940 
941  // Logic for Green Rest & Green Transfer
942  // This requires a detector check. It should only be entered when the lights are green
943  // This logic doesn't need to enter at all if in coordinated mode and greenTransfer is disabled
944  if (((EndCurrentPhaseR1 && R1RYG >= GREEN) || (EndCurrentPhaseR2 && R2RYG >= GREEN)) && (!coordinateMode || greenTransfer)) {
945  // Calculate the potential next phases.
946  // Have to do it here and below because the "final" traffic light check is at the end of yellow
947  int tempR1Phase;
948  int tempR2Phase;
949  // Get the next phases, with the first option being staying in the current phase
950  std::tie(tempR1Phase, tempR2Phase) = getNextPhases(R1Phase, R2Phase, wait4R1Green, wait4R2Green, true);
951  // entry point to green rest. First check detector status, then determine if this should be up next.
952  // Green rest is effectively the same as being perpetually past the minimum green timer but not changing
953  if ((tempR1Phase == R1Phase && EndCurrentPhaseR1) && (tempR2Phase == R2Phase && EndCurrentPhaseR2) && !coordinateMode) {
954  // mark that the phases are not desired to end
955  EndCurrentPhaseR1 = false;
956  EndCurrentPhaseR2 = false;
957  wait4R1Green = false;
958  wait4R2Green = false;
959  // Timing update. This logic should be checked the next step, so only add the simulation timestep.
960  // Potential that this needs to be extended in the future.
961  phaseEndTimeR1 += TS;
962  phaseEndTimeR2 += TS;
963  // setting the phase start time to current time - the minimum timer
964  // will still allow the phase to be extended with vehicle detection
965  phaseStartTime[R1Index] = currentTimeInSecond - minGreen[R1Index];
966  phaseStartTime[R2Index] = currentTimeInSecond - minGreen[R2Index];
967 
968  // Set my state to Green Rest
969  R1RYG = GREENREST;
970  R2RYG = GREENREST;
971 
972  } else if (tempR1Phase == R1Phase && EndCurrentPhaseR1 && greenTransfer) {
973  // This is the logic for green transfer on Ring 1
974  // Green transfer occurs when current phase should end but there isn't a better one to go to,
975  // even though the other phase might be transitioning
976  if (!EndCurrentPhaseR2 || (tempR2Phase != R2Phase)) {
977  EndCurrentPhaseR1 = false;
978  wait4R1Green = false;
979  phaseEndTimeR1 += TS;
980  if ((R1Phase == r1barrier || R1Phase == r1coordinatePhase) && R1RYG != GREENREST) {
981  // If the "green transfer" is at the barrier, it can't actually move until the other phase is done
982  phaseEndTimeR1 = currentTimeInSecond + phaseExpectedDuration[tempR2Phase - 1];
983  phaseExpectedDuration[R1Index] = phaseExpectedDuration[tempR2Phase - 1];
984  }
986  }
987  } else if (tempR2Phase == R2Phase && EndCurrentPhaseR2 && greenTransfer) {
988  if (!EndCurrentPhaseR1 || (tempR1Phase != R1Phase)) {
989  // This is the logic for green transfer on Ring 2
990  EndCurrentPhaseR2 = false;
991  wait4R2Green = false;
992  phaseEndTimeR2 += TS;
993  if ((R2Phase == r2barrier || R2Phase == r2coordinatePhase) && R2RYG != GREENREST) {
994  // If the "green transfer" is at the barrier, it can't actually move until the other phase is done
995  phaseEndTimeR2 = currentTimeInSecond + phaseExpectedDuration[tempR1Phase - 1];
996  phaseExpectedDuration[R2Index] = phaseExpectedDuration[tempR1Phase - 1];
997  }
999  }
1000  }
1001  }
1002 
1003 
1004  // Calculate the next phase with knowledge of both rings
1005  // Next Phase should be calculated on the falling edge of yellow
1006  bool calculate = false;
1007  if (wait4R1Green || wait4R2Green) {
1008  if ((currentTimeInSecond - phaseEndTimeR1 >= yellowTime[R1Index]) && (R1RYG == YELLOW)) {
1009  R1RYG = RED; //red
1010  calculate = true;
1011  }
1012  if ((currentTimeInSecond - phaseEndTimeR2 >= yellowTime[R2Index]) && (R2RYG == YELLOW)) {
1013  R2RYG = RED; //red
1014  calculate = true;
1015  }
1016  if (calculate) {
1017  std::tie(myNextPhaseR1, myNextPhaseR2) = getNextPhases(R1Phase, R2Phase, wait4R1Green, wait4R2Green);
1018  }
1019  }
1020 
1021  //enter transtion phase for Ring1
1022  if (wait4R1Green) {
1023  if (currentTimeInSecond - phaseEndTimeR1 < yellowTime[R1Index]) {
1024  // Not removing this if statement for clarity on the transition timing
1025  R1RYG = YELLOW; //yellow
1026  } else if (currentTimeInSecond - phaseEndTimeR1 < (yellowTime[R1Index] + redTime[R1Index])) {
1027  R1RYG = RED; //red
1028  // TODO: remove the 0.5 (it has timing issues with <1 timesteps)
1029  bool toUpdate = (currentTimeInSecond - phaseEndTimeR1) < yellowTime[R1Index] + 0.5;
1030  if (R1Phase == r1coordinatePhase && toUpdate) {
1031  for (int i = 0; i < 8; i++) {
1032  maxGreen[i] = nextMaxGreen[i];
1033  }
1034  offset = myNextOffset;
1036  }
1037  } else {
1038  //next phase
1039  //time 10 R1Phase = 4. Checked
1040  // R1Phase = nextPhase(rings[0], R1Phase);
1041  R1Phase = myNextPhaseR1;
1042  //offset control not included for now
1043  R1RYG = GREEN; //green
1044  //update phaseStartTime
1045  phaseStartTime[R1Phase - 1] = currentTimeInSecond;
1046 
1047  R1State = R1Phase;
1048  if (R1Phase == r1coordinatePhase) {
1049  if (coordinateMode) {
1050  phaseExpectedDuration[R1Phase - 1] = ModeCycle(myCycleLength - (currentTimeInSecond - cycleRefPoint - offset) - yellowTime[R1Phase - 1] - redTime[R1Phase - 1], myCycleLength);
1051  }
1052  }
1053  wait4R1Green = false;
1054  }
1055  }
1056 
1057  if (wait4R2Green) {
1058  if ((currentTimeInSecond - phaseEndTimeR2) < yellowTime[R2Index]) {
1059  R2RYG = YELLOW;
1060  } else if ((currentTimeInSecond - phaseEndTimeR2) < (yellowTime[R2Index] + redTime[R2Index])) {
1061  R2RYG = RED;
1062  } else {
1063  R2Phase = myNextPhaseR2;
1064  // R2Phase = nextPhase(rings[1], R2Phase);
1065  R2RYG = GREEN;
1066  //update phaseStartTime
1067  phaseStartTime[R2Phase - 1] = currentTimeInSecond;
1068  R2State = R2Phase;
1069  if (R2Phase == r2coordinatePhase) {
1070  if (coordinateMode) {
1071  phaseExpectedDuration[R2Phase - 1] = ModeCycle(myCycleLength - (currentTimeInSecond - cycleRefPoint - offset) - yellowTime[R2Phase - 1] - redTime[R2Phase - 1], myCycleLength);
1072 #ifdef DEBUG_NEMA
1073  std::cout << "R2 phase " << R1Phase << " is coordinated and has an expected duration of " << phaseExpectedDuration[R1Phase - 1] << std::endl;
1074 #endif
1075  }
1076 
1077  }
1078  wait4R2Green = false;
1079  }
1080  }
1081 
1082 
1083  std::string state1 = "";
1084  for (auto p : myPhases) {
1085  if (R1State == string2int(p->getName())) {
1086  state1 = p->getState();
1087  }
1088  }
1089  state1 = transitionState(state1, R1RYG);
1090  currentR1State = state1;
1091 
1092  std::string state2 = "";
1093  for (auto p : myPhases) {
1094  if (R2State == string2int(p->getName())) {
1095  state2 = p->getState();
1096  }
1097  }
1098  state2 = transitionState(state2, R2RYG);
1099  currentR2State = state2;
1100 
1101  outputState = combineStates(state1, state2);
1102 
1103  if (currentState != outputState) {
1104  currentState = outputState;
1105  if (whetherOutputState) {
1106  outputStateFile.open(outputStateFilePath, std::ios_base::app);
1107  outputStateFile << currentTimeInSecond << "\t" << currentState << std::endl;
1108  outputStateFile.close();
1109  }
1110 
1111  }
1112  myPhase.setName(toString(R1Phase) + "+" + toString(R2Phase));
1113  return outputState;
1114 }
1115 
1116 int NEMALogic::nextPhase(std::vector<int> ring, int currentPhase, int& distance, bool sameAllowed) {
1117  int length = (int)ring.size();
1118  int flag = 0;
1119  int nphase = 0; // next phase
1120  int i = 0; // i represents the distance
1121  int matching_i = 0;
1122  for (i = 0; i < length * 2; i++) {
1123  if (flag == 1) {
1124  if (ring[i % length] != 0) {
1125  int tempPhase = ring[i % length];
1126  if (recall[tempPhase - 1] || isDetectorActivated(tempPhase)) {
1127  nphase = tempPhase;
1128  break;
1129  }
1130 
1131 #ifdef DEBUG_NEMA
1132  else {
1133  std::cout << "phase " << tempPhase << " was skipped" << std::endl;
1134  }
1135 #endif
1136  }
1137  }
1138  if (ring[i % length] == currentPhase) {
1139  flag = 1;
1140  matching_i = i;
1141  }
1142  }
1143  if (nphase != 0) {
1144  distance = i;
1145  return nphase;
1146  } else {
1147  // this should only occur in the subset
1148  if (sameAllowed) {
1149  distance = i;
1150  return ring[matching_i % length];
1151  } else {
1152  distance = i + 1;
1153  return ring[(matching_i + 1) % length];
1154  }
1155  }
1156 }
1157 
1158 
1159 void NEMALogic::constructBarrierMap(int ring, std::vector<std::vector<int>>& barrierMap) {
1160  int flag = 0;
1161  std::vector<int> barrierOne;
1162  std::vector<int> barrierTwo;
1163  for (int localPhase : rings[ring]) {
1164  if (!flag) {
1165  barrierOne.push_back(localPhase);
1166  if (((localPhase == r1coordinatePhase || localPhase == r1barrier) && ring == 0) || ((localPhase == r2coordinatePhase || localPhase == r2barrier) && ring == 1)) {
1167  flag = 1;
1168  };
1169  } else {
1170  barrierTwo.push_back(localPhase);
1171  }
1172  };
1173  barrierMap.push_back(barrierOne);
1174  barrierMap.push_back(barrierTwo);
1175 }
1176 
1177 int NEMALogic::findBarrier(int phase, int ring) {
1178  int barrier = 0;
1179  for (int localPhase : myRingBarrierMapping[ring][1]) {
1180  if (phase == localPhase) {
1181  barrier = 1;
1182  break;
1183  }
1184  }
1185  return barrier;
1186 }
1187 
1188 
1189 std::tuple<int, int> NEMALogic::getNextPhases(int R1Phase, int R2Phase, bool toUpdateR1, bool toUpdateR2, bool stayOk) {
1190  // Only 1 or both can be !toUpdate (otherwise we wouldn't be in this situation)
1191  int nextR1Phase = R1Phase;
1192  int nextR2Phase = R2Phase;
1193  if (!toUpdateR1) {
1194  int r1BarrierNum = findBarrier(R1Phase, 0);
1195  int d = 0;
1196  nextR2Phase = nextPhase(myRingBarrierMapping[1][r1BarrierNum], R2Phase, d, stayOk);
1197  // If we aren't updating both, the search range is only the subset of values on the same side of the barrier;
1198  } else if (!toUpdateR2) {
1199  int r2BarrierNum = findBarrier(R2Phase, 1);
1200  int d = 0;
1201  nextR1Phase = nextPhase(myRingBarrierMapping[0][r2BarrierNum], R1Phase, d, stayOk);
1202  } else {
1203  // Both can be updated. We should take the change requiring the least distance travelled around the loop,
1204  // and then recalculate the other ring if it is not in the same barrier
1205  int r1Distance = 0;
1206  int r2Distance = 0;
1207  nextR1Phase = nextPhase(rings[0], R1Phase, r1Distance, stayOk);
1208  nextR2Phase = nextPhase(rings[1], R2Phase, r2Distance, stayOk);
1209  int r1Barrier = findBarrier(nextR1Phase, 0);
1210  int r2Barrier = findBarrier(nextR2Phase, 1);
1211  if ((r1Distance <= r2Distance) && (r1Barrier != r2Barrier)) {
1212  nextR2Phase = nextPhase(myRingBarrierMapping[1][r1Barrier], R2Phase, r2Distance, stayOk);
1213  } else if ((r1Distance > r2Distance) && (r1Barrier != r2Barrier)) {
1214  nextR1Phase = nextPhase(myRingBarrierMapping[0][r2Barrier], R1Phase, r1Distance, stayOk);
1215  };
1216  };
1217  return std::make_tuple(nextR1Phase, nextR2Phase);
1218 }
1219 
1220 //b should be the base of mode
1221 double NEMALogic::ModeCycle(double a, double b) {
1222  double c = a - b;
1223  while (c >= b) {
1224  c = c - b;
1225  }
1226  while (c < 0) { //should be minimum green (or may be not)
1227  c += b;
1228  }
1229  return c;
1230 }
1231 
1232 std::string NEMALogic::transitionState(std::string curState, int RYG) {
1233  std::string newState = "";
1234  if (RYG >= GREEN) {
1235  //Green
1236  newState = curState;
1237 
1238  } else if (RYG == RED) {
1239  // red
1240  for (char ch : curState) {
1241  UNUSED_PARAMETER(ch);
1242  newState += 'r';
1243  }
1244  } else {
1245  // yellow
1246  for (char ch : curState) {
1247  if (ch == 'G' || ch == 'g') {
1248  newState += 'y';
1249  } else {
1250  newState += ch;
1251  }
1252  }
1253  }
1254  return newState;
1255 
1256 }
1257 
1258 
1259 std::set<std::string> NEMALogic::getLaneIDsFromNEMAState(std::string state) {
1260  std::set<std::string> output;
1262  for (int i = 0; i < (int)state.size(); i++) {
1263  char ch = state[i];
1264  if (ch == 'G') {
1265  for (auto link : linkV[i]) {
1266  output.insert(link->getLaneBefore()->getID());
1267  }
1268  }
1269  }
1270  return output;
1271 }
1272 
1273 bool NEMALogic::isLeftTurnLane(const MSLane* const lane) const {
1274  const std::vector<MSLink*> links = lane->getLinkCont();
1275  if (links.size() == 1 && links.front()->getDirection() == LinkDirection::LEFT) {
1276  return true;;
1277  }
1278  return false;
1279 }
1280 
1281 bool
1282 NEMALogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
1283  for (int i = 0; i < (int)state.size(); i++) {
1284  if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
1285  for (MSLane* cand : getLanesAt(i)) {
1286  for (MSLane* lane : lanes) {
1287  if (lane == cand) {
1288  return true;
1289  }
1290  }
1291  }
1292  }
1293  }
1294  return false;
1295 }
1296 
1297 
1298 void
1301  for (auto& item : myLaneDetectorMap) {
1302  item.second->setVisible(true);
1303  }
1304 }
1305 
1306 void
1309  for (auto& item : myLaneDetectorMap) {
1310  item.second->setVisible(false);
1311  }
1312 }
1313 
1314 void
1316  myShowDetectors = show;
1317  for (auto& item : myLaneDetectorMap) {
1318  item.second->setVisible(myShowDetectors);
1319  }
1320 }
1321 
1322 int NEMALogic::string2int(std::string s) {
1323  std::stringstream ss(s);
1324  int ret = 0;
1325  ss >> ret;
1326  return ret;
1327 }
1328 
1329 
1330 const std::string
1331 NEMALogic::getParameter(const std::string& key, const std::string defaultValue) const {
1332  if (StringUtils::startsWith(key, "NEMA.")) {
1333  if (key == "NEMA.phaseCall") {
1334  std::string out_str = std::to_string(isDetectorActivated(1));
1335  for (int i = 2; i <= 8; i++) {
1336  out_str += ",";
1337  out_str += std::to_string(isDetectorActivated(i));
1338  }
1339  return out_str;
1340  } else {
1341  throw InvalidArgument("Unsupported parameter '" + key + "' for NEMA controller '" + getID() + "'");
1342  }
1343  } else {
1344  return Parameterised::getParameter(key, defaultValue);
1345  }
1346 }
1347 
1348 
1349 void
1350 NEMALogic::setParameter(const std::string& key, const std::string& value) {
1351  if (StringUtils::startsWith(key, "NEMA.")) {
1352  if (key == "NEMA.splits" || key == "NEMA.maxGreens") {
1353  //splits="2.0 3.0 4.0 5.0 2.0 3.0 4.0 5.0"
1354  const std::vector<std::string>& tmp = StringTokenizer(value).getVector();
1355  if (tmp.size() != 8) {
1356  throw InvalidArgument("Parameter '" + key + "' for NEMA controller '" + getID() + "' requires 8 space-separated values");
1357  }
1358  std::vector<double> timing;
1359  for (const std::string& s : tmp) {
1360  timing.push_back(StringUtils::toDouble(s));
1361  }
1362  if (key == "NEMA.maxGreens") {
1363  setNewMaxGreens(timing);
1364  } else {
1365  setNewSplits(timing);
1366  }
1367  } else if (key == "NEMA.cycleLength") {
1369  } else if (key == "NEMA.offset") {
1371  } else {
1372  throw InvalidArgument("Unsupported parameter '" + key + "' for NEMA controller '" + getID() + "'");
1373  }
1374  }
1375  Parameterised::setParameter(key, value);
1376 }
1377 
1378 void
1379 NEMALogic::error_handle_not_set(std::string param_variable, std::string param_name) {
1380  if (param_variable == "") {
1381  throw InvalidArgument("Please set " + param_name + " for NEMA tlLogic '" + getID() + "'");
1382  }
1383 }
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:281
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:288
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:280
#define INVALID_POSITION
std::string time2string(SUMOTime t)
convert SUMOTime to string
Definition: SUMOTime.cpp:68
#define STEPS2TIME(x)
Definition: SUMOTime.h:53
#define SIMSTEP
Definition: SUMOTime.h:59
#define TS
Definition: SUMOTime.h:40
#define TIME2STEPS(x)
Definition: SUMOTime.h:55
long long int SUMOTime
Definition: SUMOTime.h:32
bool noVehicles(SVCPermissions permissions)
Returns whether an edge with the given permission forbids vehicles.
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_PEDESTRIAN
pedestrian
int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
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
#define UNUSED_PARAMETER(x)
Definition: StdDefs.h:30
T MIN2(T a, T b)
Definition: StdDefs.h:74
T MAX2(T a, T b)
Definition: StdDefs.h:80
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:82
const std::vector< MSLink * > & getLinkCont() const
returns the container with all links !!!
Definition: MSLane.h:641
static bool dictionary(const std::string &id, MSLane *lane)
Static (sic!) container methods {.
Definition: MSLane.cpp:1991
MSEdge & getEdge() const
Returns the lane's edge.
Definition: MSLane.h:674
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:174
MSDetectorControl & getDetectorControl()
Returns the detector control.
Definition: MSNet.h:439
MSTLLogicControl & getTLSControl()
Returns the tls logics control.
Definition: MSNet.h:449
SUMOTime getCurrentTimeStep() const
Returns the current simulation step.
Definition: MSNet.h:318
The definition of a single phase of a tls logic.
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.
MSTrafficLightLogic * getActive() const
A class that stores and controls tls and switching of their programs.
TLSLogicVariants & get(const std::string &id) const
Returns the variants of a named tls.
std::vector< MSLane * > LaneVector
Definition of the list of arrival lanes subjected to this tls.
virtual void deactivateProgram()
const LinkVectorVector & getLinks() const
Returns the list of lists of all affected links.
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.
std::vector< MSPhaseDefinition * > Phases
Definition of a list of phases, being the junction logic.
std::vector< LinkVector > LinkVectorVector
Definition of a list that holds lists of links that do have the same attribute.
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.
bool recall[8]
double myNextOffset
std::string barriers
std::map< int, phaseDetectorInfo > phase2DetectorMap
std::map< std::string, double > getDetectorStates() const override
retrieve all detectors used by this program
double phaseEndTimeR1
bool isDetectorActivated(int phaseNumber, int depth) const
int r2coordinatePhase
void setNewMaxGreens(std::vector< double > newMaxGreens)
double maxGreen[8]
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
void init(NLDetectorBuilder &nb) override
Initialises the tls with information about incoming lanes.
double cycleRefPoint
std::string myVehicleTypes
Whether detector output separates by vType.
std::string currentState
int activeRing2Index
std::string myFile
The output file for generated detectors.
std::string coordinates
double myDetectorLength
double yellowTime[8]
std::string combineStates(std::string state1, std::string state2)
void constructBarrierMap(int ring, std::vector< std::vector< int >> &barrierMap)
helps to construct myRingBarrierMapping
double redTime[8]
std::vector< std::vector< int > > rings
int activeRing1Index
SUMOTime myFreq
The frequency for aggregating detector output.
int r1coordinatePhase
DetectorLaneMap myDetectorLaneMap
A map from detectors to lanes.
double offset
SUMOTime trySwitch() override
Switches to the next phase.
std::vector< int > coordinatePhaseIndecies
void validate_timing()
std::string ring2
double minGreen[8]
int nextPhase(std::vector< int > ring, int phaseNum, int &distance, bool sameAllowed)
int findBarrier(int desiredPhase, int ring)
std::vector< std::string > string2vector(std::string s)
bool greenTransfer
std::string transitionState(std::string curState, int RYG)
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)
double vehExt[8]
bool whetherOutputState
bool maxRecalls[8]
double phaseExpectedDuration[8]
lightState R2RYG
std::string outputStateFilePath
double phaseEndTimeR2
double nextMaxGreen[8]
bool fixForceOff
std::tuple< int, int > getNextPhases(int currentR1Index, int currentR2Index, bool toUpdateR1, bool toUpdateR2, bool stayOk=false)
std::string ring1
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)
std::string NEMA_control()
bool wait4R1Green
void setNewOffset(double newOffset)
std::set< std::string > getLaneIDsFromNEMAState(std::string state)
bool minRecalls[8]
double myNextCycleLength
std::string currentR2State
lightState R1RYG
std::vector< int > readParaFromString(std::string s)
double phaseCutOffs[8]
std::string currentR1State
bool myShowDetectors
Whether the detectors shall be shown in the GUI.
int activeRing2Phase
detectorMap myDetectorForPhase
int string2int(std::string s)
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
void deactivateProgram() override
~NEMALogic()
Destructor.
int activeRing1Phase
bool wait4R2Green
MSPhaseDefinition myPhase
virtual phase that holds the current state
double forceOffs[8]
double phaseStartTime[8]
double myDetectorLengthLeftTurnLane
double myCycleLength
std::ofstream outputStateFile
bool isLeftTurnLane(const MSLane *const lane) const
LaneDetectorMap myLaneDetectorMap
A map from lanes to detectors.
std::vector< int > barrierPhaseIndecies
double ModeCycle(double a, double b)
void setNewCycleLength(double newCycleLength)
std::vector< std::vector< int > > myRingBarrierMapping[2]
const MSPhaseDefinition & getCurrentPhaseDef() const override
Returns the definition of the current phase.
std::map< int, std::vector< std::string > > phase2ControllerLanesMap
void setNewSplits(std::vector< double > newSplits)
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 &vTypes, 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:58
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.
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
const unsigned char flag[]
Definition: flag.cpp:24
std::string to_string(const std::vector< double > &value)