View Javadoc
1   package org.opentrafficsim.road.gtu.lane.perception.categories.neighbors;
2   
3   import java.util.HashMap;
4   import java.util.HashSet;
5   import java.util.Map;
6   import java.util.Set;
7   import java.util.SortedSet;
8   
9   import org.djunits.value.vdouble.scalar.Duration;
10  import org.djunits.value.vdouble.scalar.Length;
11  import org.djunits.value.vdouble.scalar.Time;
12  import org.opentrafficsim.base.TimeStampedObject;
13  import org.opentrafficsim.base.parameters.ParameterException;
14  import org.opentrafficsim.base.parameters.ParameterTypeDuration;
15  import org.opentrafficsim.base.parameters.ParameterTypes;
16  import org.opentrafficsim.base.parameters.Parameters;
17  import org.opentrafficsim.core.gtu.GTUException;
18  import org.opentrafficsim.core.gtu.perception.PerceptionException;
19  import org.opentrafficsim.core.network.LateralDirectionality;
20  import org.opentrafficsim.core.network.NetworkException;
21  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
22  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
23  import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
24  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
25  import org.opentrafficsim.road.gtu.lane.perception.categories.AbstractDelayedPerceptionCategory;
26  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
27  
28  import nl.tudelft.simulation.dsol.SimRuntimeException;
29  
30  /**
31   * <p>
32   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
33   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
34   * <p>
35   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 17 feb. 2017 <br>
36   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
37   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
38   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
39   */
40  public abstract class AbstractDelayedNeighborsPerception extends AbstractDelayedPerceptionCategory
41          implements NeighborsPerception
42  {
43  
44      /** */
45      private static final long serialVersionUID = 20170217L;
46  
47      /** Reaction time parameter type. */
48      protected static final ParameterTypeDuration TR = ParameterTypes.TR;
49  
50      /** Time step parameter type. */
51      protected static final ParameterTypeDuration DT = ParameterTypes.DT;
52  
53      /** First update time. */
54      private Time initialTime = null;
55  
56      /** Wrapped direct perception. */
57      private final DirectNeighborsPerception direct;
58  
59      /** Reaction time. */
60      private Duration reactionTime = null;
61  
62      /** Time step of planner. */
63      private Duration plannerTimeStep = null;
64  
65      /** Remainder between reaction time and planner time step. */
66      private Duration remainder = null;
67  
68      /** Info type id base for first leaders. */
69      public static final String FIRSTLEADERS = "firstLeaders";
70  
71      /** Info type id base for first followers. */
72      public static final String FIRSTFOLLOWERS = "firstFollower";
73  
74      /** Info type id base for gtu alongside. */
75      public static final String GTUALONGSIDE = "gtuAlongside";
76  
77      /** Info type id base for leaders. */
78      public static final String LEADERS = "leaders";
79  
80      /** Info type id base for followers. */
81      public static final String FOLLOWERS = "followers";
82  
83      /** Info type id for cross-section. */
84      public static final NeighborsInfoType<SortedSet<RelativeLane>> CROSSSECTION = new NeighborsInfoType<>("cross-section");
85  
86      /** Id for odometer info type. */
87      public static final NeighborsInfoType<Length> ODOMETER = new NeighborsInfoType<>("odometer");
88  
89      /** Override for left lane change. */
90      private boolean gtuAlongsideLeftOverride = false;
91  
92      /** Override for right lane change. */
93      private boolean gtuAlongsideRightOverride = false;
94  
95      /**
96       * Constructor.
97       * @param perception LanePerception; perception
98       */
99      public AbstractDelayedNeighborsPerception(final LanePerception perception)
100     {
101         super(perception);
102         this.direct = new DirectNeighborsPerception(perception, HeadwayGtuType.COPY);
103     }
104 
105     /** {@inheritDoc} */
106     @Override
107     public final void updateAll() throws GTUException, NetworkException, ParameterException
108     {
109 
110         if (this.remainder == null)
111         {
112             try
113             {
114                 // TODO The reaction time may differ between observed objects and vary over time
115                 Parameters params = getPerception().getGtu().getParameters();
116                 this.reactionTime = params.getParameter(TR);
117                 this.plannerTimeStep = params.getParameter(DT);
118                 double rem;
119                 if (this.reactionTime.eq0())
120                 {
121                     rem = 0;
122                 }
123                 else if (this.reactionTime.gt(this.plannerTimeStep))
124                 {
125                     rem = this.reactionTime.si % this.plannerTimeStep.si;
126                 }
127                 else
128                 {
129                     rem = this.plannerTimeStep.si - this.reactionTime.si;
130                 }
131                 this.remainder = Duration.createSI(rem);
132             }
133             catch (ParameterException | GTUException exception)
134             {
135                 throw new RuntimeException("Exception while setting up delayed neighors perception.", exception);
136             }
137         }
138 
139         // direct perception in first few time steps; build up history
140         Time now = getPerception().getGtu().getSimulator().getSimulatorTime();
141         if (this.initialTime == null)
142         {
143             this.initialTime = now;
144         }
145         if (now.minus(this.initialTime).le(this.reactionTime))
146         {
147             updateAllDelayed();
148             return;
149         }
150 
151         if (this.remainder.eq0())
152         {
153             // reaction time is multiple of time step, just do it now
154             updateAllDelayed();
155         }
156         else
157         {
158             // schedule actual update slightly in the future: this will be the snapshot for a future time step
159             Time scheduledTime = now.plus(this.remainder);
160             try
161             {
162                 getPerception().getGtu().getSimulator().scheduleEventAbs(scheduledTime, this, this, "updateAllDelayed", null);
163             }
164             catch (SimRuntimeException exception)
165             {
166                 throw new RuntimeException("Scheduling perception update in the past.", exception);
167             }
168         }
169 
170         /*
171          * During the reaction time, an instantaneous lane change by a neighbor may be performed, which may cause an
172          * unreasonable lane change of the subject vehicle if it is not considered. Therefore, the 'gtuAlongSide' information is
173          * amended with a current snapshot of the surroundings. If the current first leaders/followers contains a GTU that the
174          * delayed leaders/followers do not contain, and that is within 50m, 'gtuAlongSide' is overruled with 'true', preventing
175          * a lane change.
176          */
177         if (getPerception().getLaneStructure().getExtendedCrossSection().contains(RelativeLane.LEFT))
178         {
179             this.direct.updateFirstFollowers(LateralDirectionality.LEFT);
180             this.direct.updateFirstLeaders(LateralDirectionality.LEFT);
181             this.direct.updateGtuAlongside(LateralDirectionality.LEFT);
182             this.gtuAlongsideLeftOverride = newFirstLeaderOrFollower(getFollowers(RelativeLane.LEFT),
183                     this.direct.getFirstFollowers(LateralDirectionality.LEFT))
184                     || newFirstLeaderOrFollower(getLeaders(RelativeLane.LEFT),
185                             this.direct.getFirstLeaders(LateralDirectionality.LEFT))
186                     || this.direct.isGtuAlongside(LateralDirectionality.LEFT);
187         }
188         if (getPerception().getLaneStructure().getExtendedCrossSection().contains(RelativeLane.RIGHT))
189         {
190             this.direct.updateFirstFollowers(LateralDirectionality.RIGHT);
191             this.direct.updateFirstLeaders(LateralDirectionality.RIGHT);
192             this.direct.updateGtuAlongside(LateralDirectionality.RIGHT);
193             this.gtuAlongsideRightOverride = newFirstLeaderOrFollower(getFollowers(RelativeLane.RIGHT),
194                     this.direct.getFirstFollowers(LateralDirectionality.RIGHT))
195                     || newFirstLeaderOrFollower(getLeaders(RelativeLane.RIGHT),
196                             this.direct.getFirstLeaders(LateralDirectionality.RIGHT))
197                     || this.direct.isGtuAlongside(LateralDirectionality.RIGHT);
198         }
199 
200     }
201 
202     /**
203      * Returns whether there is a gtu in the current set that is not present in the delayed set.
204      * @param delayedSet Iterable&lt;? extends HeadwayGTU&gt;; delayed set
205      * @param currentSet Set&lt;? extends HeadwayGTU&gt;; current set
206      * @return whether there is a gtu in the current set that is not present in the delayed set
207      */
208     private boolean newFirstLeaderOrFollower(final Iterable<? extends HeadwayGTU> delayedSet,
209             final Set<? extends HeadwayGTU> currentSet)
210     {
211         Set<String> set = new HashSet<>();
212         for (HeadwayGTU gtu : delayedSet)
213         {
214             set.add(gtu.getId());
215         }
216         for (HeadwayGTU gtu : currentSet)
217         {
218             if (!set.contains(gtu.getId()) && gtu.getDistance().si < 50)
219             {
220                 return true;
221             }
222         }
223         return false;
224     }
225 
226     /**
227      * Returns whether to override the gtu alongside boolean as true.
228      * @param lat LateralDirectionality; lateral direction
229      * @return whether to override the gtu alongside boolean as true
230      */
231     public final boolean isGtuAlongsideOverride(final LateralDirectionality lat)
232     {
233         return lat.isLeft() ? this.gtuAlongsideLeftOverride : this.gtuAlongsideRightOverride;
234     }
235 
236     /**
237      * Performs actual update.
238      * @throws ParameterException if parameter is not present or is given a wrong value
239      * @throws NetworkException on error in the network
240      * @throws GTUException if not initialized
241      */
242     // TODO private when DSOL allows
243     protected void updateAllDelayed() throws GTUException, NetworkException, ParameterException
244     {
245 
246         try
247         {
248             getGtu().getReferencePosition();
249         }
250         catch (GTUException exception)
251         {
252             // GTU was destroyed
253             return;
254         }
255 
256         this.direct.updateAll();
257         // below code is a copy of the updateAll() method in the direct perception TODO structure better
258         if (getPerception().getLaneStructure().getExtendedCrossSection().contains(RelativeLane.LEFT))
259         {
260             updateFirstLeaders(LateralDirectionality.LEFT);
261             updateFirstFollowers(LateralDirectionality.LEFT);
262             updateGtuAlongside(LateralDirectionality.LEFT);
263         }
264         if (getPerception().getLaneStructure().getExtendedCrossSection().contains(RelativeLane.RIGHT))
265         {
266             updateFirstLeaders(LateralDirectionality.RIGHT);
267             updateFirstFollowers(LateralDirectionality.RIGHT);
268             updateGtuAlongside(LateralDirectionality.RIGHT);
269         }
270         for (RelativeLane lane : getPerception().getLaneStructure().getExtendedCrossSection())
271         {
272             updateLeaders(lane);
273             updateFollowers(lane);
274         }
275         setInfo(CROSSSECTION,
276                 new TimeStampedObject<>(getPerception().getLaneStructure().getExtendedCrossSection(), getTimestamp()));
277         setInfo(ODOMETER, new TimeStampedObject<>(getGtu().getOdometer(), getTimestamp()));
278     }
279 
280     /** {@inheritDoc} */
281     @Override
282     public final void updateFirstLeaders(final LateralDirectionality lat)
283             throws ParameterException, GTUException, NetworkException
284     {
285         setInfo(NeighborsInfoType.getSortedSetType(FIRSTLEADERS), new RelativeLane(lat, 1),
286                 this.direct.getTimeStampedFirstLeaders(lat));
287     }
288 
289     /** {@inheritDoc} */
290     @Override
291     public final void updateFirstFollowers(final LateralDirectionality lat)
292             throws GTUException, ParameterException, NetworkException
293     {
294         setInfo(NeighborsInfoType.getSortedSetType(FIRSTFOLLOWERS), new RelativeLane(lat, 1),
295                 this.direct.getTimeStampedFirstFollowers(lat));
296     }
297 
298     /** {@inheritDoc} */
299     @Override
300     public final void updateGtuAlongside(final LateralDirectionality lat) throws GTUException, ParameterException
301     {
302         setInfo(NeighborsInfoType.getBooleanType(GTUALONGSIDE), new RelativeLane(lat, 1),
303                 this.direct.isGtuAlongsideTimeStamped(lat));
304     }
305 
306     /** {@inheritDoc} */
307     @Override
308     public final void updateLeaders(final RelativeLane lane) throws ParameterException, GTUException, NetworkException
309     {
310         setInfo(NeighborsInfoType.getIterableType(LEADERS), lane, this.direct.getTimeStampedLeaders(lane));
311     }
312 
313     /** {@inheritDoc} */
314     @Override
315     public final void updateFollowers(final RelativeLane lane) throws GTUException, NetworkException, ParameterException
316     {
317         setInfo(NeighborsInfoType.getIterableType(FOLLOWERS), lane, this.direct.getTimeStampedFollowers(lane));
318     }
319 
320     /**
321      * Returns the cross-section on which the most recent observed neighbors were determined.
322      * @return cross-section on which the most recent observed neighbors were determined
323      */
324     public final SortedSet<RelativeLane> getDelayedCrossSection()
325     {
326         try
327         {
328             return getInfo(CROSSSECTION).getObject();
329         }
330         catch (PerceptionException exception)
331         {
332             throw new RuntimeException("Crosssection was not perceived.", exception);
333         }
334     }
335 
336     /**
337      * Delayed information about the type of the neighbors. <br>
338      * @param <T> data type of info
339      */
340     public static final class NeighborsInfoType<T> extends DelayedInfoType<T>
341     {
342 
343         /** Map of id's and lane info types. */
344         private static final Map<String, NeighborsInfoType<?>> LANEINFOTYPES = new HashMap<>();
345 
346         /**
347          * Construct new info.
348          * @param id String; id
349          */
350         public NeighborsInfoType(final String id)
351         {
352             super(id, TR);
353         }
354 
355         /**
356          * Returns a (cached) info type for a sorted set of GTU's.
357          * @param id String; id
358          * @return info type
359          */
360         @SuppressWarnings("unchecked")
361         public static NeighborsInfoType<SortedSet<HeadwayGTU>> getSortedSetType(final String id)
362         {
363             if (!LANEINFOTYPES.containsKey(id))
364             {
365                 LANEINFOTYPES.put(id, new NeighborsInfoType<SortedSet<HeadwayGTU>>(id));
366             }
367             return (NeighborsInfoType<SortedSet<HeadwayGTU>>) LANEINFOTYPES.get(id);
368         }
369 
370         /**
371          * Returns a (cached) info type for a sorted set of GTU's.
372          * @param id String; id
373          * @return info type
374          */
375         @SuppressWarnings("unchecked")
376         public static NeighborsInfoType<PerceptionCollectable<HeadwayGTU, LaneBasedGTU>> getIterableType(final String id)
377         {
378             if (!LANEINFOTYPES.containsKey(id))
379             {
380                 LANEINFOTYPES.put(id, new NeighborsInfoType<SortedSet<HeadwayGTU>>(id));
381             }
382             return (NeighborsInfoType<PerceptionCollectable<HeadwayGTU, LaneBasedGTU>>) LANEINFOTYPES.get(id);
383         }
384 
385         /**
386          * Returns a (cached) info type for a sorted set of GTU's.
387          * @param id String; id
388          * @return info type
389          */
390         @SuppressWarnings("unchecked")
391         public static NeighborsInfoType<Boolean> getBooleanType(final String id)
392         {
393             if (!LANEINFOTYPES.containsKey(id))
394             {
395                 LANEINFOTYPES.put(id, new NeighborsInfoType<SortedSet<HeadwayGTU>>(id));
396             }
397             return (NeighborsInfoType<Boolean>) LANEINFOTYPES.get(id);
398         }
399 
400         /** {@inheritDoc} */
401         @Override
402         public String toString()
403         {
404             return "NeighborsInfoType []";
405         }
406 
407     }
408 
409 }