View Javadoc
1   package org.opentrafficsim.road.network.control.rampmetering;
2   
3   import java.util.Iterator;
4   import java.util.LinkedHashMap;
5   import java.util.List;
6   import java.util.Map;
7   
8   import org.djunits.value.vdouble.scalar.Duration;
9   import org.djutils.exceptions.Try;
10  import org.opentrafficsim.base.OtsRuntimeException;
11  import org.opentrafficsim.base.logger.Logger;
12  import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
13  import org.opentrafficsim.core.gtu.RelativePosition;
14  import org.opentrafficsim.core.network.NetworkException;
15  import org.opentrafficsim.core.object.DetectorType;
16  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
17  import org.opentrafficsim.road.network.lane.object.detector.LaneDetector;
18  import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
19  import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLightColor;
20  
21  import nl.tudelft.simulation.dsol.SimRuntimeException;
22  import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
23  
24  /**
25   * Controller using a cycle time.
26   * <p>
27   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
28   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
29   * </p>
30   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
31   * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
32   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
33   */
34  public class CycleTimeLightController implements RampMeteringLightController
35  {
36  
37      /** Minimum red duration. */
38      private static final Duration MIN_RED_TIME = Duration.ofSI(2.0);
39  
40      /** Whether the controller is enabled. */
41      private boolean enabled = false;
42  
43      /** Time when red phase was started. */
44      private Map<TrafficLight, Duration> greenStarts = new LinkedHashMap<>();
45  
46      /** Cycle time. */
47      private Duration cTime;
48  
49      /** Simulator. */
50      private final OtsSimulatorInterface simulator;
51  
52      /** Traffic lights. */
53      private final List<TrafficLight> trafficLights;
54  
55      /** Scheduled red event. */
56      private Map<TrafficLight, SimEventInterface<Duration>> redEvents = new LinkedHashMap<>();
57  
58      /** Scheduled green event. */
59      private Map<TrafficLight, SimEventInterface<Duration>> greenEvents = new LinkedHashMap<>();
60  
61      /**
62       * Constructor.
63       * @param simulator simulator
64       * @param trafficLights traffic lights
65       * @param detectorType detector type.
66       */
67      public CycleTimeLightController(final OtsSimulatorInterface simulator, final List<TrafficLight> trafficLights,
68              final DetectorType detectorType)
69      {
70          this.simulator = simulator;
71          this.trafficLights = trafficLights;
72          for (TrafficLight trafficLight : trafficLights)
73          {
74              Try.execute(() -> new RampMeteringDetector(trafficLight, detectorType),
75                      "Unexpected exception while creating a detector with a ramp metering traffic light.");
76              this.greenStarts.put(trafficLight, Duration.NEGATIVE_INFINITY);
77          }
78      }
79  
80      @Override
81      public void disable()
82      {
83          Iterator<TrafficLight> it = this.redEvents.keySet().iterator();
84          while (it.hasNext())
85          {
86              TrafficLight trafficLight = it.next();
87              this.simulator.cancelEvent(this.redEvents.get(trafficLight));
88              it.remove();
89          }
90          it = this.greenEvents.keySet().iterator();
91          while (it.hasNext())
92          {
93              TrafficLight trafficLight = it.next();
94              this.simulator.cancelEvent(this.greenEvents.get(trafficLight));
95              it.remove();
96          }
97          this.enabled = false;
98          for (TrafficLight trafficLight : this.trafficLights)
99          {
100             trafficLight.setTrafficLightColor(TrafficLightColor.GREEN);
101         }
102     }
103 
104     /**
105      * Starts the cycle.
106      * @param cycleTime cycle time
107      */
108     @Override
109     public void enable(final Duration cycleTime)
110     {
111         Logger.ots().info("Traffic light uses " + cycleTime);
112         this.cTime = cycleTime;
113         if (!this.enabled)
114         {
115             this.enabled = true;
116             for (TrafficLight trafficLight : this.trafficLights)
117             {
118                 setGreen(trafficLight);
119             }
120         }
121     }
122 
123     /**
124      * Sets the traffic light to red. Can be scheduled.
125      * @param trafficLight traffic light
126      */
127     protected void setRed(final TrafficLight trafficLight)
128     {
129         this.redEvents.remove(trafficLight);
130         Logger.ots().info("Traffic light set to RED");
131         trafficLight.setTrafficLightColor(TrafficLightColor.RED);
132     }
133 
134     /**
135      * Sets the traffic light to green. Can be scheduled and remembers the green time.
136      * @param trafficLight traffic light
137      */
138     protected void setGreen(final TrafficLight trafficLight)
139     {
140         this.greenEvents.remove(trafficLight);
141         this.greenStarts.put(trafficLight, this.simulator.getSimulatorTime());
142         Logger.ots().info("Traffic light set to GREEN");
143         trafficLight.setTrafficLightColor(TrafficLightColor.GREEN);
144     }
145 
146     /** Ramp metering sensor. */
147     private class RampMeteringDetector extends LaneDetector
148     {
149 
150         /** The traffic light. */
151         private final TrafficLight trafficLight;
152 
153         /**
154          * @param trafficLight traffic light
155          * @param detectorType detector type.
156          * @throws NetworkException when the position on the lane is out of bounds
157          */
158         RampMeteringDetector(final TrafficLight trafficLight, final DetectorType detectorType) throws NetworkException
159         {
160             super(trafficLight.getId() + "_sensor", trafficLight.getLane(), trafficLight.getLongitudinalPosition(),
161                     RelativePosition.FRONT, detectorType);
162             this.trafficLight = trafficLight;
163         }
164 
165         @SuppressWarnings("synthetic-access")
166         @Override
167         protected void triggerResponse(final LaneBasedGtu gtu)
168         {
169             if (CycleTimeLightController.this.enabled && this.trafficLight.getTrafficLightColor().isGreen())
170             {
171                 try
172                 {
173                     // schedule green
174                     Duration minRedTime = CycleTimeLightController.this.simulator.getSimulatorTime().plus(MIN_RED_TIME);
175                     Duration cycleRedTime = CycleTimeLightController.this.greenStarts.get(this.trafficLight)
176                             .plus(CycleTimeLightController.this.cTime);
177                     Duration green;
178                     if (minRedTime.ge(cycleRedTime))
179                     {
180                         Logger.ots().info("Traffic light set to RED");
181                         this.trafficLight.setTrafficLightColor(TrafficLightColor.RED);
182                         green = minRedTime;
183                     }
184                     else
185                     {
186                         Logger.ots().info("Traffic light set to YELLOW (RED over 'MIN_RED_TIME')");
187                         this.trafficLight.setTrafficLightColor(TrafficLightColor.YELLOW);
188                         CycleTimeLightController.this.redEvents.put(this.trafficLight, CycleTimeLightController.this.simulator
189                                 .scheduleEventRel(MIN_RED_TIME, () -> CycleTimeLightController.this.setRed(this.trafficLight)));
190                         green = cycleRedTime;
191                     }
192                     CycleTimeLightController.this.greenEvents.put(this.trafficLight,
193                             CycleTimeLightController.this.simulator.scheduleEventAbs(Duration.ofSI(green.si),
194                                     () -> CycleTimeLightController.this.setGreen(this.trafficLight)));
195                 }
196                 catch (SimRuntimeException exception)
197                 {
198                     throw new OtsRuntimeException(exception);
199                 }
200             }
201         }
202 
203     }
204 
205 }