View Javadoc
1   package org.opentrafficsim.road.network.lane.object.sensor;
2   
3   import org.djunits.value.vdouble.scalar.Length;
4   import org.djutils.exceptions.Throw;
5   import org.opentrafficsim.core.compatibility.Compatible;
6   import org.opentrafficsim.core.geometry.OTSGeometryException;
7   import org.opentrafficsim.core.geometry.OTSLine3D;
8   import org.opentrafficsim.core.geometry.OTSPoint3D;
9   import org.opentrafficsim.core.gtu.GTUDirectionality;
10  import org.opentrafficsim.core.gtu.GTUType;
11  import org.opentrafficsim.core.gtu.RelativePosition;
12  import org.opentrafficsim.core.network.NetworkException;
13  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
14  import org.opentrafficsim.road.network.lane.CrossSectionElement;
15  import org.opentrafficsim.road.network.lane.Lane;
16  import org.opentrafficsim.road.network.lane.object.AbstractLaneBasedObject;
17  
18  import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
19  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
20  import nl.tudelft.simulation.language.d3.DirectedPoint;
21  
22  /**
23   * <p>
24   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
25   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
26   * <p>
27   * $LastChangedDate: 2015-09-14 01:33:02 +0200 (Mon, 14 Sep 2015) $, @version $Revision: 1401 $, by $Author: averbraeck $,
28   * initial version Dec 31, 2014 <br>
29   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
30   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
31   */
32  public abstract class AbstractSensor extends AbstractLaneBasedObject implements SingleSensor
33  {
34      /** */
35      private static final long serialVersionUID = 20141231L;
36  
37      /** The relative position of the vehicle that triggers the sensor. */
38      private final RelativePosition.TYPE positionType;
39  
40      /** The simulator for being able to generate an animation. */
41      private final DEVSSimulatorInterface.TimeDoubleUnit simulator;
42  
43      /** The GTU types and driving directions that this sensor will trigger on. */
44      private final Compatible detectedGTUTypes;
45  
46      /**
47       * Create a sensor on a lane at a position on that lane.
48       * @param id String; the id of the sensor.
49       * @param lane Lane; the lane for which this is a sensor.
50       * @param longitudinalPosition Length; the position (between 0.0 and the length of the Lane) of the sensor on the design
51       *            line of the lane.
52       * @param positionType RelativePosition.TYPE; the relative position type (e.g., FRONT, BACK) of the vehicle that triggers
53       *            the sensor.
54       * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; the simulator (needed to generate the animation).
55       * @param geometry OTSLine3D; the geometry of the object, which provides its location and bounds as well
56       * @param elevation Length; elevation of the sensor
57       * @param detectedGTUTypes Compatible; The GTU types will trigger this sensor
58       * @throws NetworkException when the position on the lane is out of bounds
59       */
60      @SuppressWarnings("checkstyle:parameternumber")
61      public AbstractSensor(final String id, final Lane lane, final Length longitudinalPosition,
62              final RelativePosition.TYPE positionType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
63              final OTSLine3D geometry, final Length elevation, final Compatible detectedGTUTypes) throws NetworkException
64      {
65          super(id, lane, longitudinalPosition, geometry, elevation);
66          Throw.when(simulator == null, NullPointerException.class, "simulator is null");
67          Throw.when(positionType == null, NullPointerException.class, "positionType is null");
68          Throw.when(id == null, NullPointerException.class, "id is null");
69          this.positionType = positionType;
70          this.simulator = simulator;
71          this.detectedGTUTypes = detectedGTUTypes;
72          getLane().addSensor(this); // Implements OTS-218
73      }
74  
75      /**
76       * Create a sensor on a lane at a position on that lane at elevation <code>Sensor.DEFAULT_SENSOR_ELEVATION</code>.
77       * @param id String; the id of the sensor.
78       * @param lane Lane; the lane for which this is a sensor.
79       * @param longitudinalPosition Length; the position (between 0.0 and the length of the Lane) of the sensor on the design
80       *            line of the lane.
81       * @param positionType RelativePosition.TYPE; the relative position type (e.g., FRONT, BACK) of the vehicle that triggers
82       *            the sensor.
83       * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; the simulator (needed to generate the animation).
84       * @param geometry OTSLine3D; the geometry of the object, which provides its location and bounds as well
85       * @param detectedGTUTypes Compatible; The GTU types will trigger this sensor
86       * @throws NetworkException when the position on the lane is out of bounds
87       */
88      public AbstractSensor(final String id, final Lane lane, final Length longitudinalPosition,
89              final RelativePosition.TYPE positionType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
90              final OTSLine3D geometry, final Compatible detectedGTUTypes) throws NetworkException
91      {
92          this(id, lane, longitudinalPosition, positionType, simulator, geometry, DEFAULT_SENSOR_ELEVATION, detectedGTUTypes);
93      }
94  
95      /**
96       * Create a new AbstractSensor on a lane at a position on that lane at elevation
97       * <code>Sensor.DEFAULT_SENSOR_ELEVATION</code> and default geometry.
98       * @param id String; the id of the new AbstractSensor
99       * @param lane Lane; the lane on which the new AbstractSensor is positioned
100      * @param longitudinalPosition Length; the position (between 0.0 and the length of the Lane) of the sensor on the design
101      *            line of the lane
102      * @param positionType RelativePosition.TYPE; the relative position type (e.g., FRONT, BACK) of the vehicle that triggers
103      *            the sensor.
104      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; the simulator (needed to generate the animation).
105      * @param detectedGTUTypes Compatible; The GTU types will trigger this sensor
106      * @throws NetworkException when the position on the lane is out of bounds
107      */
108     public AbstractSensor(final String id, final Lane lane, final Length longitudinalPosition,
109             final RelativePosition.TYPE positionType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
110             final Compatible detectedGTUTypes) throws NetworkException
111     {
112         this(id, lane, longitudinalPosition, positionType, simulator, makeGeometry(lane, longitudinalPosition),
113                 detectedGTUTypes);
114     }
115 
116     /**
117      * Make a geometry perpendicular to the center line of the lane with a length of 90% of the width of the lane.
118      * @param lane Lane; the lane for which to make a perpendicular geometry
119      * @param longitudinalPosition Length; the position on the lane
120      * @return an OTSLine3D that describes the line
121      * @throws NetworkException in case the sensor point on the center line of the lane cannot be found
122      */
123     private static OTSLine3D makeGeometry(final Lane lane, final Length longitudinalPosition) throws NetworkException
124     {
125         try
126         {
127             double w45 = lane.getWidth(longitudinalPosition).si * 0.45;
128             DirectedPoint c = lane.getCenterLine().getLocation(longitudinalPosition);
129             double a = c.getRotZ();
130             OTSPoint3D p1 = new OTSPoint3D(c.x + w45 * Math.cos(a + Math.PI / 2), c.y - w45 * Math.sin(a + Math.PI / 2), c.z);
131             OTSPoint3D p2 = new OTSPoint3D(c.x - w45 * Math.cos(a + Math.PI / 2), c.y + w45 * Math.sin(a + Math.PI / 2), c.z);
132             return new OTSLine3D(p1, p2);
133         }
134         catch (OTSGeometryException exception)
135         {
136             throw new NetworkException(exception);
137         }
138     }
139 
140     /** {@inheritDoc} */
141     @Override
142     public final void trigger(final LaneBasedGTU gtu)
143     {
144         fireTimedEvent(SingleSensor.SENSOR_TRIGGER_EVENT, new Object[] {getId(), this, gtu, this.positionType},
145                 getSimulator().getSimulatorTime());
146         triggerResponse(gtu);
147     }
148 
149     /**
150      * Implementation of the response to a trigger of this sensor by a GTU.
151      * @param gtu LaneBasedGTU; the lane based GTU that triggered this sensor.
152      */
153     protected abstract void triggerResponse(final LaneBasedGTU gtu);
154 
155     /** {@inheritDoc} */
156     @Override
157     public final RelativePosition.TYPE getPositionType()
158     {
159         return this.positionType;
160     }
161 
162     /** {@inheritDoc} */
163     @Override
164     public final DEVSSimulatorInterface.TimeDoubleUnit getSimulator()
165     {
166         return this.simulator;
167     }
168 
169     /** {@inheritDoc} */
170     @SuppressWarnings("checkstyle:designforextension")
171     @Override
172     public int hashCode()
173     {
174         final int prime = 31;
175         int result = 1;
176         result = prime * result + ((getLane() == null) ? 0 : getLane().hashCode());
177         long temp;
178         temp = Double.doubleToLongBits(getLongitudinalPosition().si);
179         result = prime * result + (int) (temp ^ (temp >>> 32));
180         result = prime * result + ((this.positionType == null) ? 0 : this.positionType.hashCode());
181         return result;
182     }
183 
184     /** {@inheritDoc} */
185     @SuppressWarnings({"checkstyle:needbraces", "checkstyle:designforextension"})
186     @Override
187     public boolean equals(final Object obj)
188     {
189         if (this == obj)
190             return true;
191         if (obj == null)
192             return false;
193         if (getClass() != obj.getClass())
194             return false;
195         AbstractSensor other = (AbstractSensor) obj;
196         if (this.getLane() == null)
197         {
198             if (other.getLane() != null)
199                 return false;
200         }
201         else if (!this.getLane().equals(other.getLane()))
202             return false;
203         if (Double.doubleToLongBits(this.getLongitudinalPosition().si) != Double
204                 .doubleToLongBits(other.getLongitudinalPosition().si))
205             return false;
206         if (this.positionType == null)
207         {
208             if (other.positionType != null)
209                 return false;
210         }
211         else if (!this.positionType.equals(other.positionType))
212             return false;
213         return true;
214     }
215 
216     /** {@inheritDoc} */
217     @SuppressWarnings("checkstyle:designforextension")
218     @Override
219     public int compareTo(final SingleSensor o)
220     {
221         if (this.getLane() != o.getLane())
222         {
223             return this.getLane().hashCode() < o.getLane().hashCode() ? -1 : 1;
224         }
225         if (this.getLongitudinalPosition().si != o.getLongitudinalPosition().si)
226         {
227             return this.getLongitudinalPosition().si < o.getLongitudinalPosition().si ? -1 : 1;
228         }
229         if (!this.positionType.equals(o.getPositionType()))
230         {
231             return this.positionType.hashCode() < o.getPositionType().hashCode() ? -1 : 1;
232         }
233         if (!this.equals(o))
234         {
235             return this.hashCode() < o.hashCode() ? -1 : 1;
236         }
237         return 0;
238     }
239 
240     /** {@inheritDoc} */
241     @Override
242     @SuppressWarnings("checkstyle:designforextension")
243     public String toString()
244     {
245         return "Sensor[" + getId() + "]";
246     }
247 
248     /** {@inheritDoc} */
249     @Override
250     public abstract AbstractSensor clone(CrossSectionElement newCSE, SimulatorInterface.TimeDoubleUnit newSimulator)
251             throws NetworkException;
252 
253     /** {@inheritDoc} */
254     @Override
255     public final boolean isCompatible(final GTUType gtuType, final GTUDirectionality directionality)
256     {
257         return this.detectedGTUTypes.isCompatible(gtuType, directionality);
258     }
259 
260     /**
261      * Retrieve the object that decides if a particular GTU type is detected when passing in a particular direction.
262      * @return Compatible; the object that decides if a particular GTU type is detected when passing in a particular direction
263      */
264     public final Compatible getDetectedGTUTypes()
265     {
266         return this.detectedGTUTypes;
267     }
268 
269 }