1 package org.opentrafficsim.kpi.sampling;
2
3 import java.util.LinkedHashMap;
4 import java.util.LinkedHashSet;
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.djutils.exceptions.Throw;
13 import org.opentrafficsim.kpi.interfaces.GtuDataInterface;
14 import org.opentrafficsim.kpi.sampling.data.ExtendedDataType;
15 import org.opentrafficsim.kpi.sampling.meta.FilterDataType;
16 import org.opentrafficsim.kpi.sampling.meta.MetaData;
17
18
19
20
21
22
23
24
25
26
27
28
29
30 public abstract class Sampler<G extends GtuDataInterface>
31 {
32
33
34 private final SamplerData<G> samplerData;
35
36
37 private final Set<ExtendedDataType<?, ?, ?, G>> extendedDataTypes;
38
39
40 private final Set<FilterDataType<?>> filterDataTypes;
41
42
43 private final Map<String, Map<KpiLaneDirection, Trajectory<G>>> trajectoryPerGtu = new LinkedHashMap<>();
44
45
46 private final Map<KpiLaneDirection, Time> endTimes = new LinkedHashMap<>();
47
48
49 private Set<SpaceTimeRegion> spaceTimeRegions = new LinkedHashSet<>();
50
51
52
53
54
55
56 @SuppressWarnings({"unchecked", "rawtypes"})
57 public Sampler(final Set<ExtendedDataType<?, ?, ?, G>> extendedDataTypes, final Set<FilterDataType<?>> filterDataTypes)
58 {
59 this.extendedDataTypes = new LinkedHashSet<>(extendedDataTypes);
60 this.filterDataTypes = new LinkedHashSet<>(filterDataTypes);
61 Set<Column<?>> columns = new LinkedHashSet<>();
62
63 for (ExtendedDataType<?, ?, ?, G> extendedDataType : this.extendedDataTypes)
64 {
65 columns.add(new SimpleColumn(extendedDataType.getId(), extendedDataType.getId(), extendedDataType.getType()));
66 }
67 for (FilterDataType<?> filterDataType : this.filterDataTypes)
68 {
69 columns.add(new SimpleColumn(filterDataType.getId(), filterDataType.getId(), String.class));
70 }
71 this.samplerData = new SamplerData<>(columns);
72 }
73
74
75
76
77
78 public SamplerData<G> getSamplerData()
79 {
80 return this.samplerData;
81 }
82
83
84
85
86
87
88 public boolean contains(final ExtendedDataType<?, ?, ?, ?> extendedDataType)
89 {
90 return this.extendedDataTypes.contains(extendedDataType);
91 }
92
93
94
95
96
97
98
99 public final void registerSpaceTimeRegion(final SpaceTimeRegion spaceTimeRegion)
100 {
101 Throw.whenNull(spaceTimeRegion, "SpaceTimeRegion may not be null.");
102 Time firstPossibleDataTime;
103 if (this.samplerData.contains(spaceTimeRegion.getLaneDirection()))
104 {
105 firstPossibleDataTime = this.samplerData.getTrajectoryGroup(spaceTimeRegion.getLaneDirection()).getStartTime();
106 }
107 else
108 {
109 firstPossibleDataTime = now();
110 }
111 Throw.when(spaceTimeRegion.getStartTime().lt(firstPossibleDataTime), IllegalStateException.class,
112 "Space time region with start time %s is defined while data is available from %s onwards.", spaceTimeRegion
113 .getStartTime(), firstPossibleDataTime);
114 if (this.samplerData.contains(spaceTimeRegion.getLaneDirection()))
115 {
116 this.endTimes.put(spaceTimeRegion.getLaneDirection(), Time.max(this.endTimes.get(spaceTimeRegion
117 .getLaneDirection()), spaceTimeRegion.getEndTime()));
118 }
119 else
120 {
121 this.endTimes.put(spaceTimeRegion.getLaneDirection(), spaceTimeRegion.getEndTime());
122 scheduleStartRecording(spaceTimeRegion.getStartTime(), spaceTimeRegion.getLaneDirection());
123 }
124 scheduleStopRecording(this.endTimes.get(spaceTimeRegion.getLaneDirection()), spaceTimeRegion.getLaneDirection());
125 this.spaceTimeRegions.add(spaceTimeRegion);
126 }
127
128
129
130
131
132 public abstract Time now();
133
134
135
136
137
138
139 public abstract void scheduleStartRecording(Time time, KpiLaneDirection kpiLaneDirection);
140
141
142
143
144
145
146 public abstract void scheduleStopRecording(Time time, KpiLaneDirection kpiLaneDirection);
147
148
149
150
151
152 public final void startRecording(final KpiLaneDirection kpiLaneDirection)
153 {
154 Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
155 if (this.samplerData.contains(kpiLaneDirection))
156 {
157 return;
158 }
159 this.samplerData.putTrajectoryGroup(kpiLaneDirection, new TrajectoryGroup<>(now(), kpiLaneDirection));
160 initRecording(kpiLaneDirection);
161 }
162
163
164
165
166
167 public abstract void initRecording(KpiLaneDirection kpiLaneDirection);
168
169
170
171
172
173 public final void stopRecording(final KpiLaneDirection kpiLaneDirection)
174 {
175 Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
176 if (!this.samplerData.contains(kpiLaneDirection) || this.endTimes.get(kpiLaneDirection).gt(now()))
177 {
178 return;
179 }
180 finalizeRecording(kpiLaneDirection);
181 }
182
183
184
185
186
187 public abstract void finalizeRecording(KpiLaneDirection kpiLaneDirection);
188
189
190
191
192
193
194
195
196
197
198 public final void processGtuAddEvent(final KpiLaneDirection kpiLaneDirection, final Length position, final Speed speed,
199 final Acceleration acceleration, final Time time, final G gtu)
200 {
201 Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
202 Throw.whenNull(position, "Position may not be null.");
203 Throw.whenNull(speed, "Speed may not be null.");
204 Throw.whenNull(acceleration, "Acceleration may not be null.");
205 Throw.whenNull(time, "Time may not be null.");
206 Throw.whenNull(gtu, "GtuDataInterface may not be null.");
207 if (kpiLaneDirection.getLaneData().getLength().lt(position))
208 {
209
210 return;
211 }
212 String gtuId = gtu.getId();
213 Trajectory<G> trajectory = new Trajectory<>(gtu, makeMetaData(gtu), this.extendedDataTypes, kpiLaneDirection);
214 if (!this.trajectoryPerGtu.containsKey(gtuId))
215 {
216 Map<KpiLaneDirection, Trajectory<G>> map = new LinkedHashMap<>();
217 this.trajectoryPerGtu.put(gtuId, map);
218 }
219 this.trajectoryPerGtu.get(gtuId).put(kpiLaneDirection, trajectory);
220 this.samplerData.getTrajectoryGroup(kpiLaneDirection).addTrajectory(trajectory);
221 processGtuMoveEvent(kpiLaneDirection, position, speed, acceleration, time, gtu);
222 }
223
224
225
226
227
228
229
230
231
232
233
234 public final void processGtuMoveEvent(final KpiLaneDirection kpiLaneDirection, final Length position, final Speed speed,
235 final Acceleration acceleration, final Time time, final G gtu)
236 {
237 Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
238 Throw.whenNull(position, "Position may not be null.");
239 Throw.whenNull(speed, "Speed may not be null.");
240 Throw.whenNull(acceleration, "Acceleration may not be null.");
241 Throw.whenNull(time, "Time may not be null.");
242 Throw.whenNull(gtu, "GtuDataInterface may not be null.");
243 String gtuId = gtu.getId();
244 if (this.trajectoryPerGtu.containsKey(gtuId) && this.trajectoryPerGtu.get(gtuId).containsKey(kpiLaneDirection))
245 {
246 this.trajectoryPerGtu.get(gtuId).get(kpiLaneDirection).add(position, speed, acceleration, time, gtu);
247 }
248 }
249
250
251
252
253
254
255
256
257
258
259 public final void processGtuRemoveEvent(final KpiLaneDirection kpiLaneDirection, final Length position, final Speed speed,
260 final Acceleration acceleration, final Time time, final G gtu)
261 {
262 processGtuMoveEvent(kpiLaneDirection, position, speed, acceleration, time, gtu);
263 processGtuRemoveEvent(kpiLaneDirection, gtu);
264 }
265
266
267
268
269
270
271 public final void processGtuRemoveEvent(final KpiLaneDirection kpiLaneDirection, final G gtu)
272 {
273 Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
274 Throw.whenNull(gtu, "GtuDataInterface may not be null.");
275 String gtuId = gtu.getId();
276 if (this.trajectoryPerGtu.containsKey(gtuId))
277 {
278 this.trajectoryPerGtu.get(gtuId).remove(kpiLaneDirection);
279 if (this.trajectoryPerGtu.get(gtuId).isEmpty())
280 {
281 this.trajectoryPerGtu.remove(gtuId);
282 }
283 }
284 }
285
286
287
288
289
290
291 @SuppressWarnings("unchecked")
292 private <T> MetaData makeMetaData(final G gtu)
293 {
294 MetaData metaData = new MetaData();
295 for (FilterDataType<?> metaDataType : this.filterDataTypes)
296 {
297 T value = (T) metaDataType.getValue(gtu);
298 if (value != null)
299 {
300 metaData.put((FilterDataType<T>) metaDataType, value);
301 }
302 }
303 return metaData;
304 }
305
306
307
308 }