View Javadoc
1   package org.opentrafficsim.core.perception;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import org.djunits.value.vdouble.scalar.Duration;
7   import org.djunits.value.vdouble.scalar.Time;
8   import org.djutils.exceptions.Throw;
9   import org.opentrafficsim.core.perception.AbstractHistorical.Event;
10  import org.opentrafficsim.core.perception.HistoryManager.HistoricalElement;
11  
12  /**
13   * Base class for objects or properties that can be perceived from their actual state in the past. The principle by which a past
14   * state is determined is by storing an internal event for each change to the object. Each event can be reversed, and by working
15   * from a current state backwards, any previous state within the available history can be restored.<br>
16   * <br>
17   * This class couples the historical to a {@code HistoryManager} and in response to a request from the {@code HistoryManager}
18   * will clear old events. Subclasses need to define their own events as extensions to {@code AbstractHistorical.Event}. This
19   * class provides the following methods to subclasses to work with the events.
20   * <ul>
21   * <li>{@code now()}, returns the current time from the {@code HistoryManager}, which needs to be stored with each event.</li>
22   * <li>{@code getEvents(Time)}, returns all events between now and the given time, ordered from recent to old.</li>
23   * <li>{@code getEvent(Time)}, returns the most recent event from before the given time.</li>
24   * <li>{@code getLastEvent()}, returns the most recent event.</li>
25   * <li>{@code removeEvent(Event)}, removes (oldest occurrence off) the event.</li>
26   * <li>{@code addEvent(Event)}, add the event.</li>
27   * </ul>
28   * Typically, any change results in a new event which is added with {@code addEvent(Event)}, where the event stores information
29   * such that the event can be restored. When an old state is requested, one or more events can be obtained with either of the
30   * get methods, after which they are applied to restore a previous state in a manner depending on the nature of the
31   * subclass.<br>
32   * <br>
33   * This class is defined with a single event type parameter {@code E}. Subclasses can use different event classes, so long as
34   * all of them derive from a common ancestor. For instance an 'add' and a 'remove' event that inherit from an abstract super.
35   * <p>
36   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
37   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
38   * <p>
39   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 1 jan. 2018 <br>
40   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
41   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
42   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
43   * @param <T> value type
44   * @param <E> event type
45   */
46  public abstract class AbstractHistorical<T, E extends Event> implements HistoricalElement
47  {
48  
49      /** History manager. */
50      private final HistoryManager historyManager;
51  
52      /** List of events to determine the value at a previous time. */
53      private final List<E> events = new ArrayList<>();
54  
55      /**
56       * Constructor.
57       * @param historyManager HistoryManager; history manager
58       */
59      protected AbstractHistorical(final HistoryManager historyManager)
60      {
61          Throw.whenNull(historyManager, "History manager may not be null.");
62          this.historyManager = historyManager;
63          historyManager.registerHistorical(this);
64      }
65  
66      /**
67       * Returns the current time.
68       * @return Time; current time
69       */
70      protected final Time now()
71      {
72          return this.historyManager.now();
73      }
74  
75      /**
76       * Returns a list of events, ordered last to first, that includes all events <i>after</i> {@code time}.
77       * @param time Time; past time up to which to include events
78       * @return List; list of events, ordered last to first, that includes all events <i>after</i> {@code time}
79       */
80      protected final List<E> getEvents(final Time time)
81      {
82          List<E> list = new ArrayList<>();
83          int i = this.events.size() - 1;
84          while (i >= 0 && this.events.get(i).getTime() > time.si)
85          {
86              list.add(this.events.get(i));
87              i--;
88          }
89          return list;
90      }
91  
92      /**
93       * Returns the most recent event from <i>before</i> or on {@code time}, or the oldest if no such event.
94       * @param time Time; past time at which to obtain event
95       * @return E; most recent event from <i>before</i> {@code time}
96       */
97      protected final E getEvent(final Time time)
98      {
99          E prev = null;
100         for (int i = this.events.size() - 1; i >= 0; i--)
101         {
102             E event = this.events.get(i);
103             if (event.getTime() <= time.si)
104             {
105                 prev = event;
106                 break;
107             }
108         }
109 
110         if (prev == null && !this.events.isEmpty())
111         {
112             return this.events.get(0);
113         }
114 
115         return prev;
116     }
117 
118     /**
119      * Returns the last event.
120      * @return E; last event
121      */
122     protected final E getLastEvent()
123     {
124         return this.events.isEmpty() ? null : this.events.get(this.events.size() - 1);
125     }
126 
127     /**
128      * Returns whether the state at the given time is equal to the state at the current time.
129      * @param time Time; time
130      * @return boolean; whether the state at the given time is equal to the state at the current time
131      */
132     protected final boolean isLastState(final Time time)
133     {
134         return this.events.isEmpty() ? true : this.events.get(this.events.size() - 1).getTime() <= time.si;
135     }
136 
137     /**
138      * Removes the given event.
139      * @param event E; event to remove
140      */
141     protected final void removeEvent(final E event)
142     {
143         this.events.remove(event);
144     }
145 
146     /**
147      * Adds the event to the list of events.
148      * @param event E; event to add
149      */
150     protected final void addEvent(final E event)
151     {
152         this.events.add(event);
153     }
154 
155     /** {@inheritDoc} */
156     @Override
157     public final void cleanUpHistory(final Duration history)
158     {
159         double past = now().si - history.si;
160         while (this.events.size() > 1 && this.events.get(0).getTime() < past)
161         {
162             this.events.remove(0);
163         }
164     }
165 
166     /**
167      * Interface for event types.
168      * <p>
169      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
170      * <br>
171      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
172      * <p>
173      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 1 jan. 2018 <br>
174      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
175      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
176      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
177      */
178     interface Event
179     {
180 
181         /**
182          * Returns the time of this event.
183          * @return double; time of this event
184          */
185         double getTime();
186 
187     }
188 
189     /**
190      * Standard event which stores a time and value.
191      * <p>
192      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
193      * <br>
194      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
195      * <p>
196      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 1 jan. 2018 <br>
197      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
198      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
199      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
200      * @param <T> value type
201      */
202     public static class EventValue<T> implements Event
203     {
204 
205         /** Time of event. */
206         private final double time;
207 
208         /** Value of event. */
209         private final T value;
210 
211         /**
212          * Constructor.
213          * @param time double; time of event
214          * @param value T; value of event
215          */
216         public EventValue(final double time, final T value)
217         {
218             this.time = time;
219             this.value = value;
220         }
221 
222         /** {@inheritDoc} */
223         @Override
224         public double getTime()
225         {
226             return this.time;
227         }
228 
229         /**
230          * Returns the value of this event.
231          * @return T; value of this event
232          */
233         public T getValue()
234         {
235             return this.value;
236         }
237 
238         /** {@inheritDoc} */
239         @Override
240         public String toString()
241         {
242             return "EventValue [time=" + this.time + ", value=" + this.value + "]";
243         }
244 
245     }
246 
247 }