TrafficLightControllerFixedDuration.java

package org.opentrafficsim.road.network.lane.object.trafficlight;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import org.djunits.value.vdouble.scalar.Duration;
import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;

import nl.tudelft.simulation.dsol.SimRuntimeException;

/**
 * A traffic light controller with fixed durations. Red, yellow and green times of each phase can be set, as well as the time
 * between phases.
 * <p>
 * Copyright (c) 2013-2016 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/docs/current/license.html">OpenTrafficSim License</a>.
 * </p>
 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
 * initial version Oct 4, 2016 <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 TrafficLightControllerFixedDuration implements TrafficLightController
{
    /** the controller id. */
    private final String id;

    /** the simulator. */
    private final OTSDEVSSimulatorInterface simulator;

    /** the phases with a number identifying them to create a sorted map. */
    private final SortedMap<Integer, Set<TrafficLight>> phases = new TreeMap<>();

    /** the fixed yellow duration per phase. */
    private final Map<Integer, Duration> yellowDurations = new HashMap<>();

    /** the fixed green duration per phase. */
    private final Map<Integer, Duration> greenDurations = new HashMap<>();

    /** the current phase. */
    private int currentPhase;

    /** the current light in the current phase. */
    private TrafficLightColor currentColor = TrafficLightColor.RED;

    /** fixed clearance duration between phases. */
    private Duration clearanceDuration = Duration.ZERO;

    /**
     * Create a fixed time controller.
     * @param id String; the controller id
     * @param simulator OTSDEVSSimulatorInterface; the simulator
     * @throws TrafficLightException when scheduling of thhe start event fails
     */
    public TrafficLightControllerFixedDuration(final String id, final OTSDEVSSimulatorInterface simulator)
            throws TrafficLightException
    {
        this.id = id;
        this.simulator = simulator;
        try
        {
            this.simulator.scheduleEventNow(this, this, "changePhase", null);
        }
        catch (SimRuntimeException exception)
        {
            throw new TrafficLightException(exception);
        }
    }

    /**
     * Change the phase and/or color of the traffic lights.
     * @throws TrafficLightException when scheduling of thhe start event fails
     */
    protected final void changePhase() throws TrafficLightException
    {
        try
        {
            if (this.currentColor.isGreen())
            {
                for (TrafficLight trafficLight : this.phases.get(this.currentPhase))
                {
                    trafficLight.setTrafficLightColor(TrafficLightColor.YELLOW);
                }
                this.currentColor = TrafficLightColor.YELLOW;
                this.simulator.scheduleEventRel(this.yellowDurations.get(this.currentPhase), this, this, "changePhase", null);
                return;
            }
            else if (this.currentColor.isYellow())
            {
                for (TrafficLight trafficLight : this.phases.get(this.currentPhase))
                {
                    trafficLight.setTrafficLightColor(TrafficLightColor.RED);
                }
                this.currentColor = TrafficLightColor.RED;
            }

            if (this.currentColor.isRed())
            {
                Integer nextPhase = ((NavigableSet<Integer>) this.phases.keySet()).higher(this.currentPhase);
                if (nextPhase == null)
                {
                    nextPhase = this.phases.firstKey();
                }
                this.currentPhase = nextPhase;
                this.currentColor = TrafficLightColor.GREEN;
                this.simulator.scheduleEventRel(this.greenDurations.get(this.currentPhase), this, this, "changePhase", null);
            }
        }
        catch (SimRuntimeException exception)
        {
            throw new TrafficLightException(exception);
        }
    }

    /** {@inheritDoc} */
    @Override
    public final int getNumberOfPhases()
    {
        return this.phases.size();
    }

    /** {@inheritDoc} */
    @Override
    public final int getCurrentPhase()
    {
        return this.currentPhase;
    }

    /** {@inheritDoc} */
    @Override
    public final Duration getClearanceDurationToNextPhase()
    {
        return this.clearanceDuration;
    }

    /**
     * Add a new phase.
     * @param phaseId int; the id of the phase to be added.
     * @param yellowDuration Duration; the yellow time
     * @param greenDuration Duration; the green time
     * @throws TrafficLightException when the phase already existed
     */
    public final void addPhase(final int phaseId, final Duration yellowDuration, final Duration greenDuration)
            throws TrafficLightException
    {
        if (this.phases.containsKey(phaseId))
        {
            throw new TrafficLightException("Phase " + phaseId + " for traffic light " + this.id + " was already defined");
        }
        this.phases.put(phaseId, new HashSet<TrafficLight>());
        this.yellowDurations.put(phaseId, yellowDuration);
        this.greenDurations.put(phaseId, greenDuration);
    }

    /** {@inheritDoc} */
    @Override
    public final void addTrafficLightToPhase(final int phaseId, final TrafficLight trafficLight) throws TrafficLightException
    {
        if (!this.phases.containsKey(phaseId))
        {
            throw new TrafficLightException("Phase " + phaseId + " for traffic light " + this.id + " was not defined");
        }
        this.phases.get(phaseId).add(trafficLight);
        trafficLight.setTrafficLightColor(TrafficLightColor.RED);
    }

    /** {@inheritDoc} */
    @Override
    public final String getId()
    {
        return this.id;
    }

    /**
     * @param clearanceDuration set clearanceDuration
     */
    public final void setClearanceDuration(final Duration clearanceDuration)
    {
        this.clearanceDuration = clearanceDuration;
    }

    /** {@inheritDoc} */
    @Override
    @SuppressWarnings("checkstyle:designforextension")
    public String toString()
    {
        return "TrafficLightControllerFixedDuration [id=" + this.id + ", phases=" + this.phases + ", yellowDurations="
                + this.yellowDurations + ", greenDurations=" + this.greenDurations + ", currentPhase=" + this.currentPhase
                + ", currentColor=" + this.currentColor + ", clearanceDuration=" + this.clearanceDuration + "]";
    }

}