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 93 : 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 93 : const std::string& basePath) :
64 : MSSimpleTrafficLightLogic(tlcontrol, id, programID, _offset, TrafficLightType::NEMA, phases, step, delay, parameter),
65 93 : myPhase(phases[0]->duration, phases[0]->getState()) {
66 186 : myDetectorLength = StringUtils::toDouble(getParameter("detector-length", "20"));
67 186 : myDetectorLengthLeftTurnLane = StringUtils::toDouble(getParameter("detector-length-leftTurnLane", "20"));
68 651 : myCycleLength = TIME2STEPS(StringUtils::toDouble(getParameter("total-cycle-length", getParameter("cycle-length", getParameter(toString(SUMO_ATTR_CYCLETIME), "60")))));
69 93 : myNextCycleLength = myCycleLength;
70 93 : myDefaultCycleTime = myCycleLength;
71 186 : myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
72 186 : myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
73 279 : myFreq = TIME2STEPS(StringUtils::toDouble(getParameter("freq", "300")));
74 186 : myVehicleTypes = getParameter("vTypes", "");
75 186 : myControllerType = parseControllerType(getParameter("controllerType", "TS2"));
76 186 : ignoreErrors = StringUtils::toBool(getParameter("ignore-errors", "false"));
77 : // This should be extended in the future.
78 93 : myNumberRings = 2;
79 93 : }
80 :
81 195 : NEMALogic::~NEMALogic() {
82 : // delete the phase objects
83 406 : for (auto p : myPhaseObjs) {
84 341 : delete p;
85 : }
86 325 : }
87 :
88 : void
89 93 : 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 186 : std::vector<int> barrierPhases = readParaFromString(barriers);
93 93 : std::vector<int> coordinatePhases = readParaFromString(coordinates);
94 :
95 : // create a {{}, {}} vector of phases
96 372 : rings.push_back(readParaFromString(ring1));
97 186 : 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 186 : std::vector<int> VecMinRecall = readParaFromString(getParameter("minRecall", "1,2,3,4,5,6,7,8"));
117 186 : 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 230 : for (const auto& r : rings) {
139 : int ringIter = 0;
140 : lastPhaseIter = phaseIter;
141 : phaseIter = 0;
142 774 : for (const auto& p : r) {
143 637 : if (p != 0) {
144 : // find the phase definition matching the phase integer
145 : MSPhaseDefinition* tempPhase = nullptr;
146 1256 : for (const auto& pDef : myPhases) {
147 2462 : if (string2int(pDef->getName()) == p) {
148 381 : tempPhase = pDef;
149 381 : break;
150 : }
151 : }
152 : // there must be a matching MSPhaseDefinition
153 406 : if (tempPhase == nullptr) {
154 75 : 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 381 : if (myPhaseStrLen < 0) {
162 89 : myPhaseStrLen = (int)state.size();
163 292 : } 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 762 : getLaneInfoFromNEMAState(state, laneIDs, controlledStateIndexes);
171 :
172 : std::vector<std::string> laneIDs_vector;
173 1327 : for (std::string laneID : laneIDs) {
174 946 : laneIDs_vector.push_back(laneID);
175 946 : myLanePhaseMap[laneID] = p;
176 : }
177 381 : phase2ControllerLanesMap[p] = laneIDs_vector;
178 :
179 : // Create the Phase Object
180 : // find if it is at a barrier
181 381 : bool barrierPhase = vectorContainsPhase(barrierPhases, p) || vectorContainsPhase(coordinatePhases, p);
182 : // is it a coordinate phase
183 381 : bool coordinatePhase = vectorContainsPhase(coordinatePhases, p) && coordinateMode;
184 : // is there a minimum or max recall
185 381 : bool minRecall = vectorContainsPhase(VecMinRecall, p);
186 381 : 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 381 : bool phaseGreenRest = ((VecMaxRecall.size() + VecMinRecall.size()) < 1);
189 381 : if (!phaseGreenRest) {
190 381 : bool recallActive = minRecall || maxRecall;
191 381 : if (recallActive) {
192 899 : for (const auto& pO : r) {
193 875 : if (pO != p) {
194 1436 : if (vectorContainsPhase(VecMinRecall, pO)
195 718 : || 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 381 : int barrierNum = ringIter / 2;
208 :
209 : // now ready to create the phase
210 381 : myPhaseObjs.push_back(
211 381 : 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 381 : if (phaseIter > 0) {
216 224 : myPhaseObjs.back()->setSequentialPriorPhase(myPhaseObjs[lastPhaseIter + (phaseIter - 1)]);
217 : }
218 381 : phaseIter++;
219 381 : }
220 612 : ringIter++;
221 : }
222 : // Set the first to point to the last, wrapping around the ring.
223 137 : myPhaseObjs[lastPhaseIter]->setSequentialPriorPhase(myPhaseObjs[lastPhaseIter + phaseIter - 1]);
224 : // index the ring counter
225 137 : ringNum++;
226 : }
227 :
228 : //TODO: set the default phases. This could also be set using dual entry in future
229 202 : for (int i = 0; i < 2; i++) {
230 : // create the coordinate phase ptr
231 135 : coordinatePhaseObjs[i] = getPhaseObj(coordinatePhases[i], i);
232 135 : defaultBarrierPhases[i][coordinatePhaseObjs[i]->barrierNum] = coordinatePhaseObjs[i];
233 : // create the other barrier phase ptr
234 135 : PhasePtr b = getPhaseObj(barrierPhases[i], i);
235 134 : defaultBarrierPhases[i][b->barrierNum] = b;
236 : // the barrier 1 and barrier 0 default phase must not have the same barrier number
237 134 : 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 134 : IntVector latchingDetectors = readParaFromString(getParameter("latchingDetectors", ""));
247 : std::vector<std::pair<int, int>> cp;
248 421 : for (auto& p : myPhaseObjs) {
249 354 : std::string cps = "crossPhaseSwitching:";
250 1062 : int crossPhase = StringUtils::toInt(getParameter(cps.append(std::to_string(p->phaseName)), "0"));
251 354 : 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 421 : for (auto& p : myPhaseObjs) {
258 354 : bool latching = vectorContainsPhase(latchingDetectors, p->phaseName);
259 : int cpTarget = 0;
260 : int cpSource = 0;
261 370 : 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 354 : p->init(this, cpTarget, cpSource, latching);
268 : }
269 :
270 : // Calculate Force offs Based on Timing
271 67 : calculateForceOffs();
272 :
273 67 : if (coordinateMode) {
274 : // Calculate the Initial Phases in coordinated operation only.
275 : // Otherwise they have already been calculated above
276 33 : calculateInitialPhases();
277 : } else {
278 : // Fall back being the barrier 0 default phases
279 : // NEMAPhase* defaultP[2] = {defaultBarrierPhases[0][0], defaultBarrierPhases[1][0]};
280 34 : 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 67 : myPhase.setState(composeLightString());
319 201 : myPhase.setName(toString(myActivePhaseObjs[0]->phaseName) + "+" + toString(myActivePhaseObjs[1]->phaseName));
320 67 : setTrafficLightSignals(SIMSTEP);
321 67 : myStep = 0;
322 :
323 : //validating timing
324 67 : validate_timing();
325 176 : }
326 :
327 : bool
328 3888 : NEMALogic::vectorContainsPhase(std::vector<int> v, int phaseNum) {
329 3888 : if (std::find(v.begin(), v.end(), phaseNum) != v.end()) {
330 1399 : return true;
331 : }
332 : return false;
333 : }
334 :
335 : void
336 93 : NEMALogic::init(NLDetectorBuilder& nb) {
337 :
338 : // TODO: Create a parameter for this
339 93 : cycleRefPoint = TIME2STEPS(0);
340 :
341 186 : std::string barriers = getParameter("barrierPhases", "");
342 307 : std::string coordinates = getParameter("coordinatePhases", getParameter("barrier2Phases", ""));
343 214 : std::string ring1 = getParameter("ring1", "");
344 214 : std::string ring2 = getParameter("ring2", "");
345 :
346 186 : fixForceOff = StringUtils::toBool(getParameter("fixForceOff", "false"));
347 93 : offset = myOffset;
348 93 : myNextOffset = myOffset;
349 186 : whetherOutputState = StringUtils::toBool(getParameter("whetherOutputState", "false"));
350 186 : coordinateMode = StringUtils::toBool(getParameter("coordinate-mode", "false"));
351 :
352 : // set the queued traci changes to false
353 93 : queuedTraciChanges = false;
354 :
355 : //missing parameter error
356 279 : error_handle_not_set(ring1, "ring1");
357 279 : error_handle_not_set(ring2, "ring2");
358 279 : error_handle_not_set(barriers, "barrierPhases");
359 279 : 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 93 : constructTimingAndPhaseDefs(barriers, coordinates, ring1, ring2);
382 :
383 : //init the traffic light
384 66 : 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 938 : for (const LaneVector& lanes : myLanes) {
388 1753 : for (MSLane* const lane : lanes) {
389 : //decide the detector length
390 : double detector_length = 0;
391 881 : if (isLeftTurnLane(lane)) {
392 129 : detector_length = myDetectorLengthLeftTurnLane;
393 : } else {
394 752 : detector_length = myDetectorLength;
395 : }
396 881 : if (noVehicles(lane->getPermissions())) {
397 : // do not build detectors on green verges or sidewalks
398 16 : continue;
399 : }
400 : // Build detector and register them in the detector control
401 865 : if (myLaneDetectorMap.find(lane) == myLaneDetectorMap.end()) {
402 538 : MSE2Collector* det = nullptr;
403 1104 : const std::string customID = getParameter(lane->getID());
404 538 : 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 471 : int phaseNumber = 0;
413 471 : if (myLanePhaseMap.find(lane->getID()) != myLanePhaseMap.end()) {
414 469 : phaseNumber = myLanePhaseMap.find(lane->getID())->second;
415 : }
416 471 : int index = lane->getIndex();
417 1884 : std::string id = myID + "_" + myProgramID + "_D" + toString(phaseNumber) + "." + toString(index);
418 517 : while (MSNet::getInstance()->getDetectorControl().getTypedDetectors(SUMO_TAG_LANE_AREA_DETECTOR).get(id) != nullptr) {
419 23 : index++;
420 92 : 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 471 : 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 471 : 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 471 : 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 471 : 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 942 : 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 537 : myLaneDetectorMap[lane] = det;
449 537 : myDetectorLaneMap[det] = lane;
450 1075 : myDetectorInfoVector.push_back(DetectorInfo(det, (int)myPhases.size()));
451 :
452 : }
453 : }
454 : }
455 374 : for (auto item : phase2ControllerLanesMap) {
456 309 : int NEMAPhaseIndex = item.first;
457 309 : std::vector<std::string> laneIDs = item.second;
458 : std::vector<MSE2Collector*> detectors;
459 309 : MSE2Collector* detector = nullptr;
460 1064 : for (std::string laneID : laneIDs) {
461 755 : MSLane* lane = MSLane::dictionary(laneID);
462 755 : detector = myLaneDetectorMap[lane];
463 755 : 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 927 : for (int i = 0; i < 2; i++) {
468 618 : if (vectorContainsPhase(rings[i], NEMAPhaseIndex)) {
469 682 : getPhaseObj(NEMAPhaseIndex, i)->setDetectors(detectors);
470 : }
471 : }
472 309 : }
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 65 : const int numLinks = (int)myLinks.size();
481 65 : std::vector<bool> neverMajor(numLinks, true);
482 379 : for (const MSPhaseDefinition* phase : myPhases) {
483 : const std::string& state = phase->getState();
484 4572 : for (int i = 0; i < numLinks; i++) {
485 4258 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
486 : neverMajor[i] = false;
487 : }
488 : }
489 : }
490 65 : std::vector<bool> oneLane(numLinks, false);
491 937 : for (int i = 0; i < numLinks; i++) {
492 1721 : for (MSLane* lane : getLanesAt(i)) {
493 : int numMotorized = 0;
494 3342 : for (MSLane* l : lane->getEdge().getLanes()) {
495 2462 : if ((l->getPermissions() & motorized) != 0) {
496 2068 : numMotorized++;
497 : }
498 : }
499 880 : if (numMotorized == 1) {
500 : oneLane[i] = true;
501 31 : break;
502 : }
503 : }
504 : }
505 :
506 379 : for (const MSPhaseDefinition* phase : myPhases) {
507 314 : 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 4487 : for (int i = 0; i < numLinks; i++) {
515 4178 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR
516 4178 : || (state[i] == LINKSTATE_TL_GREEN_MINOR
517 238 : && ((neverMajor[i] // check1a
518 101 : && hasMajor(state, getLanesAt(i))) // check1b
519 142 : || oneLane[i])) // check1c
520 : ) {
521 : greenLinks.insert(i);
522 : actuatedLinks.insert(i);
523 : }
524 :
525 8388 : for (MSLane* lane : getLanesAt(i)) {
526 : if (myLaneDetectorMap.count(lane) != 0) {
527 4114 : detectorLinks[myLaneDetectorMap[lane]].insert(i);
528 : }
529 : }
530 : }
531 2858 : for (auto& item : detectorLinks) {
532 2549 : MSE2Collector* det = item.first;
533 2549 : MSLane* detectorLane = myDetectorLaneMap[det];
534 : bool usable = true;
535 : // check 1
536 6647 : for (int j : item.second) {
537 : if (greenLinks.count(j) == 0) {
538 : usable = false;
539 : }
540 : }
541 :
542 : //check 2
543 2549 : if (usable) {
544 1359 : for (MSLink* link : detectorLane->getLinkCont()) {
545 835 : 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 524 : if (usable) {
559 524 : detectors.insert(item.first);
560 1355 : for (int j : item.second) {
561 831 : linkToDetectors[j].insert(item.first);
562 : }
563 : }
564 : }
565 309 : 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 314 : myDetectorForPhase.push_back(detectorInfos);
571 838 : for (MSE2Collector* det : detectors) {
572 4935 : for (DetectorInfo& detInfo : myDetectorInfoVector) {
573 4411 : if (detInfo.det == det) {
574 524 : myDetectorForPhase.back().push_back(&detInfo);
575 : detInfo.servedPhase[phaseIndex] = true;
576 : }
577 : }
578 : }
579 314 : }
580 :
581 917 : for (int i : actuatedLinks) {
582 881 : if (linkToDetectors[i].size() == 0 && myLinks[i].size() > 0
583 881 : && (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 65 : }
588 :
589 : void
590 67 : NEMALogic::validate_timing() {
591 : // check that the cycle length for each ring adds up to the specified cycle length
592 199 : for (int ringIndex = 0; ringIndex < 2; ringIndex++) {
593 : SUMOTime cycleLengthCalculated = 0;
594 485 : for (auto& p : getPhasesByRing(ringIndex)) {
595 352 : cycleLengthCalculated += (p->maxDuration + p->yellow + p->red);
596 133 : }
597 133 : if (coordinateMode && (cycleLengthCalculated != myCycleLength)) {
598 43 : int ringNumber = ringIndex + 1;
599 129 : const std::string error = "At NEMA tlLogic '" + getID() + "', Ring " + toString(ringNumber) + " does not add to cycle length.";
600 43 : if (ignoreErrors) {
601 127 : WRITE_WARNING(error);
602 : } else {
603 2 : throw ProcessError(error);
604 : }
605 : }
606 : }
607 : // check that the barriers sum together
608 66 : SUMOTime cycleLengths[2][2] = { {0, 0}, {0, 0} };
609 198 : 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 481 : for (const auto p : getPhasesByRing(ringIndex)) {
612 349 : cycleLengths[ringIndex][p->barrierNum] += p->maxDuration + p->yellow + p->red;
613 132 : }
614 : }
615 : // Write warnings if the barriers do not sum
616 198 : for (int barrierNum = 0; barrierNum < 2; barrierNum++) {
617 132 : if (cycleLengths[0][barrierNum] != cycleLengths[1][barrierNum]) {
618 42 : 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 66 : if (!coordinateMode && offset != 0) {
630 0 : WRITE_WARNINGF(TL("NEMA tlLogic '%' is not coordinated but an offset was set."), getID());
631 : }
632 66 : }
633 :
634 : void
635 8 : NEMALogic::setNewSplits(std::vector<double> newSplits) {
636 : assert(newSplits.size() == 8);
637 48 : for (auto& p : myPhaseObjs) {
638 40 : if (newSplits[p->phaseName - 1] > 0) {
639 : // set the phase's nextMaxDuration. This will be implemented when implementTraciChanges is called
640 40 : p->nextMaxDuration = TIME2STEPS(newSplits[p->phaseName - 1]) - p->yellow - p->red;
641 : }
642 : }
643 8 : }
644 :
645 :
646 : void
647 4 : NEMALogic::setNewMaxGreens(std::vector<double> newMaxGreens) {
648 24 : for (auto& p : myPhaseObjs) {
649 20 : if (newMaxGreens[p->phaseName - 1] > 0) {
650 : // set the phase's nextMaxDuration. This will be implemented when implementTraciChanges is called
651 20 : p->nextMaxDuration = TIME2STEPS(newMaxGreens[p->phaseName - 1]);
652 : }
653 : }
654 4 : }
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 40 : NEMALogic::setNewOffset(double newOffset) {
666 : // set the controller's offset. This will be implemented when implementTraciChanges is called
667 40 : myNextOffset = TIME2STEPS(newOffset);
668 40 : }
669 :
670 :
671 625 : std::vector<int> NEMALogic::readParaFromString(std::string s) {
672 : std::vector<int> output;
673 3746 : for (char c : s) {
674 3121 : if (c >= '0' && c <= '9') {
675 1794 : int temp = c - '0';
676 1794 : output.push_back(temp);
677 : }
678 : }
679 625 : return output;
680 0 : }
681 :
682 : const MSPhaseDefinition&
683 124863 : NEMALogic::getCurrentPhaseDef() const {
684 124863 : return myPhase;
685 : }
686 :
687 1022 : int NEMALogic::measureRingDistance(int p1, int p2, int ringNum) {
688 1022 : 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 5594 : for (int i = 0; i < (length * 2); i++) {
693 5594 : if (rings[ringNum][i % length] > 0) {
694 4232 : if (found) {
695 2116 : d++;
696 2116 : if (rings[ringNum][i % length] == p2) {
697 : break;
698 : }
699 2116 : } else if (rings[ringNum][i % length] == p1) {
700 : found = true;
701 : }
702 : }
703 : }
704 : assert(d > 0);
705 1022 : return d;
706 : }
707 :
708 :
709 : SUMOTime
710 14534 : NEMALogic::ModeCycle(SUMOTime a, SUMOTime b) {
711 14534 : SUMOTime c = a - b;
712 14613 : while (c >= b) {
713 79 : c = c - b;
714 : }
715 36157 : while (c < 0) {
716 21623 : c += b;
717 : }
718 14534 : return c;
719 : }
720 :
721 :
722 : void
723 381 : NEMALogic::getLaneInfoFromNEMAState(std::string state, StringVector& laneIDs, IntVector& stateIndex) {
724 : std::set<std::string> output;
725 5731 : for (int i = 0; i < (int)myLinks.size(); i++) {
726 5350 : if (myLinks[i].empty()) {
727 : // unused index
728 5 : continue;
729 : }
730 5345 : char ch = state[i];
731 : // if the ch is 'G', it means that the phase is controlling this lane
732 5345 : if (ch == 'G') {
733 950 : stateIndex.push_back(i);
734 1908 : for (auto link : myLinks[i]) {
735 : const MSLane* incoming = link->getLaneBefore();
736 958 : if (incoming->isNormal()) {
737 946 : laneIDs.push_back(incoming->getID());
738 : }
739 : }
740 : }
741 : }
742 381 : }
743 :
744 881 : bool NEMALogic::isLeftTurnLane(const MSLane* const lane) const {
745 881 : const std::vector<MSLink*> links = lane->getLinkCont();
746 881 : if (links.size() == 1 && links.front()->getDirection() == LinkDirection::LEFT) {
747 129 : return true;
748 : }
749 : return false;
750 881 : }
751 :
752 : bool
753 101 : NEMALogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
754 804 : for (int i = 0; i < (int)state.size(); i++) {
755 799 : if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
756 244 : for (MSLane* cand : getLanesAt(i)) {
757 248 : for (MSLane* lane : lanes) {
758 172 : if (lane == cand) {
759 : return true;
760 : }
761 : }
762 : }
763 : }
764 : }
765 : return false;
766 : }
767 :
768 :
769 : void
770 93 : NEMALogic::activateProgram() {
771 93 : MSTrafficLightLogic::activateProgram();
772 93 : for (auto& item : myLaneDetectorMap) {
773 0 : item.second->setVisible(true);
774 : }
775 93 : }
776 :
777 : void
778 0 : NEMALogic::deactivateProgram() {
779 0 : MSTrafficLightLogic::deactivateProgram();
780 0 : for (auto& item : myLaneDetectorMap) {
781 0 : item.second->setVisible(false);
782 : }
783 0 : }
784 :
785 : void
786 0 : NEMALogic::setShowDetectors(bool show) {
787 0 : myShowDetectors = show;
788 0 : for (auto& item : myLaneDetectorMap) {
789 0 : item.second->setVisible(myShowDetectors);
790 : }
791 0 : }
792 :
793 1231 : int NEMALogic::string2int(std::string s) {
794 1231 : std::stringstream ss(s);
795 1231 : int ret = 0;
796 1231 : ss >> ret;
797 1231 : return ret;
798 1231 : }
799 :
800 :
801 : const std::string
802 4484 : NEMALogic::getParameter(const std::string& key, const std::string defaultValue) const {
803 8968 : if (StringUtils::startsWith(key, "NEMA.")) {
804 1200 : if (key == "NEMA.phaseCall") {
805 1200 : int activeCalls[8] = { 0 };
806 7200 : for (const auto p : myPhaseObjs) {
807 : // This handles the case when the controller has multiple of the same phase call
808 6000 : if (!activeCalls[p->phaseName - 1]) {
809 4912 : activeCalls[p->phaseName - 1] = 1 * p->lastDetectActive;
810 : }
811 : }
812 1200 : std::string outStr = "";
813 10800 : for (int i = 0; i < 8; i++) {
814 9600 : outStr += std::to_string(activeCalls[i]);
815 9600 : if (i < 7) {
816 : outStr += ",";
817 : }
818 : }
819 1200 : return outStr;
820 : } else {
821 0 : throw InvalidArgument("Unsupported parameter '" + key + "' for NEMA controller '" + getID() + "'");
822 : }
823 : } else {
824 6568 : return Parameterised::getParameter(key, defaultValue);
825 : }
826 : }
827 :
828 :
829 : void
830 52 : NEMALogic::setParameter(const std::string& key, const std::string& value) {
831 52 : queuedTraciChanges = true;
832 104 : if (StringUtils::startsWith(key, "NEMA.")) {
833 52 : if (key == "NEMA.splits" || key == "NEMA.maxGreens") {
834 : //splits="2.0 3.0 4.0 5.0 2.0 3.0 4.0 5.0"
835 24 : const std::vector<std::string>& tmp = StringTokenizer(value).getVector();
836 12 : if (tmp.size() != 8) {
837 0 : queuedTraciChanges = false;
838 0 : throw InvalidArgument("Parameter '" + key + "' for NEMA controller '" + getID() + "' requires 8 space or comma separated values");
839 : }
840 : std::vector<double> timing;
841 108 : for (const std::string& s : tmp) {
842 96 : timing.push_back(StringUtils::toDouble(s));
843 : }
844 12 : if (key == "NEMA.maxGreens") {
845 4 : setNewMaxGreens(timing);
846 : } else {
847 8 : setNewSplits(timing);
848 : }
849 52 : } else if (key == "NEMA.cycleLength") {
850 0 : setNewCycleLength(StringUtils::toDouble(value));
851 40 : } else if (key == "NEMA.offset") {
852 40 : setNewOffset(StringUtils::toDouble(value));
853 : } else {
854 0 : queuedTraciChanges = false;
855 0 : throw InvalidArgument("Unsupported parameter '" + key + "' for NEMA controller '" + getID() + "'");
856 : }
857 : }
858 52 : Parameterised::setParameter(key, value);
859 52 : }
860 :
861 : void
862 372 : NEMALogic::error_handle_not_set(std::string param_variable, std::string param_name) {
863 372 : if (param_variable == "") {
864 0 : throw InvalidArgument("Please set " + param_name + " for NEMA tlLogic '" + getID() + "'");
865 : }
866 372 : }
867 :
868 : void
869 91 : NEMALogic::calculateForceOffs170() {
870 91 : SUMOTime zeroTime[2] = { TIME2STEPS(0), TIME2STEPS(0) };
871 273 : for (int i = 0; i < 2; i++) {
872 : SUMOTime runningTime = 0;
873 : // loop through the phases for ring 0 and then 1
874 656 : for (auto& p : getPhasesByRing(i)) {
875 474 : runningTime += p->maxDuration + p->getTransitionTimeStateless();
876 : // in 170, the cycle "starts" when the coordinated phase goes to yellow.
877 : // See https://ops.fhwa.dot.gov/publications/fhwahop08024/chapter6.html
878 474 : if (p->coordinatePhase) {
879 114 : zeroTime[i] = runningTime;
880 : }
881 474 : p->forceOffTime = runningTime - p->getTransitionTimeStateless();
882 474 : p->greatestStartTime = p->forceOffTime - p->minDuration;
883 182 : }
884 : }
885 : // find the minimum offset time and then subtract from everything, modecycling where negative
886 : // This sets the 0 cycle time as start of yellow on earliest ending coordinated phase
887 91 : SUMOTime minCoordYellow = MIN2(zeroTime[0], zeroTime[1]);
888 565 : for (auto& p : myPhaseObjs) {
889 474 : p->forceOffTime = ModeCycle(p->forceOffTime - minCoordYellow, myCycleLength);
890 474 : p->greatestStartTime = ModeCycle(p->greatestStartTime - minCoordYellow, myCycleLength);
891 : }
892 :
893 : #ifdef DEBUG_NEMA
894 : std::ios_base::fmtflags oldflags = std::cout.flags();
895 : std::streamsize oldprecision = std::cout.precision();
896 : for (int i = 0; i < 2; i++) {
897 : std::cout << "Ring" << i + 1 << " force offs: \t";
898 : for (auto& p : rings[i]) {
899 : if (p > 0) {
900 : PhasePtr pObj = getPhaseObj(p, i);
901 : std::cout << std::fixed << std::setprecision(2) << STEPS2TIME(pObj->forceOffTime) << "\t";
902 : } else {
903 : std::cout << std::to_string(0) << "\t";
904 : }
905 : }
906 : std::cout << std::endl;
907 : }
908 : std::cout.flags(oldflags);
909 : std::cout.precision(oldprecision);
910 : #endif
911 91 : }
912 :
913 : void
914 72 : NEMALogic::calculateForceOffsTS2() {
915 : // TS2 "0" cycle time is the start of the "first" coordinated phases.
916 : // We can find this "0" point by first constructing the forceOffs in sequential order via the 170 method
917 72 : calculateForceOffs170();
918 :
919 : // Switch the Force Off Times to align with TS2 Cycle, which is the *start* of the earliest coordinated phase
920 : // The coordinate phases will always be the defaultBarrierPhases[i][0]
921 72 : SUMOTime minCoordTime = MIN2(coordinatePhaseObjs[0]->forceOffTime - coordinatePhaseObjs[0]->maxDuration,
922 72 : coordinatePhaseObjs[1]->forceOffTime - coordinatePhaseObjs[1]->maxDuration);
923 :
924 : // loop through all the phases and subtract this minCoordTime to move the 0 point to the start of the first coordinated phase
925 424 : for (auto& p : myPhaseObjs) {
926 352 : if ((p->forceOffTime - minCoordTime) >= 0) {
927 245 : p->forceOffTime -= (minCoordTime);
928 : } else {
929 107 : p->forceOffTime = (myCycleLength + (p->forceOffTime - (minCoordTime)));
930 : }
931 352 : p->greatestStartTime = ModeCycle(p->greatestStartTime - minCoordTime, myCycleLength);
932 : }
933 72 : }
934 :
935 : void
936 33 : NEMALogic::calculateInitialPhases170() {
937 : // get the time in the cycle
938 33 : SUMOTime cycleTime = ModeCycle(getTimeInCycle(), myCycleLength);
939 : NEMAPhase* activePhases[2];
940 99 : for (int i = 0; i < 2; i++) {
941 66 : std::vector<NEMAPhase*> ringCopy = getPhasesByRing(i);
942 : // sort by the minimum start time in the cycle.
943 66 : std::sort(ringCopy.begin(), ringCopy.end(),
944 : [](NEMAPhase * p, NEMAPhase * p1) {
945 202 : return p->greatestStartTime <= p1->greatestStartTime;
946 : }
947 : );
948 : bool found = false;
949 : // loop through the sorted phases by time and try to find the phase that should be active given the time in the cycle
950 216 : for (auto& p : ringCopy) {
951 : // This handles the wrap around. Checks if the prior phases start time should have already happened.
952 : // If it should have happened and it's not to the start time of me yet, start in my phase ( will have to be in my phase longer than max time likely )
953 168 : SUMOTime syntheticPriorStart = p->getSequentialPriorPhase()->greatestStartTime < p->greatestStartTime ?
954 66 : p->getSequentialPriorPhase()->greatestStartTime : p->getSequentialPriorPhase()->greatestStartTime - myCycleLength;
955 168 : if (cycleTime <= ModeCycle(p->greatestStartTime, myCycleLength) && cycleTime > ModeCycle(syntheticPriorStart, myCycleLength)) {
956 : found = true;
957 18 : activePhases[i] = p;
958 : break;
959 : }
960 : }
961 : if (!found) {
962 : #ifdef DEBUG_NEMA
963 : const std::string error = "I can't find the correct phase for NEMA tlLogic '" + getID() + "' Ring " + toString(i) + " to start in.";
964 : WRITE_WARNING(error);
965 : WRITE_WARNING(TL("I am starting in the coordinated phases"));
966 : #endif
967 48 : activePhases[0] = defaultBarrierPhases[0][0];
968 48 : activePhases[1] = defaultBarrierPhases[1][0];
969 : }
970 66 : }
971 :
972 : // ensure that the two found phases are on the same side of the barrier. If they aren't, just override with the default barrier phases
973 33 : if (activePhases[0]->barrierNum != activePhases[1]->barrierNum) {
974 : // give preference to whatever is on the coordinate side of the barrier, one must be if they aren't equal to each other
975 2 : activePhases[0] = activePhases[0]->barrierNum == 0 ? activePhases[0] : defaultBarrierPhases[0][0];
976 2 : activePhases[1] = activePhases[1]->barrierNum == 0 ? activePhases[1] : defaultBarrierPhases[1][0];
977 : }
978 :
979 : // force enter the phases and update their expected duration to last until the forceOff
980 33 : activePhases[0]->forceEnter(this);
981 33 : activePhases[1]->forceEnter(this);
982 33 : }
983 :
984 : void
985 22 : NEMALogic::calculateInitialPhasesTS2() {
986 : // Modifications where made to 170 algorithm so that it works with both.
987 22 : calculateInitialPhases170();
988 22 : }
989 :
990 : NEMALogic::controllerType
991 93 : NEMALogic::parseControllerType(std::string inputType) {
992 : std::string cleanString;
993 448 : for (const char& c : inputType) {
994 355 : if (isalpha(c) || isdigit(c)) {
995 355 : cleanString += (char)::tolower(c);
996 : }
997 : }
998 93 : if (cleanString == "type170") {
999 : return Type170;
1000 74 : } else if (cleanString == "ts2") {
1001 : return TS2;
1002 : } else {
1003 0 : throw InvalidArgument("Please set controllerType for NEMA tlLogic " + myID + " to either Type170 or TS2");
1004 : }
1005 : }
1006 :
1007 : std::vector<NEMAPhase*>
1008 1600 : NEMALogic::getPhasesByRing(int ringNum) {
1009 : std::vector<NEMAPhase*> phases;
1010 10454 : for (auto& p : myPhaseObjs) {
1011 8854 : if (p->ringNum == ringNum) {
1012 4458 : phases.push_back(p);
1013 : }
1014 : }
1015 1600 : return phases;
1016 0 : }
1017 :
1018 : void
1019 6249 : NEMALogic::setActivePhase(PhasePtr phase) {
1020 6249 : myActivePhaseObjs[phase->ringNum] = phase;
1021 6249 : }
1022 :
1023 : std::map<std::string, double>
1024 3761 : NEMALogic::getDetectorStates() const {
1025 : std::map<std::string, double> result;
1026 37610 : for (auto item : myDetectorLaneMap) {
1027 33849 : result[item.first->getID()] = item.first->getCurrentVehicleNumber();
1028 : }
1029 3761 : return result;
1030 : }
1031 :
1032 : NEMALogic::PhasePtr
1033 298509 : NEMALogic::getOtherPhase(PhasePtr p) {
1034 : // return a pointer to the other active phase
1035 298509 : return myActivePhaseObjs[!p->ringNum];
1036 : }
1037 :
1038 : NEMAPhase*
1039 619 : NEMALogic::getPhaseObj(int phaseNum, int ringNum) {
1040 : // This satisfies the case where there is a "duplicate" phase on both ring
1041 619 : std::vector<PhasePtr> iterRing = ringNum >= 0 ? getPhasesByRing(ringNum) : myPhaseObjs;
1042 1209 : for (auto& p : iterRing) {
1043 1208 : if (p->phaseName == phaseNum) {
1044 618 : return p;
1045 : }
1046 : }
1047 : // the phase must always be found
1048 4 : throw ProcessError("At traffic signal '" + myID + "' program '" + myProgramID + "' phase '" + toString(phaseNum) + "' not found in ring '" + toString(ringNum) + "'.");
1049 619 : }
1050 :
1051 : PhaseTransitionLogic*
1052 176 : NEMALogic::getDefaultTransition(PhaseTransitionLogic* t, PhaseTransitionLogic* ot) {
1053 : NEMAPhase* p = t->getFromPhase();
1054 : // if the current phase is not ready to switch or a barrier cross is desired by the other transition
1055 : // and t fromPhase is not ready to switch, the default transition is back to myself
1056 176 : if (!p->readyToSwitch ||
1057 176 : (p->barrierNum == ot->getToPhase()->barrierNum && p->getCurrentState() >= LightState::Green)) {
1058 88 : return p->getTransition(p->phaseName);
1059 : }
1060 : // otherwise the default transition is to the default phase on whatever barrier ot wants to transition to
1061 : else {
1062 88 : return p->getTransition(defaultBarrierPhases[p->ringNum][ot->getToPhase()->barrierNum]->phaseName);
1063 : }
1064 : }
1065 :
1066 : void
1067 34343 : NEMALogic::getNextPhases(TransitionPairs& transitions) {
1068 : std::vector<std::vector<PhaseTransitionLogic* >> potentialPhases;
1069 :
1070 : // Get a vector of each phases' potential transitions
1071 103029 : for (const auto& p : myActivePhaseObjs) {
1072 137372 : potentialPhases.push_back(p->trySwitch(this));
1073 : }
1074 :
1075 : // Loop through all combination of transitions, keeping only the valid ones and filling in the gaps where necessary
1076 68686 : for (const auto& r1_t : potentialPhases[0]) {
1077 68686 : for (const auto& r2_t : potentialPhases[1]) {
1078 : // if both transitions go to the same barrier then we are good
1079 34343 : if (r1_t->getToPhase()->barrierNum == r2_t->getToPhase()->barrierNum) {
1080 34255 : transitions.push_back({ r1_t, r2_t, (float)(r1_t->getDistance(r2_t) + r2_t->getDistance(r1_t)) / 2 });
1081 : } else {
1082 : // If the rings are different, add a choice where one of them is the default choice for whatever ring it is
1083 : // create two choices, one for each of the phase as they are on different rings
1084 88 : if (r1_t->getFromPhase()->readyToSwitch) {
1085 : // get the r2 default
1086 88 : PhaseTransitionLogic* r2_t_temp = getDefaultTransition(r2_t, r1_t);
1087 : // only add it if it does not cross a barrier!
1088 88 : if (r2_t_temp->getToPhase()->barrierNum == r1_t->getToPhase()->barrierNum) {
1089 88 : transitions.push_back({ r1_t, r2_t_temp, (float)(r2_t_temp->getDistance(r1_t) + r1_t->getDistance(r2_t_temp)) / 2 });
1090 : }
1091 : }
1092 88 : if (r2_t->getFromPhase()->readyToSwitch) {
1093 : // R1 default
1094 88 : PhaseTransitionLogic* r1_t_temp = getDefaultTransition(r1_t, r2_t);
1095 : // only add it if it does not cross a barrier!
1096 88 : if (r1_t_temp->getToPhase()->barrierNum == r2_t->getToPhase()->barrierNum) {
1097 88 : transitions.push_back({ r1_t_temp, r2_t, (float)(r2_t->getDistance(r1_t_temp) + r1_t_temp->getDistance(r2_t)) / 2 });
1098 : }
1099 : }
1100 : // If the distances are <= 1, this means that this is the shortest transition possible
1101 : // and we can return early without considering any other options
1102 88 : if (!transitions.empty()) {
1103 88 : if (transitions.back().distance < 1) {
1104 : return;
1105 : }
1106 : }
1107 : }
1108 : }
1109 : }
1110 34343 : }
1111 :
1112 :
1113 : std::string
1114 34410 : NEMALogic::composeLightString() {
1115 : // FIX with plan to support #10742
1116 34410 : std::string out(myPhaseStrLen, 'r');
1117 469821 : for (int i = 0; i < myPhaseStrLen; i++) {
1118 : bool controlled = false;
1119 435411 : std::string phaseChars = "";
1120 1306233 : for (auto& p : myActivePhaseObjs) {
1121 870822 : phaseChars += p->getNEMAChar(i);
1122 1741644 : if (p->controlledIndex(i)) {
1123 157758 : out[i] = p->getNEMAChar(i);
1124 : controlled = true;
1125 : }
1126 : }
1127 : // if the index wasn't a controlled one, the prior priority order still stands
1128 435411 : if (!controlled) {
1129 1760980 : for (auto priority_char : lightHeadPriority) {
1130 1556377 : if (std::count(phaseChars.begin(), phaseChars.end(), priority_char)) {
1131 85357 : out[i] = priority_char;
1132 85357 : break;
1133 : }
1134 : }
1135 : }
1136 : }
1137 34410 : return out;
1138 : }
1139 :
1140 :
1141 : SUMOTime
1142 87012 : NEMALogic::trySwitch() {
1143 : #ifdef DEBUG_NEMA_SWITCH
1144 : std::cout << SIMTIME << " trySwitch tls=" << getID() << "\n";
1145 : #endif
1146 87012 : PhaseTransitionLogic* nextPhases[2] = { nullptr, nullptr };
1147 :
1148 : // update the internal time. This is a must. Could have just used a reference to the time
1149 : setCurrentTime();
1150 :
1151 : // Check the Detectors
1152 583542 : for (auto& p : myPhaseObjs) {
1153 496530 : p->checkMyDetectors();
1154 : }
1155 :
1156 : // Update the timing parameters
1157 261036 : for (const auto& p : myActivePhaseObjs) {
1158 174024 : p->update(this);
1159 : }
1160 :
1161 : // Calculate the Next Phases, but only if atleast one of them is ready to transition
1162 87012 : if (myActivePhaseObjs[0]->readyToSwitch || myActivePhaseObjs[1]->readyToSwitch) {
1163 : TransitionPairs transitions;
1164 : // set the next phases by reference
1165 34343 : getNextPhases(transitions);
1166 :
1167 : // Sort the next phases by distance and select the closest.
1168 : // TODO: Is there a way to avoid this sort? The transitions are already sorted by distance prior
1169 : // to picking the valid ones
1170 34343 : if (transitions.size() > 1) {
1171 88 : std::sort(transitions.begin(), transitions.end(),
1172 : [](const transitionInfo & i, const transitionInfo & j) {
1173 160 : return i.distance < j.distance;
1174 : });
1175 : }
1176 :
1177 : // Set the Next Phases = to the transition with least combined distance
1178 34343 : nextPhases[0] = transitions.front().p1;
1179 34343 : nextPhases[1] = transitions.front().p2;
1180 :
1181 : // Try the exit logic. This doesn't necessarily mean that the phase will exit,
1182 : // as it could go into green rest or green transfer, but this is considered an "exit"
1183 103029 : for (const auto& p : myActivePhaseObjs) {
1184 68686 : if (p->readyToSwitch) {
1185 65349 : p->exit(this, nextPhases);
1186 : }
1187 : }
1188 :
1189 : // This is the only time when something might have happened, so we update the phase strings here
1190 34343 : std::string newState = composeLightString();
1191 34343 : if (newState != myPhase.getState()) {
1192 : myPhase.setState(newState);
1193 28635 : myPhase.setName(toString(myActivePhaseObjs[0]->phaseName) + "+" + toString(myActivePhaseObjs[1]->phaseName));
1194 : // ensure that SwitchCommand::execute notices a change
1195 9545 : myStep = 1 - myStep;
1196 :
1197 : }
1198 34343 : }
1199 :
1200 : // clear the phases' detectors
1201 583542 : for (auto& p : myPhaseObjs) {
1202 496530 : p->clearMyDetectors();
1203 : }
1204 :
1205 :
1206 : #ifdef FUZZ_TESTING
1207 : // Basic Assertion to ensure that the Barrier is not crossed
1208 : assert(myActivePhaseObjs[0]->barrierNum == myActivePhaseObjs[1]->barrierNum);
1209 : #endif
1210 :
1211 : // return the simulation timestep, as this controller must be checked every simulation step
1212 87012 : return DELTA_T;
1213 : }
1214 :
1215 :
1216 : void
1217 3048 : NEMALogic::implementTraciChanges(void) {
1218 : // Implement Traci Updates on the start of ring1 coordinated phase (rising edge of it turning green)
1219 3048 : if (queuedTraciChanges) {
1220 144 : for (auto& p : myPhaseObjs) {
1221 120 : p->maxDuration = p->nextMaxDuration;
1222 : }
1223 24 : offset = myNextOffset;
1224 24 : myCycleLength = myNextCycleLength;
1225 : // now that we have set the cycle length, offset and max duration, we need to update force off times
1226 24 : calculateForceOffs();
1227 24 : queuedTraciChanges = false;
1228 : }
1229 3048 : }
1230 :
1231 :
1232 : // ===========================================================================
1233 : // NEMAPhase Definitions
1234 : // ===========================================================================
1235 381 : NEMAPhase::NEMAPhase(int phaseName, bool isBarrier, bool isGreenRest, bool isCoordinated,
1236 : bool minRecall, bool maxRecall, bool fixForceOff, int barrierNum, int ringNum,
1237 : IntVector phaseStringInds,
1238 381 : MSPhaseDefinition* phase) :
1239 381 : phaseName(phaseName),
1240 381 : isAtBarrier(isBarrier),
1241 381 : isGreenRest(isGreenRest),
1242 381 : barrierNum(barrierNum),
1243 381 : coordinatePhase(isCoordinated),
1244 381 : minRecall(minRecall),
1245 381 : maxRecall(maxRecall),
1246 381 : fixForceOff(fixForceOff),
1247 381 : ringNum(ringNum),
1248 381 : myCorePhase(phase),
1249 381 : myPhaseStringInds(phaseStringInds) {
1250 : // Public
1251 381 : readyToSwitch = false;
1252 381 : greenRestTimer = 0;
1253 381 : forceOffTime = 0;
1254 381 : lastDetectActive = false;
1255 :
1256 : // Private
1257 381 : myInstance = this;
1258 381 : myLastPhaseInstance = nullptr;
1259 381 : sequentialPriorPhase = nullptr;
1260 381 : myLightState = LightState::Red;
1261 381 : transitionActive = false;
1262 :
1263 : // Timing Parameters
1264 381 : maxGreenDynamic = myCorePhase->maxDuration;
1265 381 : myStartTime = TIME2STEPS(0.);
1266 381 : myExpectedDuration = myCorePhase->minDuration;
1267 381 : myLastEnd = TIME2STEPS(0.);
1268 :
1269 : // set the phase colors
1270 381 : setMyNEMAStates();
1271 381 : }
1272 :
1273 341 : NEMAPhase::~NEMAPhase() {
1274 : // Delete the transitions from their alloc
1275 1318 : for (auto t : myTransitions) {
1276 977 : delete t;
1277 : }
1278 341 : }
1279 :
1280 : void
1281 354 : NEMAPhase::init(NEMALogic* controller, int crossPhaseTarget, int crossPhaseSource, bool latching) {
1282 : // switch the durations from steps2time
1283 354 : recalculateTiming();
1284 :
1285 1376 : for (auto p : controller->getPhasesByRing(ringNum)) {
1286 : // construct transitions for all potential movements, including back to myself
1287 1022 : myTransitions.push_back(new PhaseTransitionLogic(this, p));
1288 1022 : myTransitions.back()->setDistance(controller->measureRingDistance(phaseName, p->phaseName, ringNum));
1289 354 : }
1290 :
1291 : // sort the transitions by distance for speed later. Using plain distance here
1292 354 : std::sort(myTransitions.begin(), myTransitions.end(), [&](const PhaseTransitionLogic * i, const PhaseTransitionLogic * j) {
1293 1258 : return i->distance < j->distance;
1294 : });
1295 :
1296 : // create the phase detector info
1297 358 : myDetectorInfo = PhaseDetectorInfo(latching,
1298 4 : crossPhaseSource > 0 ? controller->getPhaseObj(crossPhaseSource) : nullptr,
1299 4 : crossPhaseTarget > 0 ? controller->getPhaseObj(crossPhaseTarget) : nullptr
1300 : );
1301 354 : }
1302 :
1303 : void
1304 354 : NEMAPhase::recalculateTiming(void) {
1305 : // This could be extended in the future to allow for traci manipulation
1306 354 : yellow = myCorePhase->yellow;
1307 354 : red = myCorePhase->red;
1308 354 : minDuration = myCorePhase->minDuration;
1309 354 : maxDuration = myCorePhase->maxDuration;
1310 354 : nextMaxDuration = myCorePhase->maxDuration;
1311 354 : maxGreenDynamic = myCorePhase->maxDuration;
1312 354 : vehExt = myCorePhase->vehext;
1313 354 : }
1314 :
1315 :
1316 : // TODO: this can be computed once.
1317 : char
1318 1028580 : NEMAPhase::getNEMAChar(int i) {
1319 1028580 : if (myLightState >= LightState::Green) {
1320 534420 : return myGreenString[i];
1321 494160 : } else if (myLightState <= LightState::Red) {
1322 178588 : return myRedString[i];
1323 : } else {
1324 315572 : return myYellowString[i];
1325 : }
1326 : }
1327 :
1328 : void
1329 381 : NEMAPhase::setMyNEMAStates() {
1330 381 : myGreenString = myCorePhase->getState();
1331 381 : myRedString = "";
1332 381 : myYellowString = "";
1333 5746 : for (char ch : myGreenString) {
1334 : myRedString += 'r';
1335 5365 : if (ch == 'G' || ch == 'g') {
1336 : myYellowString += 'y';
1337 : } else {
1338 : myYellowString += ch;
1339 : }
1340 : }
1341 381 : }
1342 :
1343 : void
1344 496530 : NEMAPhase::clearMyDetectors() {
1345 496530 : lastDetectActive = myDetectorInfo.detectActive;
1346 : // remove the active flag on the detector if the detector is not latching or if it is green
1347 496530 : if ((!myDetectorInfo.latching) || (myLightState >= LightState::Green)) {
1348 496099 : myDetectorInfo.detectActive = false;
1349 : }
1350 496530 : }
1351 :
1352 : void
1353 496530 : NEMAPhase::checkMyDetectors() {
1354 : // Check my Detectors, only necessary if it isn't currently marked as on
1355 496530 : if (!myDetectorInfo.detectActive) {
1356 : // If I have a cross phase target and it is active and I am not, save my detector as not active
1357 496293 : if (myDetectorInfo.cpdTarget != nullptr) {
1358 1272 : if (myDetectorInfo.cpdTarget->getCurrentState() >= LightState::Green) {
1359 1102 : if (myLightState < LightState::Green) {
1360 542 : myDetectorInfo.detectActive = false;
1361 542 : return;
1362 : }
1363 : }
1364 : }
1365 : // If we make it to this point, check my detector like normal.
1366 1110517 : for (auto& d : myDetectorInfo.detectors) {
1367 793429 : if (d->getCurrentVehicleNumber() > 0) {
1368 178663 : myDetectorInfo.detectActive = true;
1369 : return;
1370 : }
1371 : }
1372 : // If my detector is not active, check my cross phase
1373 317088 : if ((myDetectorInfo.cpdSource != nullptr) && (myLightState >= LightState::Green)) {
1374 497 : if (myDetectorInfo.cpdSource->getCurrentState() < LightState::Green) {
1375 864 : for (auto& d : myDetectorInfo.cpdSource->getDetectors()) {
1376 482 : if (d->getCurrentVehicleNumber() > 0) {
1377 100 : myDetectorInfo.detectActive = true;
1378 : return;
1379 : }
1380 482 : }
1381 : }
1382 : }
1383 : }
1384 : }
1385 :
1386 : void
1387 6249 : NEMAPhase::enter(NEMALogic* controller, NEMAPhase* lastPhase) {
1388 : #ifdef DEBUG_NEMA_SWITCH
1389 : std::cout << SIMTIME << " enter tls=" << controller->getID() << " phase=" << phaseName << "\n";
1390 : #endif
1391 :
1392 : // set the last phase instance to inactive
1393 : lastPhase->cleanupExit();
1394 :
1395 : // Enter the phase
1396 6249 : myStartTime = controller->getCurrentTime();
1397 6249 : myLightState = LightState::Green;
1398 6249 : myLastPhaseInstance = lastPhase;
1399 6249 : readyToSwitch = false;
1400 :
1401 : // implement the new timing parameters on the first coordinated phase to appear
1402 6249 : if (phaseName == controller->coordinatePhaseObjs[ringNum]->phaseName) {
1403 3048 : controller->implementTraciChanges();
1404 : }
1405 :
1406 : // Handle Green Rest Peculiarities
1407 6249 : if (!controller->coordinateMode && isGreenRest) {
1408 : // If the controller is in free mode and the phase is a green rest phase, then it should enter as "green rest"
1409 50 : myLightState = LightState::GreenRest;
1410 : // if the phase has "green rest" capabilities, set its timer to the dynamic maxGreen
1411 50 : greenRestTimer = maxDuration * isGreenRest;
1412 : }
1413 :
1414 : // clear the last transition decision
1415 6249 : lastTransitionDecision = nullptr;
1416 :
1417 : // Calculate the Max Green Time & Expected Duration here:
1418 6249 : if (controller->coordinateMode) {
1419 2217 : if (coordinatePhase) {
1420 1050 : myExpectedDuration = controller->ModeCycle(forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1421 : } else {
1422 1167 : maxGreenDynamic = controller->ModeCycle(forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1423 1167 : if (!fixForceOff) {
1424 810 : maxGreenDynamic = MIN2(maxDuration, maxGreenDynamic);
1425 : }
1426 1167 : myExpectedDuration = minDuration;
1427 : }
1428 : } else {
1429 4032 : myExpectedDuration = minDuration;
1430 : }
1431 : // Implements the maxRecall functionality
1432 6249 : if (maxRecall && !coordinatePhase) {
1433 106 : myExpectedDuration = maxGreenDynamic;
1434 : }
1435 : // Set the controller's active phase
1436 6249 : controller->setActivePhase(this);
1437 6249 : }
1438 :
1439 65349 : void NEMAPhase::exit(NEMALogic* controller, PhaseTransitionLogic* nextPhases[2]) {
1440 65349 : if (nextPhases[ringNum]->getToPhase() != this) {
1441 : // if the next phase is not me, then I need to go into a transition
1442 37121 : lastTransitionDecision = nextPhases[ringNum];
1443 37121 : if (myLightState >= LightState::Green) {
1444 : // if I am in green, then I need to enter yellow
1445 5911 : enterYellow(controller);
1446 5911 : return;
1447 : }
1448 :
1449 31210 : if (controller->getCurrentTime() - myLastEnd < (yellow + red)) {
1450 24857 : if (controller->getCurrentTime() - myLastEnd >= yellow) {
1451 : // if I am in yellow, then I need to enter red
1452 10526 : myLightState = LightState::Red;
1453 : }
1454 : // I am currently in the Red state but haven't reached max
1455 24857 : return;
1456 : }
1457 :
1458 6353 : handleRedXferOrNextPhase(controller, nextPhases);
1459 6353 : return;
1460 : }
1461 :
1462 28228 : handleGreenRestOrTransfer(controller, nextPhases);
1463 : }
1464 :
1465 5911 : void NEMAPhase::enterYellow(NEMALogic* controller) {
1466 5911 : myLastEnd = controller->getCurrentTime();
1467 5911 : myLightState = LightState::Yellow;
1468 5911 : transitionActive = true;
1469 5911 : }
1470 :
1471 6353 : void NEMAPhase::handleRedXferOrNextPhase(NEMALogic* controller, PhaseTransitionLogic* nextPhases[2]) {
1472 6353 : PhasePtr otherPhase = controller->getOtherPhase(this);
1473 6353 : bool barrierCross = nextPhases[ringNum]->getToPhase()->barrierNum != barrierNum;
1474 5876 : bool barrierCrossButOkay = barrierCross && (
1475 5876 : nextPhases[ringNum]->getToPhase()->barrierNum == nextPhases[otherPhase->ringNum]->getToPhase()->barrierNum
1476 6353 : ) && otherPhase->okay2ForceSwitch(controller);
1477 :
1478 6353 : if (!barrierCross) {
1479 477 : nextPhases[ringNum]->getToPhase()->enter(controller, this);
1480 477 : return;
1481 : }
1482 :
1483 5876 : if (barrierCrossButOkay) {
1484 : // if the barrier is crossed, but the other phase is going to the same barrier, then I can enter red transfer
1485 : // enter the next phase
1486 2685 : nextPhases[ringNum]->getToPhase()->enter(controller, this);
1487 : // trigger the other phase to enter red transfer
1488 2685 : nextPhases[otherPhase->ringNum]->getToPhase()->enter(controller, this);
1489 2685 : return;
1490 : }
1491 :
1492 3191 : myLightState = LightState::RedXfer;
1493 3191 : readyToSwitch = true;
1494 3191 : transitionActive = false;
1495 : }
1496 :
1497 28228 : void NEMAPhase::handleGreenRestOrTransfer(NEMALogic* controller, PhaseTransitionLogic* nextPhases[2]) {
1498 28228 : NEMAPhase* otherPhase = controller->getOtherPhase(this);
1499 28228 : readyToSwitch = false;
1500 42611 : bool isOtherPhaseReady = nextPhases[!ringNum]->getToPhase() == otherPhase && otherPhase->readyToSwitch;
1501 28228 : bool isOtherPhaseInGreenRest = otherPhase->greenRestTimer >= otherPhase->maxDuration && otherPhase->getCurrentState() == LightState::GreenRest;
1502 :
1503 28228 : if (isOtherPhaseReady || isOtherPhaseInGreenRest) {
1504 27116 : myLightState = LightState::GreenRest;
1505 27116 : myStartTime = controller->getCurrentTime() - minDuration;
1506 27116 : myExpectedDuration = minDuration;
1507 27116 : greenRestTimer = maxDuration * isGreenRest;
1508 : } else {
1509 1112 : myLightState = LightState::GreenXfer;
1510 1112 : if (isAtBarrier) {
1511 1047 : myExpectedDuration = (otherPhase->myExpectedDuration + otherPhase->myStartTime) - myStartTime;
1512 : }
1513 : }
1514 28228 : }
1515 :
1516 : SUMOTime
1517 13475 : NEMAPhase::getTransitionTime(NEMALogic* controller) {
1518 13475 : if (myLightState == LightState::RedXfer) {
1519 : // if in red xfer, I am ready to switch whenevery
1520 : return TIME2STEPS(0);
1521 : }
1522 10739 : if (!transitionActive) {
1523 : // if a transition is not active, the transition is just yellow + red time
1524 6806 : return getTransitionTimeStateless();
1525 : }
1526 : // if a transition is active, then return the time left in the transition
1527 3933 : return MAX2(TIME2STEPS(0), ((yellow + red) - (controller->getCurrentTime() - myLastEnd)));
1528 : }
1529 :
1530 : SUMOTime
1531 44379 : NEMAPhase::calcVehicleExtension(SUMOTime duration) {
1532 44379 : if (myExpectedDuration < maxGreenDynamic && myDetectorInfo.detectActive) {
1533 : // add the vehicle extension timer if the detector is active.
1534 : // capped by the minimum and maximum duration
1535 14987 : return MIN2(MAX2(duration + vehExt, minDuration), maxGreenDynamic);
1536 : }
1537 : return myExpectedDuration;
1538 : }
1539 :
1540 : void
1541 174024 : NEMAPhase::update(NEMALogic* controller) {
1542 : // If I am in a transition, the rest of the update logic does not matter
1543 174024 : if (myLightState < LightState::Green) {
1544 : // return early
1545 31293 : readyToSwitch = true;
1546 31293 : return;
1547 : }
1548 :
1549 : // Continuation Logic
1550 142731 : SUMOTime duration = controller->getCurrentTime() - myStartTime;
1551 : // Check the vehicle extension timer as long as not in green transfer and not a coordinated phase
1552 142731 : if (myLightState != LightState::GreenXfer && !coordinatePhase) {
1553 44379 : myExpectedDuration = calcVehicleExtension(duration);
1554 : }
1555 : // Special Logic for Green Rest, which behaves uniquely
1556 142731 : if (myLightState == LightState::GreenRest) {
1557 : // check all other detectors and see if anything else is active. If so,
1558 : // start the green rest timer countdown, which is == to the max duration of the phase
1559 : bool vehicleActive = false;
1560 239967 : for (auto& p : controller->getPhaseObjs()) {
1561 214565 : if ((p->phaseName != phaseName)
1562 187511 : && (p->phaseName != controller->getOtherPhase(this)->phaseName)
1563 535238 : && p->callActive()) {
1564 3712 : greenRestTimer -= DELTA_T;
1565 : vehicleActive = true;
1566 3712 : break;
1567 : }
1568 29114 : }
1569 : // catch the rising edge of the sidestreet detection and calculate the maximum timer
1570 29114 : if (vehicleActive && (greenRestTimer + DELTA_T >= maxDuration)) {
1571 2742 : maxGreenDynamic = minDuration + maxDuration;
1572 : }
1573 :
1574 : // if there are no other vehicles slide the startTime along
1575 : if (!vehicleActive) {
1576 25402 : greenRestTimer = maxDuration;
1577 25402 : if (duration >= minDuration) {
1578 25005 : myStartTime = controller->getCurrentTime() - minDuration;
1579 25005 : maxGreenDynamic = minDuration + maxDuration;
1580 25005 : myExpectedDuration = minDuration + MAX2(TIME2STEPS(0), myExpectedDuration - duration);
1581 : }
1582 : }
1583 :
1584 : // if the green rest timer is exhausted, set ready to switch
1585 29114 : if (greenRestTimer < DELTA_T) {
1586 3 : readyToSwitch = true;
1587 : // force the counterpart to be ready to switch too. This needs to be latching....
1588 3 : NEMAPhase* otherPhase = controller->getOtherPhase(this);
1589 3 : if (otherPhase->getCurrentState() > LightState::Green) {
1590 3 : otherPhase->readyToSwitch = true;
1591 : }
1592 : }
1593 :
1594 : // Special Behavior when the Green Rest Circles all the way around in coordinated mode
1595 29114 : if (coordinatePhase) {
1596 : // This means that we have green rested until I should "start" again. Just call my entry function again.
1597 26176 : if (controller->getTimeInCycle() <= ((forceOffTime - maxDuration) + DELTA_T / 2)) {
1598 268 : enter(controller, this);
1599 : }
1600 : }
1601 : }
1602 : // Check to see if a switch is desired
1603 142731 : if (duration >= myExpectedDuration) {
1604 34137 : readyToSwitch = true;
1605 : }
1606 : }
1607 :
1608 : PhaseTransitionLogic*
1609 176 : NEMAPhase::getTransition(int toPhase) {
1610 423 : for (auto t : myTransitions) {
1611 423 : if (t->getToPhase()->phaseName == toPhase) {
1612 : return t;
1613 : }
1614 : }
1615 : // This point should never be reached
1616 : assert(0);
1617 : // To satisfy the compiler and return value from all control paths
1618 0 : return myTransitions.front();
1619 : }
1620 :
1621 : std::vector<PhaseTransitionLogic*>
1622 68686 : NEMAPhase::trySwitch(NEMALogic* controller) {
1623 : // this function returns the preferred valid transition for the phase
1624 : std::vector<PhaseTransitionLogic*> nextTransitions;
1625 68686 : if (readyToSwitch) {
1626 : // only try to switch if I am ready to switch
1627 150754 : for (auto& t : myTransitions) {
1628 : // for the transitions check if it is okay
1629 150685 : if (t->okay(controller)) {
1630 : // if there was already a transition decision, it can be overriden but only if the new transition
1631 : // is on the same side of a barrier
1632 65393 : if (lastTransitionDecision != nullptr) {
1633 31254 : if (t->getToPhase()->barrierNum == lastTransitionDecision->getToPhase()->barrierNum) {
1634 31224 : nextTransitions.push_back(t);
1635 : break;
1636 : }
1637 : } else {
1638 34139 : nextTransitions.push_back(t);
1639 : // break once there is a valid option (they have already been sorted)
1640 : break;
1641 : }
1642 : }
1643 : }
1644 : }
1645 : // Add in the last transition decision if it hasn't been added in yet
1646 68686 : if (lastTransitionDecision != nullptr) {
1647 : bool found = false;
1648 : bool sameBarrier = false;
1649 31293 : for (auto& t : nextTransitions) {
1650 31224 : if (t == lastTransitionDecision) {
1651 : found = true;
1652 : break;
1653 : }
1654 2 : if (t->getToPhase()->barrierNum == lastTransitionDecision->getToPhase()->barrierNum) {
1655 : sameBarrier = true;
1656 : break;
1657 : }
1658 : }
1659 : // but only need to add it if it is not in the list AND if nothing in the list is the same barrier as it was.
1660 31293 : if (!found && !sameBarrier) {
1661 69 : nextTransitions.push_back(lastTransitionDecision);
1662 : }
1663 : }
1664 : // Add the transition back to myself, but only in the case when no others have been added
1665 68686 : if (nextTransitions.size() < 1) {
1666 3254 : nextTransitions.push_back(myTransitions.back());
1667 : }
1668 :
1669 68686 : return nextTransitions;
1670 0 : }
1671 :
1672 : // ===========================================================================
1673 : // PhaseTransitionLogic Definitions
1674 : // ===========================================================================
1675 1022 : PhaseTransitionLogic::PhaseTransitionLogic(
1676 1022 : NEMAPhase* fromPhase, NEMAPhase* toPhase) :
1677 1022 : distance(0),
1678 1022 : fromPhase(fromPhase),
1679 1022 : toPhase(toPhase)
1680 1022 : {}
1681 :
1682 : bool
1683 150685 : PhaseTransitionLogic::okay(NEMALogic* controller) {
1684 : // Picking the correct transition logic to use
1685 : // #TODO this could be a case of using function as variable and setting it at PhaseTransitionLogic
1686 : // creation time
1687 150685 : if (fromPhase == toPhase) {
1688 : // for green rest or green transfer, it cannot return to itself if a transition is active
1689 28258 : return fromPhase->getCurrentState() >= LightState::Green;
1690 122427 : } else if (fromPhase->coordinatePhase) {
1691 : // if the from phase is a coordinated phase i.e. {2, 6} in a standard setup
1692 84779 : return fromCoord(controller);
1693 37648 : } else if (fromPhase->isAtBarrier) {
1694 : // if the phase is at a barrier i.e. {2, 6, 4, 8} in a standard setup
1695 33071 : return fromBarrier(controller);
1696 4577 : } else if (controller->coordinateMode) {
1697 : // typical coordinate mode transition,
1698 4028 : return coordBase(controller);
1699 : } else {
1700 : // base transition logic
1701 549 : return freeBase(controller);
1702 : }
1703 : }
1704 :
1705 : bool
1706 119649 : PhaseTransitionLogic::freeBase(NEMALogic* controller) {
1707 : // Simplest transition logic. Just check if a detector (or recall) is active on that phase and
1708 : bool okay = false;
1709 : // is a call active on the toPhase?
1710 206495 : if (toPhase->callActive()) {
1711 : // would the transition be a barrier cross?
1712 37999 : if (fromPhase->barrierNum != toPhase->barrierNum) {
1713 36922 : PhasePtr otherPhase = controller->getOtherPhase(fromPhase);
1714 : // If it is a barrier cross, then the other phase must also be ready to switch
1715 : // or have a transition time that is lower than mine currently. DELTA_T is critical here
1716 36922 : if (otherPhase->readyToSwitch) {
1717 : // #&& otherPhase->getTransitionTime(controller) <= fromPhase->getTransitionTime(controller)) {
1718 : okay = true;
1719 : }
1720 : } else {
1721 : okay = true;
1722 : }
1723 : }
1724 119649 : return okay;
1725 : }
1726 :
1727 : bool
1728 88807 : PhaseTransitionLogic::coordBase(NEMALogic* controller) {
1729 88807 : if (toPhase->coordinatePhase &&
1730 2822 : (controller->getOtherPhase(fromPhase)->readyToSwitch || fromPhase->barrierNum == toPhase->barrierNum)) {
1731 : // transitions TO the coordinated phase may always happen, as long as the other phase is okay to switch too
1732 : return true;
1733 : }
1734 : // first check if the free logic is upheld
1735 86029 : else if (freeBase(controller)) {
1736 : // Then check if the "to phase" can fit, which means that there is enough time to fit the current transition + the minimum time of the next phase
1737 7756 : SUMOTime transitionTime = fromPhase->getTransitionTime(controller);
1738 7756 : SUMOTime timeTillForceOff = controller->ModeCycle(toPhase->forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1739 7756 : if (toPhase->minDuration + transitionTime <= timeTillForceOff) {
1740 : return true;
1741 : }
1742 : }
1743 : return false;
1744 : }
1745 :
1746 :
1747 : bool
1748 33071 : PhaseTransitionLogic::fromBarrier(NEMALogic* controller) {
1749 33071 : if (freeBase(controller)) {
1750 29308 : if (fromPhase->barrierNum == toPhase->barrierNum) {
1751 : // same barrier side so we are good.
1752 : // Check if green transfer is active. If so, we need to make sure that there are no calls on the other side of the barrier
1753 83 : if (fromPhase->getCurrentState() >= LightState::Green) {
1754 108 : for (auto& p : controller->getPhasesByRing(fromPhase->ringNum)) {
1755 114 : if (p->barrierNum != fromPhase->barrierNum && p->callActive()) {
1756 : return false;
1757 : }
1758 54 : }
1759 : }
1760 32 : return true;
1761 : } else {
1762 : // This is now a barrier cross and we need to make sure that the other phase is also ready to transition
1763 29225 : if (fromPhase->readyToSwitch && controller->getOtherPhase(fromPhase)->readyToSwitch) {
1764 : return true;
1765 : }
1766 : }
1767 : }
1768 : return false;
1769 : }
1770 :
1771 :
1772 : bool
1773 84779 : PhaseTransitionLogic::fromCoord(NEMALogic* controller) {
1774 84779 : if (coordBase(controller)) {
1775 : // Determine if the other phase is also ready to switch
1776 7445 : if (controller->getOtherPhase(fromPhase)->readyToSwitch) {
1777 : // Dr. Wang had the Type-170 code setup in a way that it could transition whenever - meaning that it didn't matter if the prior phase could fit or not
1778 7429 : if (controller->isType170()) {
1779 : return true;
1780 : }
1781 : // If the transition is already active, then report that the movement is possible
1782 4291 : if (fromPhase->isTransitionActive()) {
1783 : return true;
1784 : }
1785 : // now determine if there my prior phase can fit or not. We already know that I can fit.
1786 3034 : NEMAPhase* priorPhase = toPhase->getSequentialPriorPhase();
1787 3034 : SUMOTime timeTillForceOff = controller->ModeCycle(priorPhase->forceOffTime - controller->getTimeInCycle(), controller->getCurrentCycleLength());
1788 3034 : SUMOTime transitionTime = fromPhase->getTransitionTime(controller);
1789 : // if the time till the force off is less than the min duration ||
1790 : // if it is greater than the cycle length minus the length of the coordinate phase (which the fromPhase automatically is)
1791 3034 : if ((priorPhase->minDuration + transitionTime) > timeTillForceOff || timeTillForceOff > (controller->getCurrentCycleLength() - fromPhase->minDuration)) {
1792 : return true;
1793 : }
1794 : }
1795 : }
1796 : return false;
1797 : }
1798 :
1799 : int
1800 68862 : PhaseTransitionLogic::getDistance(PhaseTransitionLogic* otherTrans) {
1801 : // Returns the other transitions distance in green transfer situations
1802 68862 : if ((toPhase == fromPhase) && (otherTrans->toPhase->barrierNum == toPhase->barrierNum)) {
1803 31531 : if (toPhase->getCurrentState() == LightState::Green || toPhase->getCurrentState() == LightState::GreenXfer) {
1804 4319 : return otherTrans->distance;
1805 : }
1806 : }
1807 64543 : return distance;
1808 : }
|