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