View Javadoc
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   * <p>
37   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
38   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
39   * <p>
40   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 okt. 2016 <br>
41   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
42   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
43   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
44   */
45  
46  public class RoadSampler extends Sampler implements EventListenerInterface
47  {
48  
49      /** Simulator. */
50      private final OTSDEVSSimulatorInterface simulator;
51  
52      /** Sampling interval. */
53      private final Duration samplingInterval;
54  
55      /** Registration of sampling events of each GTU per lane, if interval based. */
56      private final Map<String, Map<LaneDirection, SimEvent<OTSSimTimeDouble>>> eventPerGtu = new HashMap<>();
57  
58      /**
59       * Constructor which uses the operational plan updates of GTU's as sampling interval.
60       * @param simulator simulator
61       * @throws NullPointerException if the simulator is {@code null}
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       * Constructor which uses the given frequency to determine the sampling interval.
72       * @param simulator simulator
73       * @param frequency sampling frequency
74       * @throws NullPointerException if an input is {@code null}
75       * @throws IllegalArgumentException if frequency is negative or zero
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      /** {@inheritDoc} */
88      @Override
89      public final Time now()
90      {
91          return this.simulator.getSimulatorTime().getTime();
92      }
93  
94      /** {@inheritDoc} */
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
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                     // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_addition}
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     /** {@inheritDoc} */
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         // Lane lane = ((LaneData) kpiLaneDirection.getLaneData()).getLane();
157         // int count = 0;
158         // List<LaneBasedGTU> currentGtus = new ArrayList<>();
159         // try
160         // {
161         // for (LaneBasedGTU gtu : lane.getGtuList())
162         // {
163         // DirectedLanePosition dlp = gtu.getReferencePosition();
164         // if (dlp.getLane().equals(lane) && sameDirection(kpiLaneDirection.getKpiDirection(), dlp.getGtuDirection()))
165         // {
166         // currentGtus.add(gtu);
167         // count++;
168         // }
169         // }
170         // for (LaneBasedGTU gtu : currentGtus)
171         // {
172         // // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_removal, Length position}
173         // notify(new TimedEvent<>(Lane.GTU_REMOVE_EVENT, lane, new Object[] { gtu.getId(), gtu, count },
174         // gtu.getSimulator().getSimulatorTime()));
175         // count--;
176         // }
177         // }
178         // catch (RemoteException | GTUException exception)
179         // {
180         // throw new RuntimeException("Position cannot be obtained for GTU that is registered on a lane", exception);
181         // }
182     }
183 
184     /**
185      * Compares a {@link KpiGtuDirectionality} and a {@link GTUDirectionality}.
186      * @param kpiGtuDirectionality kpi gtu direction
187      * @param gtuDirectionality gtu direction
188      * @return whether both are in the same direction
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     /** {@inheritDoc} */
200     @Override
201     public final void notify(final EventInterface event) throws RemoteException
202     {
203         if (event.getType().equals(LaneBasedGTU.LANEBASED_MOVE_EVENT))
204         {
205             // Payload: [String gtuId, DirectedPoint position, Speed speed, Acceleration acceleration, TurnIndicatorStatus
206             // turnIndicatorStatus, Length odometer, Lane referenceLane, Length positionOnReferenceLane]
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             // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_addition}
216             Lane lane = (Lane) event.getSource();
217             // TODO GTUDirectionality from Lane.GTU_ADD_EVENT
218             KpiLaneDirection laneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
219             if (!contains(laneDirection))
220             {
221                 // we are not sampling this LaneDirection
222                 return;
223             }
224             Object[] payload = (Object[]) event.getContent();
225             LaneBasedGTU gtu = (LaneBasedGTU) payload[1];
226             Length position;
227             try
228             {
229                 // TODO Length from Lane.GTU_ADD_EVENT
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             // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_removal, Length position}
251             Lane lane = (Lane) event.getSource();
252             // TODO GTUDirectionality from Lane.GTU_ADD_EVENT
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      * @return whether sampling is interval based
287      */
288     private boolean isIntervalBased()
289     {
290         return this.samplingInterval != null;
291     }
292 
293     /**
294      * Schedules a sampling event for the given gtu on the given lane for the samlping interval from the current time.
295      * @param gtu gtu to sample
296      * @param laneDirection lane direction where the gtu is at
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             // should not happen with getSimulatorTime.add()
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      * Samples a gtu and schedules the next sampling event.
324      * @param gtu gtu to sample
325      * @param laneDirection lane direction where the gtu is at
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
410     @Override
411     public final String toString()
412     {
413         return "RoadSampler [samplingInterval=" + this.samplingInterval + ", eventPerGtu=" + this.eventPerGtu + "]";
414     }
415 
416 }