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.road.gtu.lane.LaneBasedGTU;
28 import org.opentrafficsim.road.network.lane.Lane;
29 import org.opentrafficsim.road.network.lane.LaneDirection;
30
31 import nl.tudelft.simulation.dsol.SimRuntimeException;
32 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
33 import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
34 import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
35
36
37
38
39
40
41
42
43
44
45
46
47 public class RoadSampler extends Sampler<GtuData> implements EventListenerInterface
48 {
49
50
51 private final DEVSSimulatorInterface.TimeDoubleUnit simulator;
52
53
54 private final Duration samplingInterval;
55
56
57 private final Map<String, Map<LaneDirection, SimEventInterface<SimTimeDoubleUnit>>> eventPerGtu = new LinkedHashMap<>();
58
59
60 private final Map<String, Set<LaneDirection>> listenersPerGtu = new LinkedHashMap<>();
61
62
63
64
65
66
67 public RoadSampler(final DEVSSimulatorInterface.TimeDoubleUnit 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 DEVSSimulatorInterface.TimeDoubleUnit 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();
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 Lane lane = ((LaneData) kpiLaneDirection.getLaneData()).getLane();
131 lane.addListener(this, Lane.GTU_ADD_EVENT, ReferenceType.WEAK);
132 lane.addListener(this, Lane.GTU_REMOVE_EVENT, ReferenceType.WEAK);
133 int count = 1;
134 for (LaneBasedGTU gtu : lane.getGtuList())
135 {
136 try
137 {
138 if (sameDirection(kpiLaneDirection.getKpiDirection(), gtu.getDirection(lane)))
139 {
140
141 notify(new TimedEvent<>(Lane.GTU_ADD_EVENT, lane, new Object[] {gtu.getId(), gtu, count},
142 gtu.getSimulator().getSimulatorTime()));
143 }
144 count++;
145 }
146 catch (RemoteException | GTUException exception)
147 {
148 throw new RuntimeException("Position cannot be obtained for GTU that is registered on a lane", exception);
149 }
150 }
151 }
152
153
154 @Override
155 public final void finalizeRecording(final KpiLaneDirection kpiLaneDirection)
156 {
157 Lane lane = ((LaneData) kpiLaneDirection.getLaneData()).getLane();
158 lane.removeListener(this, Lane.GTU_ADD_EVENT);
159 lane.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.getSourceId()));
216 }
217 else if (event.getType().equals(Lane.GTU_ADD_EVENT))
218 {
219
220
221 Laneef="../../../../../org/opentrafficsim/road/network/lane/Lane.html#Lane">Lane lane = (Lane) event.getSourceId();
222
223 KpiLaneDirection laneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
224 if (!contains(laneDirection))
225 {
226
227 return;
228 }
229 Object[] payload = (Object[]) event.getContent();
230 LaneBasedGTU../../../../org/opentrafficsim/road/gtu/lane/LaneBasedGTU.html#LaneBasedGTU">LaneBasedGTU gtu = (LaneBasedGTU) payload[1];
231 Length position;
232 try
233 {
234
235 position = gtu.position(lane, RelativePosition.REFERENCE_POSITION);
236 }
237 catch (GTUException exception)
238 {
239 throw new RuntimeException(exception);
240 }
241 Speed speed = gtu.getSpeed();
242 Acceleration acceleration = gtu.getAcceleration();
243 processGtuAddEvent(laneDirection, position, speed, acceleration, now(), new GtuData(gtu));
244 LaneDirection/LaneDirection.html#LaneDirection">LaneDirection lDirection = new LaneDirection(lane, GTUDirectionality.DIR_PLUS);
245 if (isIntervalBased())
246 {
247 scheduleSamplingEvent(gtu, lDirection);
248 }
249 else
250 {
251 if (!this.listenersPerGtu.containsKey(gtu.getId()))
252 {
253 this.listenersPerGtu.put(gtu.getId(), new LinkedHashSet<>());
254 }
255 this.listenersPerGtu.get(gtu.getId()).add(lDirection);
256 gtu.addListener(this, LaneBasedGTU.LANEBASED_MOVE_EVENT, ReferenceType.WEAK);
257 }
258 }
259 else if (event.getType().equals(Lane.GTU_REMOVE_EVENT))
260 {
261
262
263 Laneef="../../../../../org/opentrafficsim/road/network/lane/Lane.html#Lane">Lane lane = (Lane) event.getSourceId();
264
265 KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
266 Object[] payload = (Object[]) event.getContent();
267 LaneBasedGTU../../../../org/opentrafficsim/road/gtu/lane/LaneBasedGTU.html#LaneBasedGTU">LaneBasedGTU gtu = (LaneBasedGTU) payload[1];
268 Length position = (Length) payload[3];
269 Speed speed = gtu.getSpeed();
270 Acceleration acceleration = gtu.getAcceleration();
271 processGtuRemoveEvent(kpiLaneDirection, position, speed, acceleration, now(), new GtuData(gtu));
272 LaneDirection/LaneDirection.html#LaneDirection">LaneDirection lDirection = new LaneDirection(lane, GTUDirectionality.DIR_PLUS);
273 if (isIntervalBased())
274 {
275 String gtuId = (String) payload[0];
276
277 if (this.eventPerGtu.get(gtuId) != null)
278 {
279 if (this.eventPerGtu.get(gtuId).containsKey(lDirection))
280 {
281 this.simulator.cancelEvent(this.eventPerGtu.get(gtuId).get(lDirection));
282 }
283 this.eventPerGtu.get(gtuId).remove(lDirection);
284 if (this.eventPerGtu.get(gtuId).isEmpty())
285 {
286 this.eventPerGtu.remove(gtuId);
287 }
288 }
289 }
290 else
291 {
292
293 this.listenersPerGtu.get(gtu.getId()).remove(lDirection);
294 if (this.listenersPerGtu.get(gtu.getId()).isEmpty())
295 {
296 this.listenersPerGtu.remove(gtu.getId());
297 gtu.removeListener(this, LaneBasedGTU.LANEBASED_MOVE_EVENT);
298 }
299 }
300 }
301
302 }
303
304
305
306
307 private boolean isIntervalBased()
308 {
309 return this.samplingInterval != null;
310 }
311
312
313
314
315
316
317 private void scheduleSamplingEvent(final LaneBasedGTU gtu, final LaneDirection laneDirection)
318 {
319 SimEventInterface<SimTimeDoubleUnit> simEvent;
320 try
321 {
322
323 simEvent = this.simulator.scheduleEventRel(this.samplingInterval, this, this, "notifySample",
324 new Object[] {gtu, laneDirection});
325 }
326 catch (SimRuntimeException exception)
327 {
328
329 throw new RuntimeException("Scheduling sampling in the past.", exception);
330 }
331 String gtuId = gtu.getId();
332 if (!this.eventPerGtu.containsKey(gtuId))
333 {
334 Map<LaneDirection, SimEventInterface<SimTimeDoubleUnit>> map = new LinkedHashMap<>();
335 this.eventPerGtu.put(gtuId, map);
336 }
337 this.eventPerGtu.get(gtuId).put(laneDirection, simEvent);
338 }
339
340
341
342
343
344
345 public final void notifySample(final LaneBasedGTU gtu, final LaneDirection laneDirection)
346 {
347 KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(laneDirection.getLane()),
348 laneDirection.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS);
349 try
350 {
351 this.processGtuMoveEvent(kpiLaneDirection,
352 gtu.position(laneDirection.getLane(), RelativePosition.REFERENCE_POSITION), gtu.getSpeed(),
353 gtu.getAcceleration(), now(), new GtuData(gtu));
354 }
355 catch (GTUException exception)
356 {
357 throw new RuntimeException("Requesting position on lane, but the GTU is not on the lane.", exception);
358 }
359 scheduleSamplingEvent(gtu, laneDirection);
360 }
361
362
363 @Override
364 public final int hashCode()
365 {
366 final int prime = 31;
367 int result = super.hashCode();
368 result = prime * result + ((this.eventPerGtu == null) ? 0 : this.eventPerGtu.hashCode());
369 result = prime * result + ((this.samplingInterval == null) ? 0 : this.samplingInterval.hashCode());
370 result = prime * result + ((this.simulator == null) ? 0 : this.simulator.hashCode());
371 return result;
372 }
373
374
375 @Override
376 public final boolean equals(final Object obj)
377 {
378 if (this == obj)
379 {
380 return true;
381 }
382 if (!super.equals(obj))
383 {
384 return false;
385 }
386 if (getClass() != obj.getClass())
387 {
388 return false;
389 }
390 RoadSampler./../../../org/opentrafficsim/road/network/sampling/RoadSampler.html#RoadSampler">RoadSampler other = (RoadSampler) obj;
391 if (this.eventPerGtu == null)
392 {
393 if (other.eventPerGtu != null)
394 {
395 return false;
396 }
397 }
398 else if (!this.eventPerGtu.equals(other.eventPerGtu))
399 {
400 return false;
401 }
402 if (this.samplingInterval == null)
403 {
404 if (other.samplingInterval != null)
405 {
406 return false;
407 }
408 }
409 else if (!this.samplingInterval.equals(other.samplingInterval))
410 {
411 return false;
412 }
413 if (this.simulator == null)
414 {
415 if (other.simulator != null)
416 {
417 return false;
418 }
419 }
420 else if (!this.simulator.equals(other.simulator))
421 {
422 return false;
423 }
424 return true;
425 }
426
427
428 @Override
429 public final String toString()
430 {
431 return "RoadSampler [samplingInterval=" + this.samplingInterval + "]";
432
433 }
434
435 }