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