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