View Javadoc
1   package org.opentrafficsim.road.network.lane.object.trafficlight;
2   
3   import java.util.LinkedHashSet;
4   import java.util.Set;
5   
6   import org.djunits.unit.LengthUnit;
7   import org.djunits.value.vdouble.scalar.Length;
8   import org.djutils.event.EventType;
9   import org.djutils.exceptions.Throw;
10  import org.djutils.metadata.MetaData;
11  import org.djutils.metadata.ObjectDescriptor;
12  import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
13  import org.opentrafficsim.core.gtu.GtuType;
14  import org.opentrafficsim.core.network.Link;
15  import org.opentrafficsim.core.network.NetworkException;
16  import org.opentrafficsim.core.network.Node;
17  import org.opentrafficsim.core.network.route.Route;
18  import org.opentrafficsim.road.network.lane.Lane;
19  import org.opentrafficsim.road.network.lane.object.AbstractLaneBasedObject;
20  import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
21  
22  /**
23   * Standard implementation of a traffic light.
24   * <p>
25   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
26   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
27   * </p>
28   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
29   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
30   */
31  public class TrafficLight extends AbstractLaneBasedObject
32  {
33      /** */
34      private static final long serialVersionUID = 20230216L;
35  
36      /** The color of the traffic light. */
37      private TrafficLightColor trafficLightColor;
38  
39      /** The simulator to schedule events on. */
40      private final OtsSimulatorInterface simulator;
41  
42      /** Node that we can turn to through red. */
43      private Set<Node> turnOnRed = null;
44  
45      /** Default elevation of a traffic light (above zero; don't use this for lanes at non-zero elevation). */
46      public static final Length DEFAULT_TRAFFICLIGHT_ELEVATION = new Length(1, LengthUnit.METER);
47  
48      /**
49       * The <b>timed</b> event type for pub/sub indicating the change of color of a traffic light. <br>
50       * Payload: Object[] {String trafficLightId, TrafficLight trafficLight, TrafficLightColor newColor}
51       */
52      public static final EventType TRAFFICLIGHT_CHANGE_EVENT = new EventType("TRAFFICLIGHT.CHANGE",
53              new MetaData("Traffic light changed", "Color of traffic light has changed",
54                      new ObjectDescriptor("Traffic light id", "Id of the traffic light", String.class),
55                      new ObjectDescriptor("Traffic light", "The traffic light itself", TrafficLight.class),
56                      new ObjectDescriptor("Traffic light color", "New traffic light color", TrafficLightColor.class)));
57  
58      /**
59       * Construct an AbstractTrafficLight with specified elevation.
60       * @param id String; traffic light id
61       * @param lane Lane; lane where the traffic light is located
62       * @param longitudinalPosition Length; position of the traffic light on the lane, in the design direction
63       * @param simulator OtsSimulatorInterface; the simulator for animation and timed events
64       * @param height Length; the elevation of the traffic light
65       * @throws NetworkException on failure to place the object
66       */
67      public TrafficLight(final String id, final Lane lane, final Length longitudinalPosition,
68              final OtsSimulatorInterface simulator, final Length height) throws NetworkException
69      {
70          super(id, lane, longitudinalPosition, LaneBasedObject.makeGeometry(lane, longitudinalPosition), height);
71  
72          Throw.whenNull(simulator, "Simulator may not be null");
73          this.simulator = simulator;
74          this.trafficLightColor = TrafficLightColor.RED;
75  
76          init();
77      }
78  
79      /**
80       * Construct an AbstractTrafficLight at default elevation (use only on roads at elevation 0).
81       * @param id String; traffic light id
82       * @param lane Lane; lane where the traffic light is located
83       * @param longitudinalPosition Length; position of the traffic light on the lane, in the design direction
84       * @param simulator OtsSimulatorInterface; the simulator for animation and timed events
85       * @throws NetworkException on failure to place the object
86       */
87      public TrafficLight(final String id, final Lane lane, final Length longitudinalPosition,
88              final OtsSimulatorInterface simulator) throws NetworkException
89      {
90          this(id, lane, longitudinalPosition, simulator, DEFAULT_TRAFFICLIGHT_ELEVATION);
91      }
92  
93      /**
94       * Get the current traffic light color.
95       * @return TrafficLightColor; current traffic light color.
96       */
97      public final TrafficLightColor getTrafficLightColor()
98      {
99          return this.trafficLightColor;
100     }
101 
102     /**
103      * Set the new traffic light color.
104      * @param trafficLightColor TrafficLightColor; set the trafficLightColor
105      */
106     public final void setTrafficLightColor(final TrafficLightColor trafficLightColor)
107     {
108         this.trafficLightColor = trafficLightColor;
109         fireTimedEvent(TRAFFICLIGHT_CHANGE_EVENT, new Object[] {getId(), this, trafficLightColor},
110                 this.simulator.getSimulatorTime());
111     }
112 
113     /**
114      * Add node GTUs may turn to through red.
115      * @param node Node; node.
116      */
117     public void addTurnOnRed(final Node node)
118     {
119         if (this.turnOnRed == null)
120         {
121             this.turnOnRed = new LinkedHashSet<>();
122         }
123         this.turnOnRed.add(node);
124     }
125 
126     /**
127      * Whether a GTU can turn on red.
128      * @param route Route; route.
129      * @param gtuType GtuType; GTU type.
130      * @return boolean; whether a GTU can turn on red.
131      */
132     public boolean canTurnOnRed(final Route route, final GtuType gtuType)
133     {
134         if (this.turnOnRed == null)
135         {
136             return false;
137         }
138         try
139         {
140             // move until next link split (link will be link before split)
141             Link link = this.getLane().getLink();
142             Set<Link> next = link.getEndNode().nextLinks(gtuType, link);
143             while (next.size() == 1)
144             {
145                 link = next.iterator().next();
146                 next = link.getEndNode().nextLinks(gtuType, link);
147             }
148             // check if next node in the route, beyond the split, is ok to turn to on red
149             Node endNode = link.getEndNode();
150             int nodeIndex = route.indexOf(endNode);
151             if (nodeIndex < 0 || nodeIndex == route.size() - 1)
152             {
153                 return false;
154             }
155             return this.turnOnRed.contains(route.getNode(nodeIndex + 1));
156         }
157         catch (NetworkException ex)
158         {
159             // we explicitly use the previous link which should be connected, and check the number of nodes in the route
160             throw new RuntimeException(ex);
161         }
162     }
163 
164     /** {@inheritDoc} */
165     @Override
166     @SuppressWarnings("checkstyle:designforextension")
167     public String toString()
168     {
169         return "SimpleTrafficLight [trafficLightColor=" + getTrafficLightColor() + "]";
170     }
171 
172 }