1 package org.opentrafficsim.road.network.sampling;
2
3 import java.rmi.RemoteException;
4 import java.util.HashMap;
5 import java.util.HashSet;
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.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
17 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
18 import org.opentrafficsim.core.gtu.GTUDirectionality;
19 import org.opentrafficsim.core.gtu.GTUException;
20 import org.opentrafficsim.core.gtu.RelativePosition;
21 import org.opentrafficsim.kpi.sampling.KpiGtuDirectionality;
22 import org.opentrafficsim.kpi.sampling.KpiLaneDirection;
23 import org.opentrafficsim.kpi.sampling.Sampler;
24 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
25 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
26 import org.opentrafficsim.road.network.lane.Lane;
27 import org.opentrafficsim.road.network.lane.LaneDirection;
28
29 import nl.tudelft.simulation.dsol.SimRuntimeException;
30 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
31 import nl.tudelft.simulation.event.EventInterface;
32 import nl.tudelft.simulation.event.EventListenerInterface;
33 import nl.tudelft.simulation.event.TimedEvent;
34 import nl.tudelft.simulation.language.Throw;
35
36
37
38
39
40
41
42
43
44
45
46
47 public class RoadSampler extends Sampler implements EventListenerInterface
48 {
49
50
51 private final OTSDEVSSimulatorInterface simulator;
52
53
54 private final Duration samplingInterval;
55
56
57 private final Map<String, Map<LaneDirection, SimEvent<OTSSimTimeDouble>>> eventPerGtu = new HashMap<>();
58
59
60 private final Map<String, Set<LaneDirection>> listenersPerGtu = new HashMap<>();
61
62
63
64
65
66
67 public RoadSampler(final OTSDEVSSimulatorInterface simulator)
68 {
69 Throw.whenNull(simulator, "Simulator may not be null.");
70 this.simulator = simulator;
71 this.samplingInterval = null;
72 }
73
74
75
76
77
78
79
80
81 public RoadSampler(final OTSDEVSSimulatorInterface simulator, final Frequency frequency)
82 {
83 Throw.whenNull(simulator, "Simulator may not be null.");
84 Throw.whenNull(frequency, "Frequency may not be null.");
85 Throw.when(frequency.le(Frequency.ZERO), IllegalArgumentException.class,
86 "Negative or zero sampling frequency is not permitted.");
87 this.simulator = simulator;
88 this.samplingInterval = new Duration(1.0 / frequency.si, DurationUnit.SI);
89 }
90
91
92 @Override
93 public final Time now()
94 {
95 return this.simulator.getSimulatorTime().getTime();
96 }
97
98
99 @Override
100 public final void scheduleStartRecording(final Time time, final KpiLaneDirection kpiLaneDirection)
101 {
102 try
103 {
104 this.simulator.scheduleEventAbs(time, this, this, "startRecording", new Object[] { kpiLaneDirection });
105 }
106 catch (SimRuntimeException exception)
107 {
108 throw new RuntimeException("Cannot start recording.", exception);
109 }
110 }
111
112
113 @Override
114 public final void scheduleStopRecording(final Time time, final KpiLaneDirection kpiLaneDirection)
115 {
116 try
117 {
118 this.simulator.scheduleEventAbs(time, this, this, "stopRecording", new Object[] { kpiLaneDirection });
119 }
120 catch (SimRuntimeException exception)
121 {
122 throw new RuntimeException("Cannot stop recording.", exception);
123 }
124 }
125
126
127 @Override
128 public final void initRecording(final KpiLaneDirection kpiLaneDirection)
129 {
130 ((LaneData) kpiLaneDirection.getLaneData()).getLane().addListener(this, Lane.GTU_ADD_EVENT, true);
131 ((LaneData) kpiLaneDirection.getLaneData()).getLane().addListener(this, Lane.GTU_REMOVE_EVENT, true);
132 Lane lane = ((LaneData) kpiLaneDirection.getLaneData()).getLane();
133 int count = 1;
134 for (LaneBasedGTU gtu : lane.getGtuList())
135 {
136 try
137 {
138 DirectedLanePosition dlp = gtu.getReferencePosition();
139 if (dlp.getLane().equals(lane) && sameDirection(kpiLaneDirection.getKpiDirection(), dlp.getGtuDirection()))
140 {
141
142 notify(new TimedEvent<>(Lane.GTU_ADD_EVENT, lane, new Object[] { gtu.getId(), gtu, count },
143 gtu.getSimulator().getSimulatorTime()));
144 }
145 count++;
146 }
147 catch (RemoteException | GTUException exception)
148 {
149 throw new RuntimeException("Position cannot be obtained for GTU that is registered on a lane", exception);
150 }
151 }
152 }
153
154
155 @Override
156 public final void finalizeRecording(final KpiLaneDirection kpiLaneDirection)
157 {
158 ((LaneData) kpiLaneDirection.getLaneData()).getLane().removeListener(this, Lane.GTU_ADD_EVENT);
159 ((LaneData) kpiLaneDirection.getLaneData()).getLane().removeListener(this, Lane.GTU_REMOVE_EVENT);
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186 }
187
188
189
190
191
192
193
194 private boolean sameDirection(final KpiGtuDirectionality kpiGtuDirectionality, final GTUDirectionality gtuDirectionality)
195 {
196 if (kpiGtuDirectionality.equals(KpiGtuDirectionality.DIR_PLUS))
197 {
198 return gtuDirectionality.equals(GTUDirectionality.DIR_PLUS);
199 }
200 return gtuDirectionality.equals(GTUDirectionality.DIR_MINUS);
201 }
202
203
204 @Override
205 public final void notify(final EventInterface event) throws RemoteException
206 {
207 if (event.getType().equals(LaneBasedGTU.LANEBASED_MOVE_EVENT))
208 {
209
210
211 Object[] payload = (Object[]) event.getContent();
212 KpiLaneDirection laneDirection =
213 new KpiLaneDirection(new LaneData((Lane) payload[6]), KpiGtuDirectionality.DIR_PLUS);
214 processGtuMoveEvent(laneDirection, (Length) payload[7], (Speed) payload[2], (Acceleration) payload[3], now(),
215 new GtuData((LaneBasedGTU) event.getSource()));
216 }
217 else if (event.getType().equals(Lane.GTU_ADD_EVENT))
218 {
219
220 Lane lane = (Lane) event.getSource();
221
222 KpiLaneDirection laneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
223 if (!contains(laneDirection))
224 {
225
226 return;
227 }
228 Object[] payload = (Object[]) event.getContent();
229 LaneBasedGTU gtu = (LaneBasedGTU) payload[1];
230 Length position;
231 try
232 {
233
234 position = gtu.position(lane, RelativePosition.REFERENCE_POSITION);
235 }
236 catch (GTUException exception)
237 {
238 throw new RuntimeException(exception);
239 }
240 Speed speed = gtu.getSpeed();
241 Acceleration acceleration = gtu.getAcceleration();
242 processGtuAddEvent(laneDirection, position, speed, acceleration, now(), new GtuData(gtu));
243 LaneDirection lDirection = new LaneDirection(lane, GTUDirectionality.DIR_PLUS);
244 if (isIntervalBased())
245 {
246 scheduleSamplingEvent(gtu, lDirection);
247 }
248 else
249 {
250 if (!this.listenersPerGtu.containsKey(gtu.getId()))
251 {
252 this.listenersPerGtu.put(gtu.getId(), new HashSet<>());
253 }
254 this.listenersPerGtu.get(gtu.getId()).add(lDirection);
255 gtu.addListener(this, LaneBasedGTU.LANEBASED_MOVE_EVENT, true);
256 }
257 }
258 else if (event.getType().equals(Lane.GTU_REMOVE_EVENT))
259 {
260
261 Lane lane = (Lane) event.getSource();
262
263 KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
264 Object[] payload = (Object[]) event.getContent();
265 LaneBasedGTU gtu = (LaneBasedGTU) payload[1];
266 Length position = (Length) payload[3];
267 Speed speed = gtu.getSpeed();
268 Acceleration acceleration = gtu.getAcceleration();
269 processGtuRemoveEvent(kpiLaneDirection, position, speed, acceleration, now(), new GtuData(gtu));
270 LaneDirection lDirection = new LaneDirection(lane, GTUDirectionality.DIR_PLUS);
271 if (isIntervalBased())
272 {
273 String gtuId = (String) payload[0];
274
275 if (this.eventPerGtu.get(gtuId) != null)
276 {
277 if (this.eventPerGtu.get(gtuId).containsKey(lDirection))
278 {
279 this.simulator.cancelEvent(this.eventPerGtu.get(gtuId).get(lDirection));
280 }
281 this.eventPerGtu.get(gtuId).remove(lDirection);
282 if (this.eventPerGtu.get(gtuId).isEmpty())
283 {
284 this.eventPerGtu.remove(gtuId);
285 }
286 }
287 }
288 else
289 {
290
291 this.listenersPerGtu.get(gtu.getId()).remove(lDirection);
292 if (this.listenersPerGtu.get(gtu.getId()).isEmpty())
293 {
294 this.listenersPerGtu.remove(gtu.getId());
295 gtu.removeListener(this, LaneBasedGTU.LANEBASED_MOVE_EVENT);
296 }
297 }
298 }
299
300 }
301
302
303
304
305 private boolean isIntervalBased()
306 {
307 return this.samplingInterval != null;
308 }
309
310
311
312
313
314
315 private void scheduleSamplingEvent(final LaneBasedGTU gtu, final LaneDirection laneDirection)
316 {
317 OTSSimTimeDouble simTime = this.simulator.getSimulatorTime().copy();
318 simTime.add(this.samplingInterval);
319 SimEvent<OTSSimTimeDouble> simEvent =
320 new SimEvent<>(simTime, this, this, "notifySample", new Object[] { gtu, laneDirection });
321 try
322 {
323 this.simulator.scheduleEvent(simEvent);
324 }
325 catch (SimRuntimeException exception)
326 {
327
328 throw new RuntimeException("Scheduling sampling in the past.", exception);
329 }
330 String gtuId = gtu.getId();
331 if (!this.eventPerGtu.containsKey(gtuId))
332 {
333 Map<LaneDirection, SimEvent<OTSSimTimeDouble>> map = new HashMap<>();
334 this.eventPerGtu.put(gtuId, map);
335 }
336 this.eventPerGtu.get(gtuId).put(laneDirection, simEvent);
337 }
338
339
340
341
342
343
344 public final void notifySample(final LaneBasedGTU gtu, final LaneDirection laneDirection)
345 {
346 KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(laneDirection.getLane()),
347 laneDirection.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS);
348 try
349 {
350 this.processGtuMoveEvent(kpiLaneDirection,
351 gtu.position(laneDirection.getLane(), RelativePosition.REFERENCE_POSITION), gtu.getSpeed(),
352 gtu.getAcceleration(), now(), new GtuData(gtu));
353 }
354 catch (GTUException exception)
355 {
356 throw new RuntimeException("Requesting position on lane, but the GTU is not on the lane.", exception);
357 }
358 scheduleSamplingEvent(gtu, laneDirection);
359 }
360
361
362 @Override
363 public final int hashCode()
364 {
365 final int prime = 31;
366 int result = super.hashCode();
367 result = prime * result + ((this.eventPerGtu == null) ? 0 : this.eventPerGtu.hashCode());
368 result = prime * result + ((this.samplingInterval == null) ? 0 : this.samplingInterval.hashCode());
369 result = prime * result + ((this.simulator == null) ? 0 : this.simulator.hashCode());
370 return result;
371 }
372
373
374 @Override
375 public final boolean equals(final Object obj)
376 {
377 if (this == obj)
378 {
379 return true;
380 }
381 if (!super.equals(obj))
382 {
383 return false;
384 }
385 if (getClass() != obj.getClass())
386 {
387 return false;
388 }
389 RoadSampler other = (RoadSampler) obj;
390 if (this.eventPerGtu == null)
391 {
392 if (other.eventPerGtu != null)
393 {
394 return false;
395 }
396 }
397 else if (!this.eventPerGtu.equals(other.eventPerGtu))
398 {
399 return false;
400 }
401 if (this.samplingInterval == null)
402 {
403 if (other.samplingInterval != null)
404 {
405 return false;
406 }
407 }
408 else if (!this.samplingInterval.equals(other.samplingInterval))
409 {
410 return false;
411 }
412 if (this.simulator == null)
413 {
414 if (other.simulator != null)
415 {
416 return false;
417 }
418 }
419 else if (!this.simulator.equals(other.simulator))
420 {
421 return false;
422 }
423 return true;
424 }
425
426
427 @Override
428 public final String toString()
429 {
430 return "RoadSampler [samplingInterval=" + this.samplingInterval + ", eventPerGtu=" + this.eventPerGtu + "]";
431 }
432
433 }