View Javadoc
1   package org.opentrafficsim.road.gtu.lane.perception.categories;
2   
3   import java.util.ArrayList;
4   import java.util.HashMap;
5   import java.util.List;
6   
7   import org.djunits.value.vdouble.scalar.Duration;
8   import org.djunits.value.vdouble.scalar.Time;
9   import org.opentrafficsim.base.TimeStampedObject;
10  import org.opentrafficsim.base.Type;
11  import org.opentrafficsim.core.gtu.GTUException;
12  import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
13  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
14  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeDuration;
15  import org.opentrafficsim.core.gtu.perception.PerceptionCategory;
16  import org.opentrafficsim.core.gtu.perception.PerceptionException;
17  import org.opentrafficsim.core.network.LateralDirectionality;
18  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
19  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
20  
21  import nl.tudelft.simulation.language.Throw;
22  
23  /**
24   * Utility superclass for perception categories with single delayed snapshots.
25   * <p>
26   * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
27   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
28   * <p>
29   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 14 feb. 2017 <br>
30   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
31   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
32   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
33   */
34  public abstract class AbstractDelayedPerceptionCategory extends LaneBasedAbstractPerceptionCategory
35          implements PerceptionCategory
36  {
37  
38      /** Margin of 1 millisecond. */
39      private static final Duration MARGIN = Duration.createSI(0.001);
40  
41      /** */
42      private static final long serialVersionUID = 20170217L;
43  
44      /**
45       * @param perception perception
46       */
47      public AbstractDelayedPerceptionCategory(final LanePerception perception)
48      {
49          super(perception);
50      }
51  
52      /** Map of info type and list of time stamped data of that info type. */
53      private final HashMap<DelayedInfoType<?>, HashMap<RelativeLane, List<TimeStampedObject<?>>>> map = new HashMap<>();
54  
55      /**
56       * Set info of given delayed info type, not pertaining to any lane.
57       * @param delayedInfoType info type
58       * @param info info
59       * @param <T> data type of delayed info
60       */
61      public <T> void setInfo(final DelayedInfoType<T> delayedInfoType, final TimeStampedObject<T> info)
62      {
63          setInfo(delayedInfoType, null, info);
64      }
65  
66      /**
67       * Set info of given delayed info type, pertaining to a lane.
68       * @param delayedInfoType info type
69       * @param lane lane, may be {@code null}
70       * @param info info
71       * @param <T> data type of delayed info
72       */
73      public <T> void setInfo(final DelayedInfoType<T> delayedInfoType, final RelativeLane lane, final TimeStampedObject<T> info)
74      {
75          Throw.whenNull(delayedInfoType, "Delayed info type may not be null.");
76          Throw.whenNull(info, "Info may not be null.");
77          if (!this.map.containsKey(delayedInfoType))
78          {
79              this.map.put(delayedInfoType, new HashMap<>());
80          }
81          if (!this.map.get(delayedInfoType).containsKey(lane))
82          {
83              this.map.get(delayedInfoType).put(lane, new ArrayList<TimeStampedObject<?>>());
84          }
85          List<TimeStampedObject<?>> list = this.map.get(delayedInfoType).get(lane);
86          if (!list.isEmpty())
87          {
88              Throw.when(!list.isEmpty() && info.getTimestamp().le(list.get(list.size() - 1).getTimestamp()),
89                      RuntimeException.class,
90                      "Setting delayed info for type %s with timestamp %s while info with timestamp %s is already present.",
91                      delayedInfoType, info.getTimestamp(), list.get(list.size() - 1).getTimestamp());
92          }
93  
94          // append data at end
95          list.add(info);
96      }
97  
98      /**
99       * Returns the most recent info of the given type, that is older than the delay. If all data is more recent than the delay,
100      * the oldest data is returned. If no data is present, an exception is thrown.
101      * @param delayedInfoType info type
102      * @param <T> data type of the info type
103      * @return info of the given type
104      * @throws PerceptionException if info was not perceived
105      */
106     public <T> TimeStampedObject<T> getInfo(final DelayedInfoType<T> delayedInfoType) throws PerceptionException
107     {
108         return getInfo(delayedInfoType, null);
109     }
110 
111     /**
112      * Returns the most recent info of the given type, that is older than the delay. If all data is more recent than the delay,
113      * the oldest data is returned. If no data is present, an exception is thrown.
114      * @param delayedInfoType info type
115      * @param lane lane the data pertains to, may be {@code null}
116      * @param <T> data type of the info type
117      * @return info of the given type
118      * @throws PerceptionException if info was not perceived
119      */
120     @SuppressWarnings("unchecked")
121     public <T> TimeStampedObject<T> getInfo(final DelayedInfoType<T> delayedInfoType, final RelativeLane lane)
122             throws PerceptionException
123     {
124         Throw.whenNull(delayedInfoType, "Delayed info type may not be null.");
125         Throw.when(!this.map.containsKey(delayedInfoType), PerceptionException.class,
126                 "Perception does not contain any data for info type %s.", delayedInfoType);
127         Throw.when(!this.map.get(delayedInfoType).containsKey(lane), PerceptionException.class,
128                 "Perception does not contain any data for info type %s for lane %s.", delayedInfoType, lane);
129         List<TimeStampedObject<?>> list = this.map.get(delayedInfoType).get(lane);
130         Throw.when(list.isEmpty(), RuntimeException.class, "Perception does not contain any data for info type %s.",
131                 delayedInfoType);
132 
133         // remove old data if required
134         BehavioralCharacteristics bc;
135         Time now;
136         try
137         {
138             bc = getPerception().getGtu().getBehavioralCharacteristics();
139             now = getPerception().getGtu().getSimulator().getSimulatorTime().getTime();
140         }
141         catch (GTUException exception)
142         {
143             throw new RuntimeException("GTU not yet initialized.", exception);
144         }
145         Time delayedTime;
146         try
147         {
148             delayedTime = now.minus(bc.getParameter(delayedInfoType.getDelayParameter())).plus(MARGIN);
149         }
150         catch (ParameterException exception)
151         {
152             throw new RuntimeException("Delay parameter not found.", exception);
153         }
154         while (list.size() > 1 && list.get(1).getTimestamp().le(delayedTime))
155         {
156             list.remove(0);
157         }
158 
159         return (TimeStampedObject<T>) list.get(0);
160     }
161 
162     /**
163      * Move data coupled to a lane to another lane to account for a lane change. The tactical planner needs to call this exactly
164      * when it flips logic concerning the origin and target lane.
165      * @param dir direction of lane change
166      */
167     public void changeLane(final LateralDirectionality dir)
168     {
169         for (DelayedInfoType<?> delayedInfoType : this.map.keySet())
170         {
171             HashMap<RelativeLane, List<TimeStampedObject<?>>> newMap = new HashMap<>();
172             for (RelativeLane lane : this.map.get(delayedInfoType).keySet())
173             {
174                 if (lane != null)
175                 {
176                     newMap.put(dir.isLeft() ? lane.getRight() : lane.getLeft(), this.map.get(delayedInfoType).get(lane));
177                 }
178                 else
179                 {
180                     newMap.put(lane, this.map.get(delayedInfoType).get(lane));
181                 }
182             }
183             this.map.put(delayedInfoType, newMap);
184         }
185     }
186 
187     /**
188      * Superclass for delayed info.
189      * <p>
190      * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
191      * <br>
192      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
193      * <p>
194      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 14 feb. 2017 <br>
195      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
196      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
197      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
198      * @param <T> type of information.
199      */
200     public static class DelayedInfoType<T> extends Type<DelayedInfoType<T>>
201     {
202 
203         /** Id. */
204         private final String id;
205 
206         /** Parameter for delay. */
207         private final ParameterTypeDuration delayParameter;
208 
209         /**
210          * Constructor.
211          * @param id id
212          * @param delayParameter delay parameter type
213          */
214         public DelayedInfoType(final String id, final ParameterTypeDuration delayParameter)
215         {
216             this.id = id;
217             this.delayParameter = delayParameter;
218         }
219 
220         /**
221          * Returns the id.
222          * @return id
223          */
224         public final String getId()
225         {
226             return this.getId();
227         }
228 
229         /**
230          * Returns the delay parameter type.
231          * @return delayParameter
232          */
233         public ParameterTypeDuration getDelayParameter()
234         {
235             return this.delayParameter;
236         }
237 
238         /** {@inheritDoc} */
239         @Override
240         public final int hashCode()
241         {
242             final int prime = 31;
243             int result = 1;
244             result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
245             return result;
246         }
247 
248         /** {@inheritDoc} */
249         @Override
250         public final boolean equals(final Object obj)
251         {
252             if (this == obj)
253             {
254                 return true;
255             }
256             if (obj == null)
257             {
258                 return false;
259             }
260             if (getClass() != obj.getClass())
261             {
262                 return false;
263             }
264             DelayedInfoType<?> other = (DelayedInfoType<?>) obj;
265             if (this.id == null)
266             {
267                 if (other.id != null)
268                 {
269                     return false;
270                 }
271             }
272             else if (!this.id.equals(other.id))
273             {
274                 return false;
275             }
276             return true;
277         }
278 
279         /** {@inheritDoc} */
280         @Override
281         public String toString()
282         {
283             return "DelayedInfoType [id=" + this.id + ", delayParameter=" + this.delayParameter + "]";
284         }
285 
286     }
287 
288 }