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