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