View Javadoc
1   package org.opentrafficsim.road.network.sampling;
2   
3   import java.rmi.RemoteException;
4   import java.util.HashMap;
5   import java.util.HashSet;
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.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-2017 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      /** List of lane the sampler is listening to for each GTU. Usually 1, could be 2 during a trajectory transition. */
60      private final Map<String, Set<LaneDirection>> listenersPerGtu = new HashMap<>();
61  
62      /**
63       * Constructor which uses the operational plan updates of GTU's as sampling interval.
64       * @param simulator simulator
65       * @throws NullPointerException if the simulator is {@code null}
66       */
67      public RoadSampler(final OTSDEVSSimulatorInterface simulator)
68      {
69          Throw.whenNull(simulator, "Simulator may not be null.");
70          this.simulator = simulator;
71          this.samplingInterval = null;
72      }
73  
74      /**
75       * Constructor which uses the given frequency to determine the sampling interval.
76       * @param simulator simulator
77       * @param frequency sampling frequency
78       * @throws NullPointerException if an input is {@code null}
79       * @throws IllegalArgumentException if frequency is negative or zero
80       */
81      public RoadSampler(final OTSDEVSSimulatorInterface 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      /** {@inheritDoc} */
92      @Override
93      public final Time now()
94      {
95          return this.simulator.getSimulatorTime().getTime();
96      }
97  
98      /** {@inheritDoc} */
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
127     @Override
128     public final void initRecording(final KpiLaneDirection kpiLaneDirection)
129     {
130         ((LaneData) kpiLaneDirection.getLaneData()).getLane().addListener(this, Lane.GTU_ADD_EVENT, true);
131         ((LaneData) kpiLaneDirection.getLaneData()).getLane().addListener(this, Lane.GTU_REMOVE_EVENT, true);
132         Lane lane = ((LaneData) kpiLaneDirection.getLaneData()).getLane();
133         int count = 1;
134         for (LaneBasedGTU gtu : lane.getGtuList())
135         {
136             try
137             {
138                 DirectedLanePosition dlp = gtu.getReferencePosition();
139                 if (dlp.getLane().equals(lane) && sameDirection(kpiLaneDirection.getKpiDirection(), dlp.getGtuDirection()))
140                 {
141                     // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_addition}
142                     notify(new TimedEvent<>(Lane.GTU_ADD_EVENT, lane, new Object[] { gtu.getId(), gtu, count },
143                             gtu.getSimulator().getSimulatorTime()));
144                 }
145                 count++;
146             }
147             catch (RemoteException | GTUException exception)
148             {
149                 throw new RuntimeException("Position cannot be obtained for GTU that is registered on a lane", exception);
150             }
151         }
152     }
153 
154     /** {@inheritDoc} */
155     @Override
156     public final void finalizeRecording(final KpiLaneDirection kpiLaneDirection)
157     {
158         ((LaneData) kpiLaneDirection.getLaneData()).getLane().removeListener(this, Lane.GTU_ADD_EVENT);
159         ((LaneData) kpiLaneDirection.getLaneData()).getLane().removeListener(this, Lane.GTU_REMOVE_EVENT);
160         // Lane lane = ((LaneData) kpiLaneDirection.getLaneData()).getLane();
161         // int count = 0;
162         // List<LaneBasedGTU> currentGtus = new ArrayList<>();
163         // try
164         // {
165         // for (LaneBasedGTU gtu : lane.getGtuList())
166         // {
167         // DirectedLanePosition dlp = gtu.getReferencePosition();
168         // if (dlp.getLane().equals(lane) && sameDirection(kpiLaneDirection.getKpiDirection(), dlp.getGtuDirection()))
169         // {
170         // currentGtus.add(gtu);
171         // count++;
172         // }
173         // }
174         // for (LaneBasedGTU gtu : currentGtus)
175         // {
176         // // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_removal, Length position}
177         // notify(new TimedEvent<>(Lane.GTU_REMOVE_EVENT, lane, new Object[] { gtu.getId(), gtu, count },
178         // gtu.getSimulator().getSimulatorTime()));
179         // count--;
180         // }
181         // }
182         // catch (RemoteException | GTUException exception)
183         // {
184         // throw new RuntimeException("Position cannot be obtained for GTU that is registered on a lane", exception);
185         // }
186     }
187 
188     /**
189      * Compares a {@link KpiGtuDirectionality} and a {@link GTUDirectionality}.
190      * @param kpiGtuDirectionality kpi gtu direction
191      * @param gtuDirectionality gtu direction
192      * @return whether both are in the same direction
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     /** {@inheritDoc} */
204     @Override
205     public final void notify(final EventInterface event) throws RemoteException
206     {
207         if (event.getType().equals(LaneBasedGTU.LANEBASED_MOVE_EVENT))
208         {
209             // Payload: [String gtuId, DirectedPoint position, Speed speed, Acceleration acceleration, TurnIndicatorStatus
210             // turnIndicatorStatus, Length odometer, Lane referenceLane, Length positionOnReferenceLane]
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.getSource()));
216         }
217         else if (event.getType().equals(Lane.GTU_ADD_EVENT))
218         {
219             // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_addition}
220             Lane lane = (Lane) event.getSource();
221             // TODO GTUDirectionality from Lane.GTU_ADD_EVENT
222             KpiLaneDirection laneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
223             if (!contains(laneDirection))
224             {
225                 // we are not sampling this LaneDirection
226                 return;
227             }
228             Object[] payload = (Object[]) event.getContent();
229             LaneBasedGTU gtu = (LaneBasedGTU) payload[1];
230             Length position;
231             try
232             {
233                 // TODO Length from Lane.GTU_ADD_EVENT
234                 position = gtu.position(lane, RelativePosition.REFERENCE_POSITION);
235             }
236             catch (GTUException exception)
237             {
238                 throw new RuntimeException(exception);
239             }
240             Speed speed = gtu.getSpeed();
241             Acceleration acceleration = gtu.getAcceleration();
242             processGtuAddEvent(laneDirection, position, speed, acceleration, now(), new GtuData(gtu));
243             LaneDirection lDirection = new LaneDirection(lane, GTUDirectionality.DIR_PLUS);
244             if (isIntervalBased())
245             {
246                 scheduleSamplingEvent(gtu, lDirection);
247             }
248             else
249             {
250                 if (!this.listenersPerGtu.containsKey(gtu.getId()))
251                 {
252                     this.listenersPerGtu.put(gtu.getId(), new HashSet<>());
253                 }
254                 this.listenersPerGtu.get(gtu.getId()).add(lDirection);
255                 gtu.addListener(this, LaneBasedGTU.LANEBASED_MOVE_EVENT, true);
256             }
257         }
258         else if (event.getType().equals(Lane.GTU_REMOVE_EVENT))
259         {
260             // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_removal, Length position}
261             Lane lane = (Lane) event.getSource();
262             // TODO GTUDirectionality from Lane.GTU_ADD_EVENT
263             KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
264             Object[] payload = (Object[]) event.getContent();
265             LaneBasedGTU gtu = (LaneBasedGTU) payload[1];
266             Length position = (Length) payload[3];
267             Speed speed = gtu.getSpeed();
268             Acceleration acceleration = gtu.getAcceleration();
269             processGtuRemoveEvent(kpiLaneDirection, position, speed, acceleration, now(), new GtuData(gtu));
270             LaneDirection lDirection = new LaneDirection(lane, GTUDirectionality.DIR_PLUS);
271             if (isIntervalBased())
272             {
273                 String gtuId = (String) payload[0];
274 
275                 if (this.eventPerGtu.get(gtuId) != null)
276                 {
277                     if (this.eventPerGtu.get(gtuId).containsKey(lDirection))
278                     {
279                         this.simulator.cancelEvent(this.eventPerGtu.get(gtuId).get(lDirection));
280                     }
281                     this.eventPerGtu.get(gtuId).remove(lDirection);
282                     if (this.eventPerGtu.get(gtuId).isEmpty())
283                     {
284                         this.eventPerGtu.remove(gtuId);
285                     }
286                 }
287             }
288             else
289             {
290                 // Should not remove if just added on other lane
291                 this.listenersPerGtu.get(gtu.getId()).remove(lDirection);
292                 if (this.listenersPerGtu.get(gtu.getId()).isEmpty())
293                 {
294                     this.listenersPerGtu.remove(gtu.getId());
295                     gtu.removeListener(this, LaneBasedGTU.LANEBASED_MOVE_EVENT);
296                 }
297             }
298         }
299 
300     }
301 
302     /**
303      * @return whether sampling is interval based
304      */
305     private boolean isIntervalBased()
306     {
307         return this.samplingInterval != null;
308     }
309 
310     /**
311      * Schedules a sampling event for the given gtu on the given lane for the samlping interval from the current time.
312      * @param gtu gtu to sample
313      * @param laneDirection lane direction where the gtu is at
314      */
315     private void scheduleSamplingEvent(final LaneBasedGTU gtu, final LaneDirection laneDirection)
316     {
317         OTSSimTimeDouble simTime = this.simulator.getSimulatorTime().copy();
318         simTime.add(this.samplingInterval);
319         SimEvent<OTSSimTimeDouble> simEvent =
320                 new SimEvent<>(simTime, this, this, "notifySample", new Object[] { gtu, laneDirection });
321         try
322         {
323             this.simulator.scheduleEvent(simEvent);
324         }
325         catch (SimRuntimeException exception)
326         {
327             // should not happen with getSimulatorTime.add()
328             throw new RuntimeException("Scheduling sampling in the past.", exception);
329         }
330         String gtuId = gtu.getId();
331         if (!this.eventPerGtu.containsKey(gtuId))
332         {
333             Map<LaneDirection, SimEvent<OTSSimTimeDouble>> map = new HashMap<>();
334             this.eventPerGtu.put(gtuId, map);
335         }
336         this.eventPerGtu.get(gtuId).put(laneDirection, simEvent);
337     }
338 
339     /**
340      * Samples a gtu and schedules the next sampling event.
341      * @param gtu gtu to sample
342      * @param laneDirection lane direction where the gtu is at
343      */
344     public final void notifySample(final LaneBasedGTU gtu, final LaneDirection laneDirection)
345     {
346         KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(laneDirection.getLane()),
347                 laneDirection.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS);
348         try
349         {
350             this.processGtuMoveEvent(kpiLaneDirection,
351                     gtu.position(laneDirection.getLane(), RelativePosition.REFERENCE_POSITION), gtu.getSpeed(),
352                     gtu.getAcceleration(), now(), new GtuData(gtu));
353         }
354         catch (GTUException exception)
355         {
356             throw new RuntimeException("Requesting position on lane, but the GTU is not on the lane.", exception);
357         }
358         scheduleSamplingEvent(gtu, laneDirection);
359     }
360 
361     /** {@inheritDoc} */
362     @Override
363     public final int hashCode()
364     {
365         final int prime = 31;
366         int result = super.hashCode();
367         result = prime * result + ((this.eventPerGtu == null) ? 0 : this.eventPerGtu.hashCode());
368         result = prime * result + ((this.samplingInterval == null) ? 0 : this.samplingInterval.hashCode());
369         result = prime * result + ((this.simulator == null) ? 0 : this.simulator.hashCode());
370         return result;
371     }
372 
373     /** {@inheritDoc} */
374     @Override
375     public final boolean equals(final Object obj)
376     {
377         if (this == obj)
378         {
379             return true;
380         }
381         if (!super.equals(obj))
382         {
383             return false;
384         }
385         if (getClass() != obj.getClass())
386         {
387             return false;
388         }
389         RoadSampler other = (RoadSampler) obj;
390         if (this.eventPerGtu == null)
391         {
392             if (other.eventPerGtu != null)
393             {
394                 return false;
395             }
396         }
397         else if (!this.eventPerGtu.equals(other.eventPerGtu))
398         {
399             return false;
400         }
401         if (this.samplingInterval == null)
402         {
403             if (other.samplingInterval != null)
404             {
405                 return false;
406             }
407         }
408         else if (!this.samplingInterval.equals(other.samplingInterval))
409         {
410             return false;
411         }
412         if (this.simulator == null)
413         {
414             if (other.simulator != null)
415             {
416                 return false;
417             }
418         }
419         else if (!this.simulator.equals(other.simulator))
420         {
421             return false;
422         }
423         return true;
424     }
425 
426     /** {@inheritDoc} */
427     @Override
428     public final String toString()
429     {
430         return "RoadSampler [samplingInterval=" + this.samplingInterval + ", eventPerGtu=" + this.eventPerGtu + "]";
431     }
432 
433 }