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