View Javadoc
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   * <p>
38   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
39   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
40   * <p>
41   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 okt. 2016 <br>
42   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
43   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
44   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
45   */
46  
47  public class RoadSampler extends Sampler implements EventListenerInterface
48  {
49  
50      /** Simulator. */
51      private final OTSDEVSSimulatorInterface simulator;
52  
53      /** Sampling interval. */
54      private final Duration samplingInterval;
55  
56      /** Registration of sampling events of each GTU per lane, if interval based. */
57      private final Map<String, Map<LaneDirection, SimEvent<OTSSimTimeDouble>>> eventPerGtu = new HashMap<>();
58  
59      /**
60       * Constructor which uses the operational plan updates of GTU's as sampling interval.
61       * @param simulator simulator
62       * @throws NullPointerException if the simulator is {@code null}
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       * Constructor which uses the given frequency to determine the sampling interval.
73       * @param simulator simulator
74       * @param frequency sampling frequency
75       * @throws NullPointerException if an input is {@code null}
76       * @throws IllegalArgumentException if frequency is negative or zero
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      /** {@inheritDoc} */
89      @Override
90      public final Time now()
91      {
92          return this.simulator.getSimulatorTime().getTime();
93      }
94  
95      /** {@inheritDoc} */
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
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                     // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_addition}
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     /** {@inheritDoc} */
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                 // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_removal}
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      * Compares a {@link KpiGtuDirectionality} and a {@link GTUDirectionality}.
187      * @param kpiGtuDirectionality kpi gtu direction
188      * @param gtuDirectionality gtu direction
189      * @return whether both are in the same direction
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     /** {@inheritDoc} */
201     @Override
202     public final void notify(final EventInterface event) throws RemoteException
203     {
204         if (event.getType().equals(LaneBasedGTU.LANEBASED_MOVE_EVENT))
205         {
206             // Payload: [String gtuId, DirectedPoint position, Speed speed, Acceleration acceleration, TurnIndicatorStatus
207             // turnIndicatorStatus, Length odometer, Lane referenceLane, Length positionOnReferenceLane]
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             // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_addition}
217             Lane lane = (Lane) event.getSource();
218             // TODO GTUDirectionality from Lane.GTU_ADD_EVENT
219             KpiLaneDirection laneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
220             if (!contains(laneDirection))
221             {
222                 // we are not sampling this LaneDirection
223                 return;
224             }
225             Object[] payload = (Object[]) event.getContent();
226             LaneBasedGTU gtu = (LaneBasedGTU) payload[1];
227             Length position;
228             try
229             {
230                 // TODO Length from Lane.GTU_ADD_EVENT
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             // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_removal}
252             Lane lane = (Lane) event.getSource();
253             // TODO GTUDirectionality from Lane.GTU_ADD_EVENT
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             // TODO Length from Lane.GTU_ADD_EVENT
259             // this doesn't work, as the GTU is no longer on the lane it was removed from
260             // try
261             // {
262             // position = gtu.position(lane, RelativePosition.REFERENCE_POSITION);
263             // }
264             // catch (GTUException exception)
265             // {
266             // throw new RuntimeException(exception);
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      * @return whether sampling is interval based
298      */
299     private boolean isIntervalBased()
300     {
301         return this.samplingInterval != null;
302     }
303 
304     /**
305      * Schedules a sampling event for the given gtu on the given lane for the samlping interval from the current time.
306      * @param gtu gtu to sample
307      * @param laneDirection lane direction where the gtu is at
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             // should not happen with getSimulatorTime.add()
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      * Samples a gtu and schedules the next sampling event.
335      * @param gtu gtu to sample
336      * @param laneDirection lane direction where the gtu is at
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
421     @Override
422     public String toString()
423     {
424         return "RoadSampler [samplingInterval=" + this.samplingInterval + ", eventPerGtu=" + this.eventPerGtu + "]";
425     }
426 
427 }