CycleTimeLightController.java
package org.opentrafficsim.road.network.control.rampmetering;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.djunits.value.vdouble.scalar.Duration;
import org.djunits.value.vdouble.scalar.Time;
import org.djutils.exceptions.Try;
import org.opentrafficsim.core.compatibility.Compatible;
import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
import org.opentrafficsim.core.gtu.RelativePosition;
import org.opentrafficsim.core.network.NetworkException;
import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
import org.opentrafficsim.road.network.lane.CrossSectionElement;
import org.opentrafficsim.road.network.lane.object.sensor.AbstractSensor;
import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLightColor;
import nl.tudelft.simulation.dsol.SimRuntimeException;
import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface.TimeDoubleUnit;
/**
* Controller using a cycle time.
* <p>
* Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
* <p>
* @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 jun. 2019 <br>
* @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
* @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
* @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
*/
public class CycleTimeLightController implements RampMeteringLightController
{
/** Minimum red duration. */
private static final Duration MIN_RED_TIME = Duration.instantiateSI(2.0);
/** Whether the controller is enabled. */
private boolean enabled = false;
/** Time when red phase was started. */
private Map<TrafficLight, Time> greenStarts = new LinkedHashMap<>();
/** Cycle time. */
private Duration cTime;
/** Simulator. */
private final OTSSimulatorInterface simulator;
/** Traffic lights. */
private final List<TrafficLight> trafficLights;
/** Scheduled red event. */
private Map<TrafficLight, SimEventInterface<SimTimeDoubleUnit>> redEvents = new LinkedHashMap<>();
/** Scheduled green event. */
private Map<TrafficLight, SimEventInterface<SimTimeDoubleUnit>> greenEvents = new LinkedHashMap<>();
/**
* @param simulator OTSSimulatorInterface; simulator
* @param trafficLights List<TrafficLight>; traffic lights
* @param compatible Compatible; GTU types that trigger the detector, and hence the light to red
*/
public CycleTimeLightController(final OTSSimulatorInterface simulator, final List<TrafficLight> trafficLights,
final Compatible compatible)
{
this.simulator = simulator;
this.trafficLights = trafficLights;
for (TrafficLight trafficLight : trafficLights)
{
Try.execute(() -> new RampMeteringSensor(trafficLight, simulator, compatible),
"Unexpected exception while creating a detector with a ramp metering traffic light.");
this.greenStarts.put(trafficLight, Time.instantiateSI(Double.NEGATIVE_INFINITY));
}
}
/** {@inheritDoc} */
@Override
public void disable()
{
Iterator<TrafficLight> it = this.redEvents.keySet().iterator();
while (it.hasNext())
{
TrafficLight trafficLight = it.next();
this.simulator.cancelEvent(this.redEvents.get(trafficLight));
it.remove();
}
it = this.greenEvents.keySet().iterator();
while (it.hasNext())
{
TrafficLight trafficLight = it.next();
this.simulator.cancelEvent(this.greenEvents.get(trafficLight));
it.remove();
}
this.enabled = false;
for (TrafficLight trafficLight : this.trafficLights)
{
trafficLight.setTrafficLightColor(TrafficLightColor.GREEN);
}
}
/**
* Starts the cycle.
* @param cycleTime Duration; cycle time
*/
@Override
public void enable(final Duration cycleTime)
{
this.simulator.getLogger().always().info("Traffic light uses " + cycleTime);
this.cTime = cycleTime;
if (!this.enabled)
{
this.enabled = true;
for (TrafficLight trafficLight : this.trafficLights)
{
setGreen(trafficLight);
}
}
}
/**
* Sets the traffic light to red. Can be scheduled.
* @param trafficLight TrafficLight; traffic light
*/
protected void setRed(final TrafficLight trafficLight)
{
this.redEvents.remove(trafficLight);
this.simulator.getLogger().always().info("Traffic light set to RED");
trafficLight.setTrafficLightColor(TrafficLightColor.RED);
}
/**
* Sets the traffic light to green. Can be scheduled and remembers the green time.
* @param trafficLight TrafficLight; traffic light
*/
protected void setGreen(final TrafficLight trafficLight)
{
this.greenEvents.remove(trafficLight);
this.greenStarts.put(trafficLight, this.simulator.getSimulatorTime());
this.simulator.getLogger().always().info("Traffic light set to GREEN");
trafficLight.setTrafficLightColor(TrafficLightColor.GREEN);
}
/** Ramp metering sensor. */
private class RampMeteringSensor extends AbstractSensor
{
/** */
private static final long serialVersionUID = 20190618L;
/** The traffic light. */
private final TrafficLight trafficLight;
/**
* @param trafficLight TrafficLight; traffic light
* @param simulator TimeDoubleUnit; simulator
* @param detectedGTUTypes Compatible; GTU types
* @throws NetworkException when the position on the lane is out of bounds
*/
RampMeteringSensor(final TrafficLight trafficLight, final TimeDoubleUnit simulator, final Compatible detectedGTUTypes)
throws NetworkException
{
super(trafficLight.getId() + "_sensor", trafficLight.getLane(), trafficLight.getLongitudinalPosition(),
RelativePosition.FRONT, simulator, detectedGTUTypes);
this.trafficLight = trafficLight;
}
/** {@inheritDoc} */
@SuppressWarnings("synthetic-access")
@Override
protected void triggerResponse(final LaneBasedGTU gtu)
{
if (CycleTimeLightController.this.enabled && this.trafficLight.getTrafficLightColor().isGreen())
{
try
{
// schedule green
Time minRedTime = CycleTimeLightController.this.simulator.getSimulatorTime().plus(MIN_RED_TIME);
Time cycleRedTime = CycleTimeLightController.this.greenStarts.get(this.trafficLight)
.plus(CycleTimeLightController.this.cTime);
Time green;
if (minRedTime.ge(cycleRedTime))
{
getSimulator().getLogger().always().info("Traffic light set to RED");
this.trafficLight.setTrafficLightColor(TrafficLightColor.RED);
green = minRedTime;
}
else
{
getSimulator().getLogger().always().info("Traffic light set to YELLOW (RED over 'MIN_RED_TIME')");
this.trafficLight.setTrafficLightColor(TrafficLightColor.YELLOW);
CycleTimeLightController.this.redEvents.put(this.trafficLight,
CycleTimeLightController.this.simulator.scheduleEventRel(MIN_RED_TIME, this,
CycleTimeLightController.this, "setRed", new Object[] {this.trafficLight}));
green = cycleRedTime;
}
CycleTimeLightController.this.greenEvents.put(this.trafficLight,
CycleTimeLightController.this.simulator.scheduleEventAbs(green, this, CycleTimeLightController.this,
"setGreen", new Object[] {this.trafficLight}));
}
catch (SimRuntimeException exception)
{
throw new RuntimeException(exception);
}
}
}
/** {@inheritDoc} */
@Override
public AbstractSensor clone(final CrossSectionElement newCSE,
final nl.tudelft.simulation.dsol.simulators.SimulatorInterface.TimeDoubleUnit newSimulator)
throws NetworkException
{
return null; // TODO: should be cloned as part of the ramp metering
}
}
}