package org.opentrafficsim.ahfe;
import java.util.ArrayList;
import java.util.LinkedHashMap;
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.base.Identifiable;
import org.opentrafficsim.base.TimeStampedObject;
import org.opentrafficsim.base.Type;
import org.opentrafficsim.base.parameters.ParameterException;
import org.opentrafficsim.base.parameters.ParameterTypeDuration;
import org.opentrafficsim.base.parameters.Parameters;
import org.opentrafficsim.core.gtu.GTUException;
import org.opentrafficsim.core.gtu.perception.PerceptionException;
import org.opentrafficsim.core.network.LateralDirectionality;
import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
import org.opentrafficsim.road.gtu.lane.perception.categories.LaneBasedAbstractPerceptionCategory;
import org.opentrafficsim.road.gtu.lane.perception.categories.LaneBasedPerceptionCategory;
/**
* Utility superclass for perception categories with single delayed snapshots.
*
* Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License.
*
* @version $Revision$, $LastChangedDate$, by $Author$, initial version 14 feb. 2017
* @author Alexander Verbraeck
* @author Peter Knoppers
* @author Wouter Schakel
*/
@Deprecated
public abstract class AbstractDelayedPerceptionCategory extends LaneBasedAbstractPerceptionCategory
implements LaneBasedPerceptionCategory
{
/** Margin of 1 millisecond. */
private static final Duration MARGIN = Duration.instantiateSI(0.001);
/** */
private static final long serialVersionUID = 20170217L;
/**
* @param perception LanePerception; perception
*/
public AbstractDelayedPerceptionCategory(final LanePerception perception)
{
super(perception);
}
/** Map of info type and list of time stamped data of that info type. */
private final LinkedHashMap, LinkedHashMap>>> map =
new LinkedHashMap<>();
/**
* Set info of given delayed info type, not pertaining to any lane.
* @param delayedInfoType DelayedInfoType<T>; info type
* @param info TimeStampedObject<T>; info
* @param data type of delayed info
*/
public final void setInfo(final DelayedInfoType delayedInfoType, final TimeStampedObject info)
{
setInfo(delayedInfoType, null, info);
}
/**
* Set info of given delayed info type, pertaining to a lane.
* @param delayedInfoType DelayedInfoType<T>; info type
* @param lane RelativeLane; lane, may be {@code null}
* @param info TimeStampedObject<T>; info
* @param data type of delayed info
*/
public final void setInfo(final DelayedInfoType delayedInfoType, final RelativeLane lane,
final TimeStampedObject info)
{
Throw.whenNull(delayedInfoType, "Delayed info type may not be null.");
Throw.whenNull(info, "Info may not be null.");
if (!this.map.containsKey(delayedInfoType))
{
this.map.put(delayedInfoType, new LinkedHashMap<>());
}
if (!this.map.get(delayedInfoType).containsKey(lane))
{
this.map.get(delayedInfoType).put(lane, new ArrayList>());
}
List> list = this.map.get(delayedInfoType).get(lane);
if (!list.isEmpty())
{
Throw.when(!list.isEmpty() && info.getTimestamp().le(list.get(list.size() - 1).getTimestamp()),
RuntimeException.class,
"Setting delayed info for type %s with timestamp %s while info with timestamp %s is already present.",
delayedInfoType, info.getTimestamp(), list.get(list.size() - 1).getTimestamp());
}
// append data at end
list.add(info);
}
/**
* Returns the most recent info of the given type, that is older than the delay. If all data is more recent than the delay,
* the oldest data is returned. If no data is present, an exception is thrown.
* @param delayedInfoType DelayedInfoType<T>; info type
* @param data type of the info type
* @return info of the given type
* @throws PerceptionException if info was not perceived
*/
public final TimeStampedObject getInfo(final DelayedInfoType delayedInfoType) throws PerceptionException
{
return getInfo(delayedInfoType, null);
}
/**
* Returns the most recent info of the given type, that is older than the delay. If all data is more recent than the delay,
* the oldest data is returned. If no data is present, an exception is thrown.
* @param delayedInfoType DelayedInfoType<T>; info type
* @param lane RelativeLane; lane the data pertains to, may be {@code null}
* @param data type of the info type
* @return info of the given type
* @throws PerceptionException if info was not perceived
*/
@SuppressWarnings("unchecked")
public final TimeStampedObject getInfo(final DelayedInfoType delayedInfoType, final RelativeLane lane)
throws PerceptionException
{
Throw.whenNull(delayedInfoType, "Delayed info type may not be null.");
Throw.when(!this.map.containsKey(delayedInfoType), PerceptionException.class,
"Perception does not contain any data for info type %s.", delayedInfoType);
Throw.when(!this.map.get(delayedInfoType).containsKey(lane), PerceptionException.class,
"Perception does not contain any data for info type %s for lane %s.", delayedInfoType, lane);
List> list = this.map.get(delayedInfoType).get(lane);
Throw.when(list.isEmpty(), RuntimeException.class, "Perception does not contain any data for info type %s.",
delayedInfoType);
// remove old data if required
Parameters params;
Time now;
try
{
params = getPerception().getGtu().getParameters();
now = getPerception().getGtu().getSimulator().getSimulatorTime();
}
catch (GTUException exception)
{
throw new RuntimeException("GTU not yet initialized.", exception);
}
Time delayedTime;
try
{
delayedTime = now.minus(params.getParameter(delayedInfoType.getDelayParameter())).plus(MARGIN);
}
catch (ParameterException exception)
{
throw new RuntimeException("Delay parameter not found.", exception);
}
while (list.size() > 1 && list.get(1).getTimestamp().le(delayedTime))
{
list.remove(0);
}
return (TimeStampedObject) list.get(0);
}
/**
* Move data coupled to a lane to another lane to account for a lane change. The tactical planner needs to call this exactly
* when it flips logic concerning the origin and target lane.
* @param dir LateralDirectionality; direction of lane change
*/
public final void changeLane(final LateralDirectionality dir)
{
for (DelayedInfoType> delayedInfoType : this.map.keySet())
{
LinkedHashMap>> newMap = new LinkedHashMap<>();
for (RelativeLane lane : this.map.get(delayedInfoType).keySet())
{
if (lane != null)
{
newMap.put(dir.isLeft() ? lane.getRight() : lane.getLeft(), this.map.get(delayedInfoType).get(lane));
}
else
{
newMap.put(lane, this.map.get(delayedInfoType).get(lane));
}
}
this.map.put(delayedInfoType, newMap);
}
}
/**
* Superclass for delayed info.
*
* Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
*
* BSD-style license. See OpenTrafficSim License.
*
* @version $Revision$, $LastChangedDate$, by $Author$, initial version 14 feb. 2017
* @author Alexander Verbraeck
* @author Peter Knoppers
* @author Wouter Schakel
* @param type of information.
*/
public static class DelayedInfoType extends Type> implements Identifiable
{
/** Id. */
private final String id;
/** Parameter for delay. */
private final ParameterTypeDuration delayParameter;
/**
* Constructor.
* @param id String; id
* @param delayParameter ParameterTypeDuration; delay parameter type
*/
public DelayedInfoType(final String id, final ParameterTypeDuration delayParameter)
{
this.id = id;
this.delayParameter = delayParameter;
}
/**
* Returns the id.
* @return id
*/
@Override
public final String getId()
{
return this.getId();
}
/**
* Returns the delay parameter type.
* @return delayParameter
*/
public final ParameterTypeDuration getDelayParameter()
{
return this.delayParameter;
}
/** {@inheritDoc} */
@Override
public final int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
return result;
}
/** {@inheritDoc} */
@Override
public final boolean equals(final Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
DelayedInfoType> other = (DelayedInfoType>) obj;
if (this.id == null)
{
if (other.id != null)
{
return false;
}
}
else if (!this.id.equals(other.id))
{
return false;
}
return true;
}
}
}