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