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