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