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.h
15 : /// @author Tianxin Li
16 : /// @author Qichao Wang
17 : /// @date August 2020
18 : ///
19 : // An actuated NEMA-phase-compliant traffic light logic
20 : /****************************************************************************/
21 : #pragma once
22 : #include <config.h>
23 :
24 : #include <utility>
25 : #include <vector>
26 : #include <bitset>
27 : #include <map>
28 : #include <set>
29 : #include <microsim/MSEventControl.h>
30 : #include <microsim/traffic_lights/MSTrafficLightLogic.h>
31 : #include "MSSimpleTrafficLightLogic.h"
32 : #include "microsim/output/MSE2Collector.h"
33 : #include "MSPhaseDefinition.h"
34 :
35 :
36 : // ===========================================================================
37 : // class declarations
38 : // ===========================================================================
39 : class NLDetectorBuilder;
40 : class MSE2Collector;
41 : class NEMAPhase;
42 : class PhaseTransitionLogic;
43 :
44 : // ===========================================================================
45 : // Enumeration
46 : // ===========================================================================
47 : enum class LightState {
48 : RedXfer,
49 : Red,
50 : Yellow,
51 : Green,
52 : GreenXfer,
53 : GreenRest,
54 : };
55 :
56 :
57 : // ===========================================================================
58 : // class definitions
59 : // ===========================================================================
60 : /**
61 : * @class NEMALogic
62 : * @brief A NEMA (adaptive) traffic light logic based on E2Detector
63 : */
64 : class NEMALogic : public MSSimpleTrafficLightLogic {
65 : public:
66 :
67 : typedef NEMAPhase* PhasePtr;
68 :
69 : typedef std::map<MSLane*, MSE2Collector*> LaneDetectorMap;
70 :
71 : typedef std::map<MSE2Collector*, MSLane*, ComparatorIdLess> DetectorLaneMap;
72 :
73 : // Small structure for storing two ring transitions and the average distance
74 : struct transitionInfo {
75 : PhaseTransitionLogic* p1;
76 : PhaseTransitionLogic* p2;
77 : float distance;
78 : };
79 :
80 : enum controllerType {
81 : Type170,
82 : TS2
83 : };
84 :
85 : /// @brief constant for storing the priority order for light heads. Iterates left to right and stops when finds a match.
86 : const std::string lightHeadPriority = "GgyuOs";
87 :
88 : typedef std::vector<transitionInfo> TransitionPairs;
89 :
90 : /** @brief Constructor
91 : * @param[in] tlcontrol The tls control responsible for this tls
92 : * @param[in] id This tls' id
93 : * @param[in] programID This tls' sub-id (program id)
94 : * @param[in] phases Definitions of the phases
95 : * @param[in] step The initial phase index
96 : * @param[in] delay The time to wait before the first switch
97 : * @param[in] parameter The parameter to use for tls set-up
98 : */
99 : NEMALogic(MSTLLogicControl& tlcontrol,
100 : const std::string& id, const std::string& programID,
101 : const SUMOTime offset,
102 : const MSSimpleTrafficLightLogic::Phases& phases,
103 : int step, SUMOTime delay,
104 : const std::map<std::string, std::string>& parameter,
105 : const std::string& basePath);
106 :
107 :
108 : /** @brief Initialises the tls with information about incoming lanes
109 : * @param[in] nb The detector builder
110 : * @exception ProcessError If something fails on initialisation
111 : */
112 : void init(NLDetectorBuilder& nb) override;
113 :
114 : /// @brief Destructor
115 : ~NEMALogic();
116 :
117 : /// @brief overrides the MSSimpleTrafficLightLogic trySwitch method
118 : SUMOTime trySwitch() override;
119 :
120 : /// @name Dynamic Information Retrieval
121 : /// @{
122 :
123 : /** @brief Returns myPhase, which doesn't correspond to a NEMA phase, but rather the composite light string
124 : * @return The current phase (NEMA controller really uses )
125 : */
126 : const MSPhaseDefinition& getCurrentPhaseDef() const override;
127 : /// @}
128 :
129 :
130 : void activateProgram() override;
131 : void deactivateProgram() override;
132 :
133 : bool showDetectors() const {
134 0 : return myShowDetectors;
135 : }
136 :
137 : void setShowDetectors(bool show);
138 :
139 : /// @brief retrieve all detectors used by this program
140 : std::map<std::string, double> getDetectorStates() const override;
141 :
142 : /**
143 : * @brief extends the transitions vector with valid Transitions given the current traffic light state
144 : *
145 : * @param[out] transitions a reference to the TransitionPairs vector
146 : */
147 : void getNextPhases(TransitionPairs& transitions);
148 :
149 : /**
150 : * @brief Calculates the modulus a / b, normally used to calculate the cycle time between
151 : * two times. Usage example: ModeCycle(t1 - t2, cycleLength)
152 : *
153 : * @param a time 1
154 : * @param b time 2
155 : * @return SUMOTime
156 : */
157 : SUMOTime ModeCycle(SUMOTime a, SUMOTime b);
158 :
159 :
160 : /**
161 : * @brief returns the IDs of the phase's controlled lanes.
162 : * Found by looking for the "G" in the light state string
163 : *
164 : * @param state the light state string
165 : * @return std::set<std::string>
166 : */
167 : void getLaneInfoFromNEMAState(std::string state, StringVector& laneIDs, IntVector& stateIndex);
168 :
169 : /**
170 : * @brief Set the max green of all phases.
171 : *
172 : * @param newMaxGreens a vector of new max green times. Must be length 8
173 : */
174 : void setNewMaxGreens(std::vector<double> newMaxGreens);
175 :
176 : /**
177 : * @brief Set the new splits of all phases
178 : *
179 : * @param newSplits a vector of new splits. Must be length 8
180 : */
181 : void setNewSplits(std::vector<double> newSplits);
182 :
183 : /**
184 : * @brief set the new cycle length for the controller
185 : *
186 : * @param newCycleLength
187 : */
188 : void setNewCycleLength(double newCycleLength);
189 :
190 : /**
191 : * @brief Set the new offset for the controller
192 : *
193 : * @param newOffset
194 : */
195 : void setNewOffset(double newOffset);
196 :
197 : /**
198 : * @brief Get the current cycle length
199 : *
200 : * @return SUMOTime
201 : */
202 : SUMOTime getCurrentCycleLength() {
203 12705 : return myCycleLength;
204 : }
205 :
206 : /// @brief try to set the given parameter. Parameters prefixed with 'NEMA.' control functionality
207 : void setParameter(const std::string& key, const std::string& value) override;
208 :
209 : /// @brief try to get the value of the given parameter. Parameters prefixed with 'NEMA.' control functionality
210 : const std::string getParameter(const std::string& key, const std::string defaultValue = "") const override;
211 :
212 : /// @brief Wrapper Function to Simplify Accessing Time
213 : inline SUMOTime getCurrentTime(void) const {
214 229161 : return simTime;
215 : }
216 :
217 : // /// @brief Wrapper Function to Simplify Accessing Offset Cycle Time
218 : // inline SUMOTime getCurrentOffsetTime(void) const {return simTime - cycleRefPoint - offset; };
219 :
220 : /// @brief override Function to Simplify Accessing Offset Cycle Time
221 : inline SUMOTime getTimeInCycle() const {
222 38561 : return (simTime - cycleRefPoint - offset) % myCycleLength;
223 : }
224 :
225 :
226 : /// @brief set the active phase
227 : void setActivePhase(PhasePtr phase);
228 :
229 : /**
230 : * @brief Get the Active Phase object for a specified ring
231 : *
232 : * @param ringNum
233 : * @return PhasePtr
234 : */
235 : inline PhasePtr getActivePhase(int ringNum) {
236 : return myActivePhaseObjs[ringNum];
237 : }
238 :
239 : /**
240 : * @brief get all phases for a given ring
241 : *
242 : * @param ringNum
243 : * @return std::vector<PhasePtr>
244 : */
245 : std::vector<PhasePtr> getPhasesByRing(int ringNum);
246 :
247 : /**
248 : * @brief get the phase object matching the phaseNum
249 : * If ringNum is passed, it will only search for the phase in the given ring
250 : *
251 : * @param phaseNum an integer corresponding to the phase
252 : * @param ringNum the ring to search for the phase in. Defaults to -1, meaning both rings will be searched
253 : * @return PhasePtr (NEMAPhase*)
254 : */
255 : PhasePtr getPhaseObj(int phaseNum, int ringNum = -1);
256 :
257 : /**
258 : * @brief get a vector of all phase objects
259 : *
260 : * @return std::vector<PhasePtr>
261 : */
262 : inline std::vector<PhasePtr> getPhaseObjs(void) {
263 28614 : return myPhaseObjs;
264 : }
265 :
266 : /**
267 : * @brief return the ring distance between two phases
268 : *
269 : * @param p1 phase 1
270 : * @param p2 phase 2
271 : * @param ringNum the ring on which to measure the phase distance
272 : * @return int
273 : */
274 : int measureRingDistance(int p1, int p2, int ringNum);
275 :
276 : /**
277 : * @brief checks if the controller is of type170
278 : *
279 : * @return true if myControllerType == Type170
280 : * @return false
281 : */
282 : inline bool isType170(void) const {
283 7385 : return myControllerType == Type170;
284 : }
285 :
286 : /**
287 : * @brief Get the opposite active phase
288 : *
289 : * @param p a pointer to the known phase
290 : * @return PhasePtr
291 : */
292 : PhasePtr getOtherPhase(PhasePtr p);
293 :
294 : /// @brief whether the controller is in coordinated mode or not
295 : bool coordinateMode;
296 :
297 : /**
298 : * @brief implement any pending traci changes
299 : * This function is called once per cycle
300 : */
301 : void implementTraciChanges(void);
302 :
303 : /// @brief a store of the coordinated phase objects. Only used meaningfully when the controller is
304 : /// in coordinated mode
305 : PhasePtr coordinatePhaseObjs[2];
306 :
307 : protected:
308 :
309 : /// @brief flag to keep track of whether a timing change has been requested via traci
310 : bool queuedTraciChanges;
311 :
312 : /// @brief the controller's offset
313 : SUMOTime offset;
314 : /// @brief the next offset to implement
315 : SUMOTime myNextOffset;
316 :
317 : /// @brief the coordinated cycle length
318 : SUMOTime myCycleLength;
319 : /// @brief the next cycle length (set by traci)
320 : SUMOTime myNextCycleLength;
321 :
322 : /// @brief stores the simulation time to make it easily accessible
323 : SUMOTime simTime = 0;
324 :
325 : /// @brief stores controllers # of rings
326 : int myNumberRings;
327 :
328 : /// @brief stores the length of phase string for the controller "GGrrrrs" = 6. Must be the same length for all phases
329 : int myPhaseStrLen = -1;
330 :
331 : /// @brief Set the simTime
332 : inline void setCurrentTime(void) {
333 87009 : simTime = MSNet::getInstance()->getCurrentTimeStep();
334 : }
335 :
336 : /// @brief variable to store the active phases
337 : PhasePtr myActivePhaseObjs[2] = { nullptr, nullptr };
338 :
339 : /// @brief a vector that stores a pointer to the instantiated NEMAPhase objects
340 : std::vector<PhasePtr > myPhaseObjs;
341 :
342 : /// @brief an array to store the phases located at a barrier for each ring
343 : PhasePtr defaultBarrierPhases[2][2];
344 :
345 : /**
346 : * Construct Timing and Phase Defs
347 : * @brief constructs phase using the configuration file
348 : * @param barriers a string of barrier phases ("4,8")
349 : * @param coordinates a string of coordinated phases ("2,6")
350 : * @param ring1 a string of phases in ring 1 ("1,2,3,4")
351 : * @param ring2 a string of phases in ring 2 ("5,6,7,8")
352 : */
353 : void constructTimingAndPhaseDefs(std::string& barriers, std::string& coordinates,
354 : std::string& ring1, std::string& ring2);
355 :
356 : /** @brief iterates over the two active phases (myActivePhaseObjs) and merges the two active phases
357 : * @return std::string the light string to implement (GGGrrrGGGrrr)
358 : */
359 : std::string composeLightString();
360 :
361 : /** @brief check if a vector contains an element
362 : * @param v the vector of phase numbers
363 : * @param phaseNum the phase number
364 : * @return bool
365 : */
366 : bool vectorContainsPhase(std::vector<int> v, int phaseNum);
367 :
368 : // create a small datatype for mapping detector to phase index
369 : // This is the one copied from MSActuatedTrafficLightLogic
370 : // not used in our controller, but it is here for meeting the SUMO default traffic logic light check
371 : // this one and related could be removed with extra efforts
372 2294 : struct DetectorInfo {
373 537 : DetectorInfo(MSE2Collector* _det, int numPhases) :
374 537 : det(_det),
375 537 : servedPhase(numPhases, false)
376 : {}
377 : MSE2Collector* det;
378 : SUMOTime lastGreenTime = 0;
379 : std::vector<bool> servedPhase;
380 : };
381 : typedef std::vector<std::vector<DetectorInfo*>> detectorMap;
382 : detectorMap myDetectorForPhase;
383 : /// @brief storing the detector info in a vector
384 : std::vector<DetectorInfo> myDetectorInfoVector;
385 :
386 :
387 : /// @brief return whether there is a major link from the given lane in the given phase
388 : bool hasMajor(const std::string& state, const LaneVector& lanes) const;
389 :
390 : /**
391 : * @brief converts a comma separated string into a integer vector
392 : * "1,2,3,4" -> {1,2,3,4}
393 : *
394 : * @param s the string of comma separated integers
395 : * @return std::vector<int>
396 : */
397 : std::vector<int> readParaFromString(std::string s);
398 :
399 : /**
400 : * @brief decide whether the detector is for left turn lane
401 : * if it is, use the detector length for left turn lane
402 : *
403 : * @param lane a pointer to the lane
404 : * @return whether a lane is a left turn or not
405 : */
406 : bool isLeftTurnLane(const MSLane* const lane) const;
407 :
408 : /// @brief convert a string to an integer
409 : int string2int(std::string s);
410 :
411 : /// @brief A map from lanes to detectors
412 : LaneDetectorMap myLaneDetectorMap;
413 :
414 : /// @brief A map from lanes names to phases
415 : std::map<std::string, int> myLanePhaseMap;
416 :
417 : /// @brief A map from detectors to lanes
418 : DetectorLaneMap myDetectorLaneMap;
419 :
420 : /// @brief store the generated detector length
421 : double myDetectorLength;
422 :
423 : /// @brief store the left turn lane detestor length
424 : double myDetectorLengthLeftTurnLane;
425 :
426 : /// Whether the detectors shall be shown in the GUI
427 : bool myShowDetectors;
428 :
429 : /// The output file for generated detectors
430 : std::string myFile;
431 :
432 : /// The frequency for aggregating detector output
433 : SUMOTime myFreq;
434 :
435 : /// Whether detector output separates by vType
436 : std::string myVehicleTypes;
437 :
438 : /*
439 : {
440 : {3,4,1,2},
441 : {7,8,5,6}
442 : }
443 : */
444 : std::vector<std::vector<int>> rings;
445 :
446 : /*
447 : {
448 : {1 : PhaseDetectorInfo{
449 : detectors: {det1, det2, ...},
450 : crossPhaseDetector: 6
451 : },
452 : },
453 : {2 : ...
454 : }
455 : */
456 : // std::map<int, PhaseDetectorInfo> phase2DetectorMap;
457 : std::map<int, std::vector<std::string>> phase2ControllerLanesMap;
458 :
459 : bool fixForceOff;
460 : SUMOTime cycleRefPoint;// missing update
461 : bool whetherOutputState;
462 : bool ignoreErrors;
463 :
464 : /**
465 : * @brief return the default transition for t give its and the ot's state
466 : *
467 : * @param t the target phase
468 : * @param ot the other active phase
469 : * @return PhaseTransitionLogic* the transition logic describing this transition
470 : */
471 : PhaseTransitionLogic* getDefaultTransition(PhaseTransitionLogic* t, PhaseTransitionLogic* ot);
472 :
473 : // Store the cabinet type
474 : controllerType myControllerType;
475 :
476 : /**
477 : * @brief parse the controllerType from the tllogic description
478 : *
479 : * @param inputType
480 : * @return controllerType
481 : */
482 : controllerType parseControllerType(std::string inputType);
483 :
484 : /// @brief virtual phase that holds the current state
485 : MSPhaseDefinition myPhase;
486 :
487 : /**
488 : * @brief throw an InvalidArgument error if the param_name is not set
489 : *
490 : * @param param_variable the value of param_name
491 : * @param param_name the name of the parameter
492 : */
493 : void error_handle_not_set(std::string param_variable, std::string param_name);
494 :
495 : /**
496 : * @brief validates the NEMA timing.
497 : * Writes warnings if ignoreError set to true else throws ProcessError
498 : *
499 : */
500 : void validate_timing();
501 :
502 : /**
503 : * @brief calculate the forceOffs for a TS2 style offset
504 : * From https://ops.fhwa.dot.gov/publications/fhwahop08024/chapter6.htm#6.3
505 : *
506 : */
507 : void calculateForceOffsTS2();
508 : /**
509 : * @brief calculate the forceOffs for a Type 170 style offset
510 : * From https://ops.fhwa.dot.gov/publications/fhwahop08024/chapter6.htm#6.3
511 : */
512 : void calculateForceOffs170();
513 :
514 : /// @brief directs the code to the correct force off function accorifing to its cabinet type
515 91 : void calculateForceOffs() {
516 91 : switch (myControllerType) {
517 19 : case Type170:
518 19 : return calculateForceOffs170();
519 72 : case TS2:
520 72 : return calculateForceOffsTS2();
521 0 : default:
522 0 : return calculateForceOffs170();
523 : }
524 : }
525 :
526 :
527 : /// @brief calculate the initial phases for the TS2 style controller to start in
528 : void calculateInitialPhasesTS2();
529 : /// @brief calculate the initial phases for Type 170
530 : void calculateInitialPhases170();
531 : /// @brief directs the controller to the correct calculate phases function
532 33 : void calculateInitialPhases() {
533 33 : switch (myControllerType) {
534 11 : case Type170:
535 11 : return calculateInitialPhases170();
536 22 : case TS2:
537 22 : return calculateInitialPhasesTS2();
538 0 : default:
539 : // Default to Type170
540 0 : return calculateInitialPhases170();
541 : }
542 : }
543 : };
544 :
545 :
546 : /**
547 : * @class NEMAPhase
548 : * @brief One phase in the NEMAController
549 : *
550 : * This represents one phase and all its parameters in a NEMA traffic light
551 : * The phase ultimately controls its transition to the next phase,
552 : * and is resbonisble for determining the valid transitions given its current state
553 : */
554 : class NEMAPhase {
555 : public:
556 : /// @brief Typedef for commonly used phase pointer
557 : typedef NEMAPhase* PhasePtr;
558 :
559 : /// @struct PhaseDetectorInfo
560 : /// @brief stores information about the phase's detector(s)
561 695 : struct PhaseDetectorInfo {
562 381 : PhaseDetectorInfo() :
563 : detectors(),
564 381 : cpdTarget(),
565 381 : cpdSource(),
566 381 : detectActive(),
567 381 : latching()
568 : {}
569 354 : PhaseDetectorInfo(bool latching, PhasePtr cpdSource, PhasePtr cpdTarget) :
570 354 : cpdTarget(cpdTarget),
571 354 : cpdSource(cpdSource),
572 354 : detectActive(false),
573 354 : latching(latching)
574 : {}
575 : ///@brief a vector of pointers to the phase's detectors
576 : std::vector<MSE2Collector*> detectors;
577 : /// @brief the cross-phase switching target for myself (6 if 6 should check 1 if 6 is green and I am phase 1)
578 : PhasePtr cpdTarget;
579 : /// @brief the cross-phase switching source for myself (1 if 6 should check 1 if 6 is green and I am phase 6)
580 : PhasePtr cpdSource;
581 : /// @brief where any of my detectors are active or not
582 : bool detectActive;
583 : /// @brief whether the detectors are latching or not
584 : bool latching;
585 : };
586 :
587 : // create a PhaseDetectorInfo type
588 : typedef PhaseDetectorInfo PhaseDetectorInfo;
589 :
590 : /**
591 : * @brief Construct a new NEMAPhase object
592 : *
593 : * @param phaseName the "name" of the phase as an integer
594 : * @param isBarrier if the phase is located at a barrier or not
595 : * @param isGreenRest if it is a phase in which the traffic signal can green rest
596 : * @param isCoordinated if it is a coordinated phase
597 : * @param minRecall whether or not the phase has minimum recall or not
598 : * @param maxRecall whether or not the phase has maximum recall or not
599 : * @param fixForceOff if the phase has a force off or not
600 : * @param barrierNum the barrier to which the phase belongs (0 or 1)
601 : * @param ringNum the ring to which the phase belongs (0 or 1)
602 : * @param phaseStringInds the indexes of lanes that I control, ie. "srrrrGG" is {5, 6}
603 : * @param phase the MSPhaseDefinition base class
604 : */
605 : NEMAPhase(int phaseName,
606 : bool isBarrier,
607 : bool isGreenRest,
608 : bool isCoordinated,
609 : bool minRecall,
610 : bool maxRecall,
611 : bool fixForceOff,
612 : int barrierNum,
613 : int ringNum,
614 : IntVector phaseStringInds,
615 : MSPhaseDefinition* phase);
616 :
617 : /// @brief Destructor
618 : ~NEMAPhase();
619 :
620 : /// @brief gets the current light state
621 : inline LightState getCurrentState() const {
622 86206 : return myLightState;
623 : }
624 : /// @brief returns a vector of the phases detectors
625 : inline std::vector<MSE2Collector*> getDetectors() const {
626 482 : return myDetectorInfo.detectors;
627 : }
628 :
629 :
630 : /// @brief sets the detectors for the phase
631 : inline void setDetectors(std::vector<MSE2Collector*> detectors) {
632 341 : myDetectorInfo.detectors = detectors;
633 341 : }
634 :
635 : /// @brief check if a transition is active
636 : inline bool isTransitionActive() const {
637 4247 : return myLightState < LightState::Green;
638 : }
639 :
640 : // Build a Map of Valid Transitions and store the detector-based information
641 : /**
642 : * @brief initializes the object
643 : *
644 : * @param controller a pointer to the controller object
645 : * @param crossPhaseTarget the cross phase switching target
646 : * @param crossPhaseSource the cross phase switching source
647 : * @param latching whether the phase has latching detectors or not
648 : */
649 : void init(NEMALogic* controller, int crossPhaseTarget, int crossPhaseSource, bool latching);
650 :
651 : /**
652 : * @brief update is called on the active phases by the NEMAController at every time step
653 : *
654 : * @param controller a reference to the controller
655 : */
656 : void update(NEMALogic* controller);
657 :
658 : /**
659 : * @brief handles the transition out of a phase into the next (puts the phase through (G -> Y -> R) transition
660 : *
661 : * @param controller a reference to the NEMAController
662 : * @param nextPhases the next phases that the controller wants to transition to
663 : */
664 : void exit(NEMALogic* controller, PhaseTransitionLogic* nextPhases[2]);
665 :
666 : /**
667 : * @brief handles the transition into a green rest state
668 : *
669 : * @param controller a reference to the NEMAController
670 : * @param nextPhases the next phases that the controller wants to transition to
671 : */
672 : void handleGreenRestOrTransfer(NEMALogic* controller, PhaseTransitionLogic* nextPhases[2]);
673 :
674 : /**
675 : * @brief handles the transition into yellow
676 : *
677 : * @param controller a reference to the NEMAController
678 : */
679 : void enterYellow(NEMALogic* controller);
680 :
681 : /**
682 : * @brief handles the transition into a red xfer state, which is roughly the same as green rest
683 : *
684 : * @param controller a reference to the NEMAController
685 : */
686 : void handleRedXferOrNextPhase(NEMALogic* controller, PhaseTransitionLogic* nextPhases[2]);
687 :
688 : /// @brief simple method to check if there is a recall on the phase.
689 : inline bool hasRecall(void) {
690 : return minRecall || maxRecall;
691 : }
692 :
693 : /// @brief simple method to check if there is either a recall or an active detector
694 : inline bool callActive(void) {
695 276261 : return minRecall || maxRecall || myDetectorInfo.detectActive;
696 : }
697 :
698 : /// @brief simple method to check if a detector is active
699 : inline bool detectActive(void) {
700 : return myDetectorInfo.detectActive;
701 : }
702 :
703 : /// @brief Check Detectors. Called on all phases at every step
704 : void checkMyDetectors(void);
705 :
706 : /// @brief Clear My Detectors. Called on all phases at every step
707 : void clearMyDetectors(void);
708 :
709 : // Need-to-know Phase Settings
710 : int phaseName;
711 : bool isAtBarrier;
712 : bool isGreenRest;
713 : int barrierNum;
714 : bool coordinatePhase;
715 : bool minRecall;
716 : bool maxRecall;
717 : bool fixForceOff;
718 : int ringNum;
719 :
720 : /// @brief store the last detect check for traci purposes
721 : bool lastDetectActive;
722 :
723 : /// @brief a count down timer to track green rest transition time
724 : SUMOTime greenRestTimer;
725 : SUMOTime greatestStartTime;
726 : /// @brief stores the force off time in coordinated mode
727 : SUMOTime forceOffTime;
728 :
729 : /// @brief flag to for the supervisory controller to denote whether phase is ready to switch or not.
730 : bool readyToSwitch;
731 :
732 : /**
733 : * @brief Get the Transition Time
734 : *
735 : * @param controller
736 : * @return SUMOTime
737 : */
738 : SUMOTime getTransitionTime(NEMALogic* controller);
739 :
740 : /**
741 : * @brief Get the Transition time given
742 : *
743 : * @param controller
744 : * @return SUMOTime
745 : */
746 : inline SUMOTime getTransitionTimeStateless(void) {
747 7085 : return yellow + red;
748 : }
749 :
750 : /// @brief get the prior phase
751 : inline PhasePtr getSequentialPriorPhase(void) {
752 3122 : return sequentialPriorPhase;
753 : }
754 :
755 : /// @brief set the prior phase
756 : inline void setSequentialPriorPhase(PhasePtr priorPhase) {
757 137 : sequentialPriorPhase = priorPhase;
758 224 : }
759 :
760 : /**
761 : * @brief calculate a vector of potention next phases
762 : *
763 : * @param controller
764 : * @return std::vector<PhaseTransitionLogic*>
765 : */
766 : std::vector<PhaseTransitionLogic*> trySwitch(NEMALogic* controller);
767 :
768 : /**
769 : * @brief return the PhaseTransitionLogic matching the toPhase
770 : *
771 : * @param toPhase a integer representing the target phase
772 : * @return PhaseTransitionLogic*
773 : */
774 : PhaseTransitionLogic* getTransition(int toPhase);
775 :
776 : /// @brief Return the ryg light string for the phase
777 : char getNEMAChar(int i);
778 :
779 : /// @brief accessory function to recalculate timing
780 : void recalculateTiming(void);
781 :
782 : /// @brief Force Enter. This Should only be called at initialization time
783 : inline void forceEnter(NEMALogic* controller) {
784 101 : enter(controller, sequentialPriorPhase);
785 68 : }
786 :
787 : /// @brief Return whether or not the phase index is controlled by me
788 : inline bool controlledIndex(int i) {
789 : return std::count(myPhaseStringInds.begin(), myPhaseStringInds.end(), i) > 0;
790 : }
791 :
792 : /// @name Basic Phase Timing Parameters
793 : /// @{
794 : SUMOTime yellow;
795 : SUMOTime red;
796 : SUMOTime minDuration;
797 : SUMOTime maxDuration;
798 : SUMOTime nextMaxDuration;
799 : SUMOTime vehExt;
800 : /// @}
801 :
802 : /// @brief public method to set whether phase is active or not
803 : inline void cleanupExit(void) {
804 6255 : transitionActive = false;
805 6255 : readyToSwitch = false;
806 6255 : myLightState = LightState::Red;
807 : }
808 :
809 : /// @brief simple internal check to see if done okay to transition
810 : inline bool okay2ForceSwitch(NEMALogic* controller) {
811 5888 : return readyToSwitch && !transitionActive && (getTransitionTime(controller) <= TIME2STEPS(0));
812 : }
813 :
814 : private:
815 : /// @brief A reference to the core phase of which NEMAPhase wraps
816 : MSPhaseDefinition* myCorePhase = nullptr;
817 :
818 : /// @name store references to myself, the last phase I was in, and the sequentially next phase
819 : /// @{
820 : PhasePtr myInstance = nullptr;
821 : PhasePtr myLastPhaseInstance = nullptr;
822 : PhasePtr sequentialPriorPhase = nullptr;
823 : /// @}
824 :
825 : // Phase Knowledge Space
826 : LightState myLightState;
827 : PhaseDetectorInfo myDetectorInfo;
828 :
829 : /// @name Timing Parameters
830 : /// @{
831 : SUMOTime maxGreenDynamic;
832 : SUMOTime myStartTime;
833 : SUMOTime myExpectedDuration;
834 : SUMOTime myLastEnd;
835 : /// @}
836 :
837 : /// @name Light String Parameters
838 : /// @{
839 : IntVector myPhaseStringInds;
840 : std::string myGreenString;
841 : std::string myYellowString;
842 : std::string myRedString;
843 : /// }
844 :
845 : /**
846 : * @brief Applies the vehicle extension timer if appropriate
847 : *
848 : * @param duration the current phase duration
849 : * @return SUMOTime
850 : */
851 : SUMOTime calcVehicleExtension(SUMOTime duration);
852 :
853 : /// @brief stores a sorted list of potential transitions
854 : std::vector<PhaseTransitionLogic*> myTransitions;
855 :
856 : /**
857 : * @brief handles entry to the phase during simulation
858 : * Sets the color to green and determines maximum duration
859 : *
860 : * @param controller a reference to the controller
861 : * @param lastPhase a reference to the last phase
862 : */
863 : void enter(NEMALogic* controller, PhasePtr lastPhase);
864 :
865 : /**
866 : * @brief this function replaces getNEMAStates calculation at every call
867 : * It sets my myGreenString, myYellowString, and myRedString on class creation
868 : */
869 : void setMyNEMAStates(void);
870 :
871 : /// @brief variable to store whether a transition is active or not
872 : bool transitionActive;
873 :
874 : /// @brief pointer to save the last transition
875 : PhaseTransitionLogic* lastTransitionDecision;
876 :
877 : };
878 :
879 :
880 : /**
881 : * @class PhaseTransitionLogic
882 : * @brief This class handles the transition logic between two phases
883 : *
884 : * This is intended to be extensible in the future. Each phase stores some
885 : * number of PhaseTransitionLogics, equal to the number of non-zero phases in a ring
886 : *
887 : */
888 : class PhaseTransitionLogic {
889 : public:
890 : /// @brief Typedef for commonly used phase pointer
891 : typedef NEMAPhase* PhasePtr;
892 :
893 : /**
894 : * @brief Construct a new Phase Transition Logic object
895 : *
896 : * @param fromPhase the phase who "owns" this transition
897 : * @param toPhase the phase to which I represent a transition to
898 : */
899 : PhaseTransitionLogic(
900 : PhasePtr fromPhase,
901 : PhasePtr toPhase
902 : );
903 :
904 : /**
905 : * @brief This function is the main PhaseTransitionLogic function
906 : * It is called by the fromPhase to check if a transition to the toPhase is acceptable
907 : *
908 : * @param controller a reference to the controller
909 : * @return true
910 : * @return false
911 : */
912 : bool okay(NEMALogic* controller);
913 :
914 : /**
915 : * @brief return the ring distance that this transition represents
916 : *
917 : * @param otherTrans the other PhaseTransitionLogic
918 : * @return int
919 : */
920 : int getDistance(PhaseTransitionLogic* otherTrans);
921 : /// @brief set the transition distance
922 : inline void setDistance(int d) {
923 1022 : distance = d;
924 : }
925 : int distance;
926 :
927 : /// @brief deconstructor
928 977 : ~PhaseTransitionLogic() {};
929 :
930 : /// @brief get the to phase
931 : inline PhasePtr getToPhase(void) const {
932 168413 : return toPhase;
933 : }
934 :
935 : /// @brief get the from phase
936 : inline PhasePtr getFromPhase(void) const {
937 356 : return fromPhase;
938 : }
939 :
940 : private:
941 : PhasePtr fromPhase;
942 : PhasePtr toPhase;
943 :
944 : /// @brief build the transition logic based on the from and to phase
945 : void buildLogic(void);
946 :
947 : /**
948 : * @brief If the fromPhase is at a barrier, then this function
949 : * will be called to check whether the transition is valid
950 : *
951 : * @param controller a reference to the controller
952 : * @return true
953 : * @return false
954 : */
955 : bool fromBarrier(NEMALogic* controller);
956 :
957 : /**
958 : * @brief if the fromPhase is a coordinated phase, then
959 : * this logic will be checked
960 : *
961 : * @param controller
962 : * @return true
963 : * @return false
964 : */
965 : bool fromCoord(NEMALogic* controller);
966 :
967 : /**
968 : * @brief this represents the bare minimum logic,
969 : * that the toPhase has an active detector and that the fromPhase is ready to switch
970 : *
971 : * @param controller
972 : * @return true
973 : * @return false
974 : */
975 : bool freeBase(NEMALogic* controller);
976 :
977 : /**
978 : * @brief represents the bare minimum coordinate mode logic.
979 : * Requires that the toPhase can fit its minimum green time before the force off
980 : *
981 : * @param controller
982 : * @return true
983 : * @return false
984 : */
985 : bool coordBase(NEMALogic* controller);
986 : };
|