AbstractHistorical.java
package org.opentrafficsim.core.perception;
import java.util.ArrayList;
import java.util.List;
import org.djunits.value.vdouble.scalar.Duration;
import org.djunits.value.vdouble.scalar.Time;
import org.djutils.exceptions.Throw;
import org.opentrafficsim.core.perception.AbstractHistorical.Event;
import org.opentrafficsim.core.perception.HistoryManager.HistoricalElement;
/**
* Base class for objects or properties that can be perceived from their actual state in the past. The principle by which a past
* state is determined is by storing an internal event for each change to the object. Each event can be reversed, and by working
* from a current state backwards, any previous state within the available history can be restored.<br>
* <br>
* This class couples the historical to a {@code HistoryManager} and in response to a request from the {@code HistoryManager}
* will clear old events. Subclasses need to define their own events as extensions to {@code AbstractHistorical.Event}. This
* class provides the following methods to subclasses to work with the events.
* <ul>
* <li>{@code now()}, returns the current time from the {@code HistoryManager}, which needs to be stored with each event.</li>
* <li>{@code getEvents(Time)}, returns all events between now and the given time, ordered from recent to old.</li>
* <li>{@code getEvent(Time)}, returns the most recent event from before the given time.</li>
* <li>{@code getLastEvent()}, returns the most recent event.</li>
* <li>{@code removeEvent(Event)}, removes (oldest occurrence off) the event.</li>
* <li>{@code addEvent(Event)}, add the event.</li>
* </ul>
* Typically, any change results in a new event which is added with {@code addEvent(Event)}, where the event stores information
* such that the event can be restored. When an old state is requested, one or more events can be obtained with either of the
* get methods, after which they are applied to restore a previous state in a manner depending on the nature of the
* subclass.<br>
* <br>
* This class is defined with a single event type parameter {@code E}. Subclasses can use different event classes, so long as
* all of them derive from a common ancestor. For instance an 'add' and a 'remove' event that inherit from an abstract super.
* <p>
* Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
* </p>
* @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
* @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
* @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
* @param <T> value type
* @param <E> event type
*/
public abstract class AbstractHistorical<T, E extends Event> implements HistoricalElement
{
/** History manager. */
private final HistoryManager historyManager;
/** List of events to determine the value at a previous time. */
private final List<E> events = new ArrayList<>();
/**
* Constructor.
* @param historyManager HistoryManager; history manager
*/
protected AbstractHistorical(final HistoryManager historyManager)
{
Throw.whenNull(historyManager, "History manager may not be null.");
this.historyManager = historyManager;
historyManager.registerHistorical(this);
}
/**
* Returns the current time.
* @return Time; current time
*/
protected final Time now()
{
return this.historyManager.now();
}
/**
* Returns a list of events, ordered last to first, that includes all events <i>after</i> {@code time}.
* @param time Time; past time up to which to include events
* @return List; list of events, ordered last to first, that includes all events <i>after</i> {@code time}
*/
protected final List<E> getEvents(final Time time)
{
List<E> list = new ArrayList<>();
int i = this.events.size() - 1;
while (i >= 0 && this.events.get(i).getTime() > time.si)
{
list.add(this.events.get(i));
i--;
}
return list;
}
/**
* Returns the most recent event from <i>before</i> or on {@code time}, or the oldest if no such event.
* @param time Time; past time at which to obtain event
* @return E; most recent event from <i>before</i> {@code time}
*/
protected final E getEvent(final Time time)
{
E prev = null;
for (int i = this.events.size() - 1; i >= 0; i--)
{
E event = this.events.get(i);
if (event.getTime() <= time.si)
{
prev = event;
break;
}
}
if (prev == null && !this.events.isEmpty())
{
return this.events.get(0);
}
return prev;
}
/**
* Returns the last event.
* @return E; last event
*/
protected final E getLastEvent()
{
return this.events.isEmpty() ? null : this.events.get(this.events.size() - 1);
}
/**
* Returns whether the state at the given time is equal to the state at the current time.
* @param time Time; time
* @return boolean; whether the state at the given time is equal to the state at the current time
*/
protected final boolean isLastState(final Time time)
{
return this.events.isEmpty() ? true : this.events.get(this.events.size() - 1).getTime() <= time.si;
}
/**
* Removes the given event.
* @param event E; event to remove
*/
protected final void removeEvent(final E event)
{
this.events.remove(event);
}
/**
* Adds the event to the list of events.
* @param event E; event to add
*/
protected final void addEvent(final E event)
{
this.events.add(event);
}
/** {@inheritDoc} */
@Override
public final void cleanUpHistory(final Duration history)
{
double past = now().si - history.si;
while (this.events.size() > 1 && this.events.get(0).getTime() < past)
{
this.events.remove(0);
}
}
/**
* Interface for event types.
* <p>
* Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* <br>
* BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
* </p>
* @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
* @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
* @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
*/
interface Event
{
/**
* Returns the time of this event.
* @return double; time of this event
*/
double getTime();
}
/**
* Standard event which stores a time and value.
* <p>
* Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* <br>
* BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
* </p>
* @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
* @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
* @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
* @param <T> value type
*/
public static class EventValue<T> implements Event
{
/** Time of event. */
private final double time;
/** Value of event. */
private final T value;
/**
* Constructor.
* @param time double; time of event
* @param value T; value of event
*/
public EventValue(final double time, final T value)
{
this.time = time;
this.value = value;
}
/** {@inheritDoc} */
@Override
public double getTime()
{
return this.time;
}
/**
* Returns the value of this event.
* @return T; value of this event
*/
public T getValue()
{
return this.value;
}
/** {@inheritDoc} */
@Override
public String toString()
{
return "EventValue [time=" + this.time + ", value=" + this.value + "]";
}
}
}