View Javadoc
1   package org.opentrafficsim.kpi.sampling;
2   
3   import java.util.HashMap;
4   import java.util.HashSet;
5   import java.util.LinkedHashMap;
6   import java.util.LinkedHashSet;
7   import java.util.Map;
8   import java.util.Set;
9   
10  import org.djunits.value.vdouble.scalar.Acceleration;
11  import org.djunits.value.vdouble.scalar.Length;
12  import org.djunits.value.vdouble.scalar.Speed;
13  import org.djunits.value.vdouble.scalar.Time;
14  import org.opentrafficsim.kpi.interfaces.GtuDataInterface;
15  import org.opentrafficsim.kpi.sampling.data.ExtendedDataType;
16  import org.opentrafficsim.kpi.sampling.meta.MetaData;
17  import org.opentrafficsim.kpi.sampling.meta.MetaDataType;
18  
19  import nl.tudelft.simulation.language.Throw;
20  
21  /**
22   * Sampler is the highest level organizer for sampling.
23   * <p>
24   * Copyright (c) 2013-2016 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/node/13">OpenTrafficSim License</a>.
26   * <p>
27   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Sep 22, 2016 <br>
28   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
29   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
30   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
31   */
32  public abstract class Sampler
33  {
34  
35      /** Map with all sampling data. */
36      private final Map<KpiLaneDirection, TrajectoryGroup> trajectories = new LinkedHashMap<>();
37  
38      /** End times of active samplings. */
39      private final Map<KpiLaneDirection, Time> endTimes = new LinkedHashMap<>();
40  
41      /** Registration of current trajectories of each GTU per lane. */
42      private final Map<String, Map<KpiLaneDirection, Trajectory>> trajectoryPerGtu = new LinkedHashMap<>();
43  
44      /** Registration of included extended data types. */
45      private final Set<ExtendedDataType<?>> extendedDataTypes = new LinkedHashSet<>();
46  
47      /** Set of registered meta data types. */
48      private Set<MetaDataType<?>> registeredMetaDataTypes = new LinkedHashSet<>();
49  
50      /**
51       * @param spaceTimeRegion space-time region
52       * @throws IllegalStateException if data is not available from the requested start time
53       */
54      public final void registerSpaceTimeRegion(final SpaceTimeRegion spaceTimeRegion)
55      {
56          Throw.whenNull(spaceTimeRegion, "SpaceTimeRegion may not be null.");
57          Time firstPossibleDataTime;
58          if (this.trajectories.containsKey(spaceTimeRegion.getLaneDirection()))
59          {
60              firstPossibleDataTime = this.trajectories.get(spaceTimeRegion.getLaneDirection()).getStartTime();
61          }
62          else
63          {
64              firstPossibleDataTime = now();
65          }
66          Throw.when(spaceTimeRegion.getStartTime().lt(firstPossibleDataTime), IllegalStateException.class,
67                  "Space time region with start time %s is defined while data is available from %s onwards.",
68                  spaceTimeRegion.getStartTime(), firstPossibleDataTime);
69          if (this.trajectories.containsKey(spaceTimeRegion.getLaneDirection()))
70          {
71              this.endTimes.put(spaceTimeRegion.getLaneDirection(),
72                      Time.max(this.endTimes.get(spaceTimeRegion.getLaneDirection()), spaceTimeRegion.getEndTime()));
73          }
74          else
75          {
76              this.endTimes.put(spaceTimeRegion.getLaneDirection(), spaceTimeRegion.getEndTime());
77              scheduleStartRecording(spaceTimeRegion.getStartTime(), spaceTimeRegion.getLaneDirection());
78          }
79          scheduleStopRecording(this.endTimes.get(spaceTimeRegion.getLaneDirection()), spaceTimeRegion.getLaneDirection());
80      }
81  
82      /**
83       * Returns the current simulation time.
84       * @return current simulation time
85       */
86      public abstract Time now();
87  
88      /**
89       * Schedules the start of recording for a given lane-direction.
90       * @param time time to start recording
91       * @param kpiLaneDirection lane-direction to start recording
92       */
93      public abstract void scheduleStartRecording(final Time time, final KpiLaneDirection kpiLaneDirection);
94  
95      /**
96       * Schedules the stop of recording for a given lane-direction.
97       * @param time time to stop recording
98       * @param kpiLaneDirection lane-direction to stop recording
99       */
100     public abstract void scheduleStopRecording(final Time time, final KpiLaneDirection kpiLaneDirection);
101 
102     /**
103      * Registers meta data types that will be stored with the trajectories.
104      * @param metaDataTypes meta data types to register
105      */
106     public final void registerMetaDataTypes(final Set<MetaDataType<?>> metaDataTypes)
107     {
108         Throw.whenNull(metaDataTypes, "MetaDataTypes may not be null.");
109         this.registeredMetaDataTypes.addAll(metaDataTypes);
110     }
111 
112     /**
113      * Registers extended data type that will be stored with the trajectories.
114      * @param extendedDataType extended data type to register
115      */
116     public final void registerExtendedDataType(final ExtendedDataType<?> extendedDataType)
117     {
118         Throw.whenNull(extendedDataType, "ExtendedDataType may not be null.");
119         this.extendedDataTypes.add(extendedDataType);
120     }
121 
122     /**
123      * Whether this sampler has the given extended data type registered to it.
124      * @param extendedDataType extended data type
125      * @return whether this sampler has the given extended data type registered to it
126      */
127     public boolean contains(final ExtendedDataType<?> extendedDataType)
128     {
129         return this.extendedDataTypes.contains(extendedDataType);
130     }
131 
132     /**
133      * Start recording at the given time (which should be the current time) on the given lane direction.
134      * @param kpiLaneDirection lane direction
135      */
136     public final void startRecording(final KpiLaneDirection kpiLaneDirection)
137     {
138         Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
139         if (this.trajectories.containsKey(kpiLaneDirection))
140         {
141             return;
142         }
143         this.trajectories.put(kpiLaneDirection, new TrajectoryGroup(now(), kpiLaneDirection));
144         initRecording(kpiLaneDirection);
145     }
146 
147     /**
148      * Adds listeners to start recording.
149      * @param kpiLaneDirection lane direction to initialize recording for
150      */
151     public abstract void initRecording(final KpiLaneDirection kpiLaneDirection);
152 
153     /**
154      * Stop recording at given lane direction.
155      * @param kpiLaneDirection lane direction
156      */
157     public final void stopRecording(final KpiLaneDirection kpiLaneDirection)
158     {
159         Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
160         if (!this.trajectories.containsKey(kpiLaneDirection) || this.endTimes.get(kpiLaneDirection).gt(now()))
161         {
162             return;
163         }
164         finalizeRecording(kpiLaneDirection);
165     }
166 
167     /**
168      * Remove listeners to stop recording.
169      * @param kpiLaneDirection lane direction to finalize recording for
170      */
171     public abstract void finalizeRecording(final KpiLaneDirection kpiLaneDirection);
172 
173     /**
174      * Creates a trajectory with the current snapshot of a GTU.
175      * @param kpiLaneDirection lane direction the gtu is at
176      * @param position position of the gtu on the lane
177      * @param speed speed of the gtu
178      * @param acceleration acceleration of the gtu
179      * @param time current time
180      * @param gtu gtu
181      */
182     public final void processGtuAddEvent(final KpiLaneDirection kpiLaneDirection, final Length position,
183             final Speed speed, final Acceleration acceleration, final Time time, final GtuDataInterface gtu)
184     {
185         Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
186         Throw.whenNull(position, "Position may not be null.");
187         Throw.whenNull(speed, "Speed may not be null.");
188         Throw.whenNull(acceleration, "Acceleration may not be null.");
189         Throw.whenNull(time, "Time may not be null.");
190         Throw.whenNull(gtu, "GtuDataInterface may not be null.");
191         if (kpiLaneDirection.getLaneData().getLength().lt(position))
192         {
193             // ignore event if beyond lane length (may happen during lane change)
194             return;
195         }
196         String gtuId = gtu.getId();
197         Trajectory trajectory = new Trajectory(gtu, makeMetaData(gtu), this.extendedDataTypes, kpiLaneDirection);
198         if (!this.trajectoryPerGtu.containsKey(gtuId))
199         {
200             Map<KpiLaneDirection, Trajectory> map = new LinkedHashMap<>();
201             this.trajectoryPerGtu.put(gtuId, map);
202         }
203         this.trajectoryPerGtu.get(gtuId).put(kpiLaneDirection, trajectory);
204         this.trajectories.get(kpiLaneDirection).addTrajectory(trajectory);
205         processGtuMoveEvent(kpiLaneDirection, position, speed, acceleration, time, gtu);
206     }
207 
208     /**
209      * Adds a new snapshot of a GTU to its recording trajectory, if recorded. This method may be invoked on GTU that are not
210      * being recorded; the event will then be ignored.
211      * @param kpiLaneDirection lane direction the gtu is at
212      * @param position position of the gtu on the lane
213      * @param speed speed of the gtu
214      * @param acceleration acceleration of the gtu
215      * @param time current time
216      * @param gtu gtu
217      */
218     public final void processGtuMoveEvent(final KpiLaneDirection kpiLaneDirection, final Length position,
219             final Speed speed, final Acceleration acceleration, final Time time, final GtuDataInterface gtu)
220     {
221         Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
222         Throw.whenNull(position, "Position may not be null.");
223         Throw.whenNull(speed, "Speed may not be null.");
224         Throw.whenNull(acceleration, "Acceleration may not be null.");
225         Throw.whenNull(time, "Time may not be null.");
226         Throw.whenNull(gtu, "GtuDataInterface may not be null.");
227         String gtuId = gtu.getId();
228         if (this.trajectoryPerGtu.containsKey(gtuId) && this.trajectoryPerGtu.get(gtuId).containsKey(kpiLaneDirection))
229         {
230             this.trajectoryPerGtu.get(gtuId).get(kpiLaneDirection).add(position, speed, acceleration, time, gtu);
231         }
232     }
233 
234     /**
235      * Finalizes a trajectory with the current snapshot of a GTU.
236      * @param kpiLaneDirection lane direction the gtu is at
237      * @param position position of the gtu on the lane
238      * @param speed speed of the gtu
239      * @param acceleration acceleration of the gtu
240      * @param time current time
241      * @param gtu gtu
242      */
243     public final void processGtuRemoveEvent(final KpiLaneDirection kpiLaneDirection, final Length position,
244             final Speed speed, final Acceleration acceleration, final Time time, final GtuDataInterface gtu)
245     {
246         processGtuMoveEvent(kpiLaneDirection, position, speed, acceleration, time, gtu);
247         processGtuRemoveEvent(kpiLaneDirection, gtu);
248     }
249 
250     /**
251      * Finalizes a trajectory.
252      * @param kpiLaneDirection lane direction the gtu is at
253      * @param gtu gtu
254      */
255     public final void processGtuRemoveEvent(final KpiLaneDirection kpiLaneDirection, final GtuDataInterface gtu)
256     {
257         Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
258         Throw.whenNull(gtu, "GtuDataInterface may not be null.");
259         String gtuId = gtu.getId();
260         if (this.trajectoryPerGtu.containsKey(gtuId))
261         {
262             this.trajectoryPerGtu.get(gtuId).remove(kpiLaneDirection);
263             if (this.trajectoryPerGtu.get(gtuId).isEmpty())
264             {
265                 this.trajectoryPerGtu.remove(gtuId);
266             }
267         }
268     }
269 
270     /**
271      * @param gtu gtu to return meta data for
272      * @param <T> underlying type of a meta data type
273      * @return meta data for the given gtu
274      */
275     @SuppressWarnings("unchecked")
276     private <T> MetaData makeMetaData(final GtuDataInterface gtu)
277     {
278         MetaData metaData = new MetaData();
279         for (MetaDataType<?> metaDataType : this.registeredMetaDataTypes)
280         {
281             T value = (T) metaDataType.getValue(gtu);
282             if (value != null)
283             {
284                 metaData.put((MetaDataType<T>) metaDataType, value);
285             }
286         }
287         return metaData;
288     }
289 
290     /**
291      * Returns whether there is data for the give lane direction.
292      * @param kpiLaneDirection lane direction
293      * @return whether there is data for the give lane direction
294      */
295     public final boolean contains(final KpiLaneDirection kpiLaneDirection)
296     {
297         return this.trajectories.containsKey(kpiLaneDirection);
298     }
299 
300     /**
301      * Returns the trajectory group of given lane direction.
302      * @param kpiLaneDirection lane direction
303      * @return trajectory group of given lane direction, {@code null} if none
304      */
305     public final TrajectoryGroup getTrajectoryGroup(final KpiLaneDirection kpiLaneDirection)
306     {
307         return this.trajectories.get(kpiLaneDirection);
308     }
309 
310     /** {@inheritDoc} */
311     @Override
312     public int hashCode()
313     {
314         final int prime = 31;
315         int result = 1;
316         result = prime * result + ((this.endTimes == null) ? 0 : this.endTimes.hashCode());
317         result = prime * result + ((this.extendedDataTypes == null) ? 0 : this.extendedDataTypes.hashCode());
318         result = prime * result + ((this.registeredMetaDataTypes == null) ? 0 : this.registeredMetaDataTypes.hashCode());
319         result = prime * result + ((this.trajectories == null) ? 0 : this.trajectories.hashCode());
320         result = prime * result + ((this.trajectoryPerGtu == null) ? 0 : this.trajectoryPerGtu.hashCode());
321         return result;
322     }
323 
324     /** {@inheritDoc} */
325     @Override
326     public boolean equals(final Object obj)
327     {
328         if (this == obj)
329         {
330             return true;
331         }
332         if (obj == null)
333         {
334             return false;
335         }
336         if (getClass() != obj.getClass())
337         {
338             return false;
339         }
340         Sampler other = (Sampler) obj;
341         if (this.endTimes == null)
342         {
343             if (other.endTimes != null)
344             {
345                 return false;
346             }
347         }
348         else if (!this.endTimes.equals(other.endTimes))
349         {
350             return false;
351         }
352         if (this.extendedDataTypes == null)
353         {
354             if (other.extendedDataTypes != null)
355             {
356                 return false;
357             }
358         }
359         else if (!this.extendedDataTypes.equals(other.extendedDataTypes))
360         {
361             return false;
362         }
363         if (this.registeredMetaDataTypes == null)
364         {
365             if (other.registeredMetaDataTypes != null)
366             {
367                 return false;
368             }
369         }
370         else if (!this.registeredMetaDataTypes.equals(other.registeredMetaDataTypes))
371         {
372             return false;
373         }
374         if (this.trajectories == null)
375         {
376             if (other.trajectories != null)
377             {
378                 return false;
379             }
380         }
381         else if (!this.trajectories.equals(other.trajectories))
382         {
383             return false;
384         }
385         if (this.trajectoryPerGtu == null)
386         {
387             if (other.trajectoryPerGtu != null)
388             {
389                 return false;
390             }
391         }
392         else if (!this.trajectoryPerGtu.equals(other.trajectoryPerGtu))
393         {
394             return false;
395         }
396         return true;
397     }
398 
399 }