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