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