1 package org.opentrafficsim.road.network.sampling;
2
3 import java.rmi.RemoteException;
4 import java.util.LinkedHashMap;
5 import java.util.LinkedHashSet;
6 import java.util.Map;
7 import java.util.Set;
8
9 import org.djunits.unit.DurationUnit;
10 import org.djunits.value.vdouble.scalar.Acceleration;
11 import org.djunits.value.vdouble.scalar.Duration;
12 import org.djunits.value.vdouble.scalar.Frequency;
13 import org.djunits.value.vdouble.scalar.Length;
14 import org.djunits.value.vdouble.scalar.Speed;
15 import org.djunits.value.vdouble.scalar.Time;
16 import org.djutils.event.EventInterface;
17 import org.djutils.event.EventListenerInterface;
18 import org.djutils.event.TimedEvent;
19 import org.djutils.event.ref.ReferenceType;
20 import org.djutils.exceptions.Throw;
21 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
22 import org.opentrafficsim.core.gtu.GTUDirectionality;
23 import org.opentrafficsim.core.gtu.GTUException;
24 import org.opentrafficsim.core.gtu.RelativePosition;
25 import org.opentrafficsim.kpi.sampling.KpiGtuDirectionality;
26 import org.opentrafficsim.kpi.sampling.KpiLaneDirection;
27 import org.opentrafficsim.kpi.sampling.Sampler;
28 import org.opentrafficsim.kpi.sampling.data.ExtendedDataType;
29 import org.opentrafficsim.kpi.sampling.meta.FilterDataType;
30 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
31 import org.opentrafficsim.road.network.OTSRoadNetwork;
32 import org.opentrafficsim.road.network.lane.CrossSectionLink;
33 import org.opentrafficsim.road.network.lane.Lane;
34 import org.opentrafficsim.road.network.lane.LaneDirection;
35
36 import nl.tudelft.simulation.dsol.SimRuntimeException;
37 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
38
39
40
41
42
43
44
45
46
47
48
49
50 public class RoadSampler extends Sampler<GtuData> implements EventListenerInterface
51 {
52
53
54 private static final long serialVersionUID = 20200228L;
55
56
57 private final OTSSimulatorInterface simulator;
58
59
60 private final OTSRoadNetwork network;
61
62
63 private final Duration samplingInterval;
64
65
66 private final Map<String, Map<LaneDirection, SimEventInterface<Duration>>> eventPerGtu = new LinkedHashMap<>();
67
68
69 private final Map<String, Set<LaneDirection>> listenersPerGtu = new LinkedHashMap<>();
70
71
72
73
74
75
76 public RoadSampler(final OTSRoadNetwork network)
77 {
78 this(new LinkedHashSet<>(), new LinkedHashSet<>(), network);
79 }
80
81
82
83
84
85
86
87
88 public RoadSampler(final Set<ExtendedDataType<?, ?, ?, GtuData>> extendedDataTypes,
89 final Set<FilterDataType<?>> filterDataTypes, final OTSRoadNetwork network)
90 {
91 super(extendedDataTypes, filterDataTypes);
92 Throw.whenNull(network, "Network may not be null.");
93 this.network = network;
94 this.simulator = network.getSimulator();
95 this.samplingInterval = null;
96 }
97
98
99
100
101
102
103
104
105 public RoadSampler(final OTSRoadNetwork network, final Frequency frequency)
106 {
107 this(new LinkedHashSet<>(), new LinkedHashSet<>(), network, frequency);
108 }
109
110
111
112
113
114
115
116
117
118
119 public RoadSampler(final Set<ExtendedDataType<?, ?, ?, GtuData>> extendedDataTypes,
120 final Set<FilterDataType<?>> filterDataTypes, final OTSRoadNetwork network, final Frequency frequency)
121 {
122 super(extendedDataTypes, filterDataTypes);
123 Throw.whenNull(network, "Network may not be null.");
124 Throw.whenNull(frequency, "Frequency may not be null.");
125 Throw.when(frequency.le(Frequency.ZERO), IllegalArgumentException.class,
126 "Negative or zero sampling frequency is not permitted.");
127 this.network = network;
128 this.simulator = network.getSimulator();
129 this.samplingInterval = new Duration(1.0 / frequency.si, DurationUnit.SI);
130 }
131
132
133 @Override
134 public final Time now()
135 {
136 return this.simulator.getSimulatorAbsTime();
137 }
138
139
140 @Override
141 public final void scheduleStartRecording(final Time time, final KpiLaneDirection kpiLaneDirection)
142 {
143 try
144 {
145 this.simulator.scheduleEventAbsTime(time, this, this, "startRecording", new Object[] {kpiLaneDirection});
146 }
147 catch (SimRuntimeException exception)
148 {
149 throw new RuntimeException("Cannot start recording.", exception);
150 }
151 }
152
153
154 @Override
155 public final void scheduleStopRecording(final Time time, final KpiLaneDirection kpiLaneDirection)
156 {
157 try
158 {
159 this.simulator.scheduleEventAbsTime(time, this, this, "stopRecording", new Object[] {kpiLaneDirection});
160 }
161 catch (SimRuntimeException exception)
162 {
163 throw new RuntimeException("Cannot stop recording.", exception);
164 }
165 }
166
167
168 @Override
169 public final void initRecording(final KpiLaneDirection kpiLaneDirection)
170 {
171 Lane lane = ((LaneData) kpiLaneDirection.getLaneData()).getLane();
172 lane.addListener(this, Lane.GTU_ADD_EVENT, ReferenceType.WEAK);
173 lane.addListener(this, Lane.GTU_REMOVE_EVENT, ReferenceType.WEAK);
174 int count = 1;
175 for (LaneBasedGTU gtu : lane.getGtuList())
176 {
177 try
178 {
179 if (sameDirection(kpiLaneDirection.getKpiDirection(), gtu.getDirection(lane)))
180 {
181
182
183
184
185 notify(new TimedEvent<>(Lane.GTU_ADD_EVENT, lane, new Object[] {gtu.getId(), count},
186 gtu.getSimulator().getSimulatorTime()));
187 }
188 count++;
189 }
190 catch (RemoteException | GTUException exception)
191 {
192 throw new RuntimeException("Position cannot be obtained for GTU that is registered on a lane", exception);
193 }
194 }
195 }
196
197
198 @Override
199 public final void finalizeRecording(final KpiLaneDirection kpiLaneDirection)
200 {
201 Lane lane = ((LaneData) kpiLaneDirection.getLaneData()).getLane();
202 lane.removeListener(this, Lane.GTU_ADD_EVENT);
203 lane.removeListener(this, Lane.GTU_REMOVE_EVENT);
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230 }
231
232
233
234
235
236
237
238 private boolean sameDirection(final KpiGtuDirectionality kpiGtuDirectionality, final GTUDirectionality gtuDirectionality)
239 {
240 if (kpiGtuDirectionality.equals(KpiGtuDirectionality.DIR_PLUS))
241 {
242 return gtuDirectionality.equals(GTUDirectionality.DIR_PLUS);
243 }
244 return gtuDirectionality.equals(GTUDirectionality.DIR_MINUS);
245 }
246
247
248 @Override
249 public final void notify(final EventInterface event) throws RemoteException
250 {
251 if (event.getType().equals(LaneBasedGTU.LANEBASED_MOVE_EVENT))
252 {
253
254
255
256 Object[] payload = (Object[]) event.getContent();
257 CrossSectionLink link = (CrossSectionLink) this.network.getLink(payload[7].toString());
258 Lane lane = (Lane) link.getCrossSectionElement(payload[8].toString());
259 LaneBasedGTU gtu = (LaneBasedGTU) this.network.getGTU(payload[0].toString());
260 KpiLaneDirection laneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
261 processGtuMoveEvent(laneDirection, (Length) payload[9], (Speed) payload[3], (Acceleration) payload[4], now(),
262 new GtuData(gtu));
263 }
264 else if (event.getType().equals(Lane.GTU_ADD_EVENT))
265 {
266
267
268 Lane lane = (Lane) event.getSourceId();
269
270 KpiLaneDirection laneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
271 if (!getSamplerData().contains(laneDirection))
272 {
273
274 return;
275 }
276 Object[] payload = (Object[]) event.getContent();
277
278 LaneBasedGTU gtu = (LaneBasedGTU) this.network.getGTU((String) payload[0]);
279 Length position;
280 try
281 {
282
283 position = gtu.position(lane, RelativePosition.REFERENCE_POSITION);
284 }
285 catch (GTUException exception)
286 {
287 throw new RuntimeException(exception);
288 }
289 Speed speed = gtu.getSpeed();
290 Acceleration acceleration = gtu.getAcceleration();
291 processGtuAddEvent(laneDirection, position, speed, acceleration, now(), new GtuData(gtu));
292 LaneDirection lDirection = new LaneDirection(lane, GTUDirectionality.DIR_PLUS);
293 if (isIntervalBased())
294 {
295 scheduleSamplingEvent(gtu, lDirection);
296 }
297 else
298 {
299 if (!this.listenersPerGtu.containsKey(gtu.getId()))
300 {
301 this.listenersPerGtu.put(gtu.getId(), new LinkedHashSet<>());
302 }
303 this.listenersPerGtu.get(gtu.getId()).add(lDirection);
304 gtu.addListener(this, LaneBasedGTU.LANEBASED_MOVE_EVENT, ReferenceType.WEAK);
305 }
306 }
307 else if (event.getType().equals(Lane.GTU_REMOVE_EVENT))
308 {
309
310
311 Lane lane = (Lane) event.getSourceId();
312
313 KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
314 Object[] payload = (Object[]) event.getContent();
315 LaneBasedGTU gtu = (LaneBasedGTU) payload[1];
316 Length position = (Length) payload[3];
317 Speed speed = gtu.getSpeed();
318 Acceleration acceleration = gtu.getAcceleration();
319 processGtuRemoveEvent(kpiLaneDirection, position, speed, acceleration, now(), new GtuData(gtu));
320 LaneDirection lDirection = new LaneDirection(lane, GTUDirectionality.DIR_PLUS);
321 if (isIntervalBased())
322 {
323 String gtuId = (String) payload[0];
324
325 if (this.eventPerGtu.get(gtuId) != null)
326 {
327 if (this.eventPerGtu.get(gtuId).containsKey(lDirection))
328 {
329 this.simulator.cancelEvent(this.eventPerGtu.get(gtuId).get(lDirection));
330 }
331 this.eventPerGtu.get(gtuId).remove(lDirection);
332 if (this.eventPerGtu.get(gtuId).isEmpty())
333 {
334 this.eventPerGtu.remove(gtuId);
335 }
336 }
337 }
338 else
339 {
340
341 this.listenersPerGtu.get(gtu.getId()).remove(lDirection);
342 if (this.listenersPerGtu.get(gtu.getId()).isEmpty())
343 {
344 this.listenersPerGtu.remove(gtu.getId());
345 gtu.removeListener(this, LaneBasedGTU.LANEBASED_MOVE_EVENT);
346 }
347 }
348 }
349
350 }
351
352
353
354
355 private boolean isIntervalBased()
356 {
357 return this.samplingInterval != null;
358 }
359
360
361
362
363
364
365 private void scheduleSamplingEvent(final LaneBasedGTU gtu, final LaneDirection laneDirection)
366 {
367 SimEventInterface<Duration> simEvent;
368 try
369 {
370
371 simEvent = this.simulator.scheduleEventRel(this.samplingInterval, this, this, "notifySample",
372 new Object[] {gtu, laneDirection});
373 }
374 catch (SimRuntimeException exception)
375 {
376
377 throw new RuntimeException("Scheduling sampling in the past.", exception);
378 }
379 String gtuId = gtu.getId();
380 if (!this.eventPerGtu.containsKey(gtuId))
381 {
382 Map<LaneDirection, SimEventInterface<Duration>> map = new LinkedHashMap<>();
383 this.eventPerGtu.put(gtuId, map);
384 }
385 this.eventPerGtu.get(gtuId).put(laneDirection, simEvent);
386 }
387
388
389
390
391
392
393 public final void notifySample(final LaneBasedGTU gtu, final LaneDirection laneDirection)
394 {
395 KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(laneDirection.getLane()),
396 laneDirection.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS);
397 try
398 {
399 this.processGtuMoveEvent(kpiLaneDirection,
400 gtu.position(laneDirection.getLane(), RelativePosition.REFERENCE_POSITION), gtu.getSpeed(),
401 gtu.getAcceleration(), now(), new GtuData(gtu));
402 }
403 catch (GTUException exception)
404 {
405 throw new RuntimeException("Requesting position on lane, but the GTU is not on the lane.", exception);
406 }
407 scheduleSamplingEvent(gtu, laneDirection);
408 }
409
410
411 @Override
412 public final int hashCode()
413 {
414 final int prime = 31;
415 int result = super.hashCode();
416 result = prime * result + ((this.eventPerGtu == null) ? 0 : this.eventPerGtu.hashCode());
417 result = prime * result + ((this.samplingInterval == null) ? 0 : this.samplingInterval.hashCode());
418 result = prime * result + ((this.simulator == null) ? 0 : this.simulator.hashCode());
419 return result;
420 }
421
422
423 @Override
424 public final boolean equals(final Object obj)
425 {
426 if (this == obj)
427 {
428 return true;
429 }
430 if (!super.equals(obj))
431 {
432 return false;
433 }
434 if (getClass() != obj.getClass())
435 {
436 return false;
437 }
438 RoadSampler other = (RoadSampler) obj;
439 if (this.eventPerGtu == null)
440 {
441 if (other.eventPerGtu != null)
442 {
443 return false;
444 }
445 }
446 else if (!this.eventPerGtu.equals(other.eventPerGtu))
447 {
448 return false;
449 }
450 if (this.samplingInterval == null)
451 {
452 if (other.samplingInterval != null)
453 {
454 return false;
455 }
456 }
457 else if (!this.samplingInterval.equals(other.samplingInterval))
458 {
459 return false;
460 }
461 if (this.simulator == null)
462 {
463 if (other.simulator != null)
464 {
465 return false;
466 }
467 }
468 else if (!this.simulator.equals(other.simulator))
469 {
470 return false;
471 }
472 return true;
473 }
474
475
476 @Override
477 public final String toString()
478 {
479 return "RoadSampler [samplingInterval=" + this.samplingInterval + "]";
480
481 }
482
483
484
485
486
487
488 public static Factory build(final OTSRoadNetwork network)
489 {
490 return new Factory(network);
491 }
492
493
494 public static final class Factory
495 {
496
497
498 private final OTSRoadNetwork network;
499
500
501 private final Set<ExtendedDataType<?, ?, ?, GtuData>> extendedDataTypes = new LinkedHashSet<>();
502
503
504 private final Set<FilterDataType<?>> filterDataTypes = new LinkedHashSet<>();
505
506
507 private Frequency freq;
508
509
510
511
512
513 Factory(final OTSRoadNetwork network)
514 {
515 this.network = network;
516 }
517
518
519
520
521
522
523 public Factory registerExtendedDataType(final ExtendedDataType<?, ?, ?, GtuData> extendedDataType)
524 {
525 Throw.whenNull(extendedDataType, "Extended data type may not be null.");
526 this.extendedDataTypes.add(extendedDataType);
527 return this;
528 }
529
530
531
532
533
534
535 public Factory registerFilterDataType(final FilterDataType<?> filterDataType)
536 {
537 Throw.whenNull(filterDataType, "Filter data type may not be null.");
538 this.filterDataTypes.add(filterDataType);
539 return this;
540 }
541
542
543
544
545
546
547 public Factory setFrequency(final Frequency frequency)
548 {
549 this.freq = frequency;
550 return this;
551 }
552
553
554
555
556
557 public RoadSampler create()
558 {
559 return this.freq == null ? new RoadSampler(this.extendedDataTypes, this.filterDataTypes, this.network)
560 : new RoadSampler(this.extendedDataTypes, this.filterDataTypes, this.network, this.freq);
561 }
562
563 }
564
565 }