org.movsim.extended.SimulatorExtended.java Source code

Java tutorial

Introduction

Here is the source code for org.movsim.extended.SimulatorExtended.java

Source

/*
 * Copyright (C) 2010, 2011, 2012 by Arne Kesting, Martin Treiber, Ralph Germ, Martin Budden
 * <movsim.org@gmail.com>
 * -----------------------------------------------------------------------------------------
 * 
 * This file is part of
 * 
 * MovSim - the multi-model open-source vehicular-traffic simulator.
 * 
 * MovSim is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * MovSim is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MovSim. If not, see <http://www.gnu.org/licenses/>
 * or <http://www.movsim.org>.
 * 
 * -----------------------------------------------------------------------------------------
 */
package org.movsim.extended;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;

import net.sf.cglib.proxy.Enhancer;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;
import org.joda.time.format.DateTimeFormat;
import org.movsim.autogen.InitialConditions;
import org.movsim.autogen.MacroIC;
import org.movsim.autogen.MicroIC;
import org.movsim.autogen.Movsim;
import org.movsim.autogen.Parking;
import org.movsim.autogen.Road;
import org.movsim.autogen.Simulation;
import org.movsim.autogen.TrafficComposition;
import org.movsim.autogen.TrafficSink;
import org.movsim.input.ProjectMetaData;
import org.movsim.input.network.OpenDriveReader;
import org.movsim.output.SimulationOutput;
import org.movsim.output.detector.LoopDetectors;
import org.movsim.output.fileoutput.FileTrafficSourceData;
import org.movsim.roadmappings.RoadMapping;
import org.movsim.roadmappings.RoadMappingPolyS;
import org.movsim.simulator.MovsimConstants;
import org.movsim.simulator.SimulationRunnable;
import org.movsim.simulator.SimulationTimeStep;
import org.movsim.simulator.Simulator;
import org.movsim.simulator.roadnetwork.AbstractTrafficSource;
import org.movsim.simulator.roadnetwork.FlowConservingBottlenecks;
import org.movsim.simulator.roadnetwork.InflowTimeSeries;
import org.movsim.simulator.roadnetwork.InitialConditionsMacro;
import org.movsim.simulator.roadnetwork.LaneSegment;
import org.movsim.simulator.roadnetwork.Lanes;
import org.movsim.simulator.roadnetwork.MicroInflowFileReader;
import org.movsim.simulator.roadnetwork.RoadNetwork;
import org.movsim.simulator.roadnetwork.RoadSegment;
import org.movsim.simulator.roadnetwork.SimpleRamp;
import org.movsim.simulator.roadnetwork.TrafficSourceMacro;
import org.movsim.simulator.roadnetwork.TrafficSourceMicro;
import org.movsim.simulator.roadnetwork.routing.Routing;
import org.movsim.simulator.trafficlights.TrafficLights;
import org.movsim.simulator.vehicles.TestVehicle;
import org.movsim.simulator.vehicles.TrafficCompositionGenerator;
import org.movsim.simulator.vehicles.Vehicle;
import org.movsim.simulator.vehicles.VehicleFactory;
import org.movsim.utilities.MyRandom;
import org.movsim.utilities.Units;
import org.movsim.xml.MovsimInputLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import com.google.common.base.Preconditions;

public class SimulatorExtended extends Simulator {

    /** The Constant LOG. */
    private static final Logger LOG = LoggerFactory.getLogger(Simulator.class);

    private long startTimeMillis;

    private final ProjectMetaData projectMetaData;
    private String projectName;
    private Movsim inputData;

    private VehicleFactory vehicleFactory;
    private TrafficCompositionGenerator defaultTrafficComposition;
    private TrafficLights trafficLights;
    private SimulationOutput simOutput;
    private final RoadNetwork roadNetwork;
    private Routing routing;
    private final SimulationRunnable simulationRunnable;
    private int obstacleCount;
    private long timeOffsetMillis;
    private double timeStep;
    private List<SimulationTimeStep> timeSteps;

    /**
     * Constructor.
     * 
     * @throws SAXException
     * @throws JAXBException
     */
    public SimulatorExtended() {
        this.projectMetaData = ProjectMetaData.getInstance();
        roadNetwork = new RoadNetwork();
        simulationRunnable = new SimulationRunnable(this);
        simulationRunnable.setCompletionCallback(this);
        timeSteps = new ArrayList<>();
    }

    public void initialize() throws JAXBException, SAXException {
        LOG.info("Copyright '\u00A9' by Arne Kesting, Martin Treiber, Ralph Germ and Martin Budden (2011-2013)");

        projectName = projectMetaData.getProjectName();
        // TODO temporary handling of Variable Message Sign until added to XML
        roadNetwork.setHasVariableMessageSign(projectName.startsWith("routing"));

        inputData = MovsimInputLoader.getInputData(projectMetaData.getInputFile());

        timeOffsetMillis = 0;
        if (inputData.getScenario().getSimulation().isSetTimeOffset()) {
            DateTime dateTime = LocalDateTime.parse(inputData.getScenario().getSimulation().getTimeOffset(),
                    DateTimeFormat.forPattern("YYYY-MM-dd'T'HH:mm:ssZ")).toDateTime(DateTimeZone.UTC);
            timeOffsetMillis = dateTime.getMillis();
            LOG.info("global time offset set={} --> {} milliseconds.", dateTime, timeOffsetMillis);
            ProjectMetaData.getInstance().setTimeOffsetMillis(timeOffsetMillis);
        }
        projectMetaData.setXodrNetworkFilename(inputData.getScenario().getNetworkFilename()); // TODO

        Simulation simulationInput = inputData.getScenario().getSimulation();

        final boolean loadedRoadNetwork = parseOpenDriveXml(roadNetwork, projectMetaData);
        routing = new Routing(inputData.getScenario().getRoutes(), roadNetwork);

        vehicleFactory = new VehicleFactory(simulationInput.getTimestep(), inputData.getVehiclePrototypes(),
                inputData.getConsumption(), routing);

        roadNetwork.setWithCrashExit(simulationInput.isCrashExit());

        simulationRunnable.setTimeStep(simulationInput.getTimestep());
        setTimeStep(simulationInput.getTimestep());

        // TODO better handling of case "duration = INFINITY"
        double duration = simulationInput.isSetDuration() ? simulationInput.getDuration() : -1;

        simulationRunnable.setDuration(duration < 0 ? Double.MAX_VALUE : duration);

        if (simulationInput.isWithSeed()) {
            MyRandom.initializeWithSeed(simulationInput.getSeed());
        }

        createTrafficCompositionGenerator(simulationInput);

        // For each road in the MovSim XML input data, find the corresponding roadSegment and
        // set its input data accordingly
        // final Map<String, RoadInput> roadInputMap = simulationInput.get.getRoadInput();
        if (loadedRoadNetwork == false && simulationInput.getRoad().size() == 1) {
            defaultTestingRoadMapping(simulationInput.getRoad().get(0));
        } else {
            matchRoadSegmentsAndRoadInput(simulationInput.getRoad());
        }

        createTrafficLights();

        this.timeSteps.add(trafficLights);
        this.timeSteps.add(roadNetwork);

        reset();
    }

    /**
     * <p>
     * Metodo para crear los semaforos de la red de trfico. El metodo permite crear un proxy
     * para interceptar las llamadas y asociarlas a un agente BESA
     * </p>
     */
    private void createTrafficLights() {
        Enhancer enhTl = new Enhancer();

        enhTl.setSuperclass(TrafficLights.class);
        enhTl.setCallback(new TrafficLightsProxy());

        trafficLights = (TrafficLights) enhTl.create(
                new Class[] { org.movsim.autogen.TrafficLights.class, RoadNetwork.class },
                new Object[] { inputData.getScenario().getTrafficLights(), roadNetwork });

        trafficLights.getSignalIdToController();
    }

    /**
      * <p>
      * Metodo para crear los vehiculos. El metodo permite crear un proxy
      * para interceptar las llamadas y asociarlas a un agente BESA
      * </p>
      */
    private void createTrafficCompositionGenerator(Simulation simulationInput) {
        Enhancer enhTcg = new Enhancer();

        enhTcg.setSuperclass(TrafficCompositionGenerator.class);
        enhTcg.setCallback(new TrafficCompositionGeneratorProxy());

        defaultTrafficComposition = (TrafficCompositionGenerator) enhTcg.create(
                new Class[] { TrafficComposition.class, VehicleFactory.class },
                new Object[] { simulationInput.getTrafficComposition(), vehicleFactory });
    }

    public Iterable<String> getVehiclePrototypeLabels() {
        return vehicleFactory.getLabels();
    }

    public TrafficCompositionGenerator getVehicleGenerator() {
        return defaultTrafficComposition;
    }

    public ProjectMetaData getProjectMetaData() {
        return projectMetaData;
    }

    public RoadNetwork getRoadNetwork() {
        return roadNetwork;
    }

    public SimulationRunnable getSimulationRunnable() {
        return simulationRunnable;
    }

    /**
     * Load scenario from xml.
     * 
     * @param scenario
     * @param path
     * @throws JAXBException
     * @throws SAXException
     * @throws ParserConfigurationException
     */
    public void loadScenarioFromXml(String scenario, String path) throws JAXBException, SAXException {
        roadNetwork.clear();
        projectMetaData.setProjectName(scenario);
        projectMetaData.setPathToProjectXmlFile(path);
        initialize();
    }

    private void matchRoadSegmentsAndRoadInput(List<Road> roads) {
        for (final Road roadInput : roads) {
            RoadSegment roadSegment = Preconditions.checkNotNull(roadNetwork.findByUserId(roadInput.getId()),
                    "cannot find roadId=" + roadInput.getId()
                            + " from input in constructed roadNetwork. IGNORE DATA!!!");
            addInputToRoadSegment(roadSegment, roadInput);
        }
    }

    /**
     * There was no xodr file and there is only one road segment in the MovSimXML file so set up a default s-shaped road
     * mapping.
     * 
     * @param road
     */
    private void defaultTestingRoadMapping(Road roadInput) {
        LOG.warn("Simulation with test network");
        final int laneCount = 1;
        final double roadLength = 1500;
        final RoadMapping roadMapping = new RoadMappingPolyS(laneCount, 10, 50, 50, 100.0 / Math.PI, roadLength);
        final RoadSegment roadSegment = new RoadSegment(roadMapping);
        addInputToRoadSegment(roadSegment, roadInput);
        roadSegment.setUserId("1");
        roadSegment.addDefaultSink();
        roadNetwork.add(roadSegment);
    }

    /**
     * Parse the OpenDrive (.xodr) file to load the network topology and road layout.
     * 
     * @param projectMetaData
     * @return
     * @throws SAXException
     * @throws JAXBException
     * @throws ParserConfigurationException
     */
    private static boolean parseOpenDriveXml(RoadNetwork roadNetwork, ProjectMetaData projectMetaData)
            throws JAXBException, SAXException {
        final String xodrFileName = projectMetaData.getXodrNetworkFilename();
        final String xodrPath = projectMetaData.getPathToProjectFile();
        final String fullXodrFileName = xodrPath + xodrFileName;
        LOG.info("try to load {}", fullXodrFileName);
        final boolean loaded = OpenDriveReader.loadRoadNetwork(roadNetwork, fullXodrFileName);
        LOG.info("done with parsing road network {}. Success: {}", fullXodrFileName, loaded);
        return loaded;
    }

    /**
     * Add input data to road segment.
     * 
     * Note by rules of encapsulation this function is NOT a member of RoadSegment, since RoadSegment should not be
     * aware of form of XML file or RoadInput data structure.
     * 
     * @param roadSegment
     * @param roadInput
     */
    private void addInputToRoadSegment(RoadSegment roadSegment, Road roadInput) {
        // setup own vehicle generator for roadSegment: needed for trafficSource and initial conditions
        TrafficCompositionGenerator composition = roadInput.isSetTrafficComposition()
                ? new TrafficCompositionGenerator(roadInput.getTrafficComposition(), vehicleFactory)
                : defaultTrafficComposition;
        if (roadInput.isSetTrafficComposition()) {
            LOG.info("road with id={} has its own vehicle composition generator.", roadSegment.id());
        }

        // set up the traffic source
        if (roadInput.isSetTrafficSource()) {
            final org.movsim.autogen.TrafficSource trafficSourceData = roadInput.getTrafficSource();
            AbstractTrafficSource trafficSource = null;
            if (trafficSourceData.isSetInflow()) {
                InflowTimeSeries inflowTimeSeries = new InflowTimeSeries(trafficSourceData.getInflow());
                trafficSource = new TrafficSourceMacro(composition, roadSegment, inflowTimeSeries);
            } else if (trafficSourceData.isSetInflowFromFile()) {
                trafficSource = new TrafficSourceMicro(composition, roadSegment);
                MicroInflowFileReader reader = new MicroInflowFileReader(trafficSourceData.getInflowFromFile(),
                        roadSegment.laneCount(), timeOffsetMillis, routing, (TrafficSourceMicro) trafficSource);
                reader.readData();
            }
            if (trafficSource != null) {
                if (trafficSourceData.isLogging()) {
                    trafficSource.setRecorder(new FileTrafficSourceData(roadSegment.userId()));
                }
                roadSegment.setTrafficSource(trafficSource);
            }
        }

        // set up the traffic sink
        if (roadInput.isSetTrafficSink()) {
            createParkingSink(roadInput.getTrafficSink(), roadSegment);
        }

        // set up simple ramp with dropping mechanism
        if (roadInput.isSetSimpleRamp()) {
            org.movsim.autogen.SimpleRamp simpleRampData = roadInput.getSimpleRamp();
            InflowTimeSeries inflowTimeSeries = new InflowTimeSeries(simpleRampData.getInflow());
            SimpleRamp simpleRamp = new SimpleRamp(composition, roadSegment, simpleRampData, inflowTimeSeries);
            if (simpleRampData.isLogging()) {
                simpleRamp.setRecorder(new FileTrafficSourceData(roadSegment.userId()));
            }
            roadSegment.setSimpleRamp(simpleRamp);
        }

        // set up the detectors
        if (roadInput.isSetDetectors()) {
            roadSegment.setLoopDetectors(new LoopDetectors(roadSegment, roadInput.getDetectors()));
        }
        // set up the flow conserving bottlenecks
        if (roadInput.isSetFlowConservingInhomogeneities()) {
            roadSegment.setFlowConservingBottlenecks(
                    new FlowConservingBottlenecks(roadInput.getFlowConservingInhomogeneities()));
        }

        if (roadInput.isSetInitialConditions()) {
            initialConditions(roadSegment, roadInput.getInitialConditions(), composition);
        }

    }

    private void createParkingSink(TrafficSink trafficSink, RoadSegment roadSegment) {
        // setup source which models the re-entrance of vehicles leaving the parking lot
        if (!trafficSink.isSetParking()) {
            return;
        }
        Parking parking = trafficSink.getParking();
        RoadSegment sourceRoadSegment = Preconditions.checkNotNull(
                roadNetwork.findByUserId(parking.getSourceRoadId()),
                "cannot find roadSegment=" + parking.getSourceRoadId() + " specified as re-entrance from the road="
                        + roadSegment.id());
        TrafficSourceMicro trafficSource = new TrafficSourceMicro(defaultTrafficComposition, sourceRoadSegment);
        if (trafficSink.isLogging()) {
            trafficSource.setRecorder(new FileTrafficSourceData(sourceRoadSegment.userId()));
        }
        sourceRoadSegment.setTrafficSource(trafficSource);
        roadSegment.sink().setupParkingLot(parking, timeOffsetMillis, trafficSource);
    }

    private static void initialConditions(RoadSegment roadSegment, InitialConditions initialConditions,
            TrafficCompositionGenerator vehGenerator) {
        Preconditions.checkNotNull(initialConditions);
        if (initialConditions.isSetMacroIC()) {
            setMacroInitialConditions(roadSegment, initialConditions.getMacroIC(), vehGenerator);
        } else if (initialConditions.isSetMicroIC()) {
            setMicroInitialConditions(roadSegment, initialConditions.getMicroIC(), vehGenerator);
        } else {
            LOG.info("no initial conditions defined");
        }
    }

    /**
     * Determine vehicle positions on all relevant lanes while considering minimum gaps to avoid accidents. Gaps are
     * left at the beginning and the end of the road segment on purpose. However, the consistency check is not complete
     * and other segments are not considered.
     * 
     * @param roadSegment
     * @param macroInitialConditions
     * @param vehGenerator
     */
    private static void setMacroInitialConditions(RoadSegment roadSegment, List<MacroIC> macroInitialConditions,
            TrafficCompositionGenerator vehGenerator) {

        LOG.info("choose macro initial conditions: generate vehicles from macro-localDensity ");
        final InitialConditionsMacro icMacro = new InitialConditionsMacro(macroInitialConditions);

        for (LaneSegment laneSegment : roadSegment.laneSegments()) {
            if (laneSegment.type() != Lanes.Type.TRAFFIC) {
                LOG.debug("no macroscopic initial conditions for non-traffic lanes (slip roads etc).");
                continue;
            }

            double position = roadSegment.roadLength(); // start at end of segment
            while (position > 0) {
                final TestVehicle testVehicle = vehGenerator.getTestVehicle();

                final double rhoLocal = icMacro.rho(position);
                double speedInit = icMacro.hasUserDefinedSpeeds() ? icMacro.vInit(position)
                        : testVehicle.getEquilibriumSpeed(rhoLocal);
                if (LOG.isDebugEnabled() && !icMacro.hasUserDefinedSpeeds()) {
                    LOG.debug("use equilibrium speed={} in macroscopic initial conditions.", speedInit);
                }

                if (LOG.isDebugEnabled()) {
                    LOG.debug(String.format(
                            "macroscopic init conditions from input: roadId=%s, x=%.3f, rho(x)=%.3f/km, speed=%.2fkm/h",
                            roadSegment.id(), position, Units.INVM_TO_INVKM * rhoLocal,
                            Units.MS_TO_KMH * speedInit));
                }

                if (rhoLocal <= 0) {
                    LOG.debug("no vehicle added at x={} for vanishing initial localDensity={}.", position,
                            rhoLocal);
                    position -= 50; // move on in upstream direction
                    continue;
                }

                final Vehicle veh = vehGenerator.createVehicle(testVehicle);
                final double meanDistanceInLane = 1. / (rhoLocal + MovsimConstants.SMALL_VALUE);
                // TODO icMacro for ca
                // final double minimumGap = veh.getLongitudinalModel().isCA() ? veh.getLength() : veh.getLength() +
                // veh.getLongitudinalModel().getS0();
                final double minimumGap = veh.getLength() + veh.getLongitudinalModel().getMinimumGap();
                final double posDecrement = Math.max(meanDistanceInLane, minimumGap);
                position -= posDecrement;

                if (position <= posDecrement) {
                    LOG.debug("leave minimum gap at origin of road segment and start with next lane, pos={}",
                            position);
                    break;
                }
                final Vehicle leader = laneSegment.rearVehicle();
                final double gapToLeader = (leader == null) ? MovsimConstants.GAP_INFINITY
                        : leader.getRearPosition() - position;

                if (LOG.isDebugEnabled()) {
                    LOG.debug(String.format(
                            "meanDistance=%.3f, minimumGap=%.2f, posDecrement=%.3f, gapToLeader=%.3f\n",
                            meanDistanceInLane, minimumGap, posDecrement, gapToLeader));
                }

                if (gapToLeader > 0) {
                    veh.setFrontPosition(position);
                    veh.setSpeed(speedInit);
                    veh.setLane(laneSegment.lane());
                    LOG.debug("add vehicle from macroscopic initial conditions at pos={} with speed={}.", position,
                            speedInit);
                    roadSegment.addVehicle(veh);
                } else {
                    LOG.debug("cannot add vehicle due to gap constraints at pos={} with speed={}.", position,
                            speedInit);
                }

            }
        }
    }

    private static void setMicroInitialConditions(RoadSegment roadSegment, List<MicroIC> initialMicroConditions,
            TrafficCompositionGenerator vehGenerator) {
        LOG.debug(("choose micro initial conditions"));
        int vehicleNumber = 1;
        for (final MicroIC ic : initialMicroConditions) {
            // TODO counter
            final String vehTypeFromFile = ic.getLabel();
            final Vehicle veh = (vehTypeFromFile.length() == 0) ? vehGenerator.createVehicle()
                    : vehGenerator.createVehicle(vehTypeFromFile);
            veh.setVehNumber(vehicleNumber);
            vehicleNumber++;
            // testwise:
            veh.setFrontPosition(Math.round(ic.getPosition() / veh.physicalQuantities().getxScale()));
            veh.setSpeed(Math.round(ic.getSpeed() / veh.physicalQuantities().getvScale()));
            final int lane = ic.getLane();
            if (lane < Lanes.MOST_INNER_LANE || lane > roadSegment.laneCount()) {
                throw new IllegalArgumentException(
                        "lane=" + lane + " given in initial condition does not exist for road=" + roadSegment.id()
                                + " which has a laneCount of " + roadSegment.laneCount());
            }
            veh.setLane(lane);
            roadSegment.addVehicle(veh);
            LOG.info(String.format("set vehicle with label = %s on lane=%d with front at x=%.2f, speed=%.2f",
                    veh.getLabel(), veh.lane(), veh.getFrontPosition(), veh.getSpeed()));
            if (veh.getLongitudinalModel().isCA()) {
                LOG.info(
                        String.format("and for the CA in physical quantities: front position at x=%.2f, speed=%.2f",
                                veh.physicalQuantities().getFrontPosition(), veh.physicalQuantities().getSpeed()));
            }
        }
    }

    public void reset() {
        simulationRunnable.reset();
        if (inputData.getScenario().isSetOutputConfiguration()) {
            simOutput = new SimulationOutput(simulationRunnable.timeStep(),
                    projectMetaData.isInstantaneousFileOutput(), inputData.getScenario().getOutputConfiguration(),
                    roadNetwork, routing, vehicleFactory);

            this.timeSteps.add(simOutput);
        }
        obstacleCount = roadNetwork.obstacleCount();
    }

    public void runToCompletion() {
        LOG.info("Simulator.run: start simulation at {} seconds of simulation project={}",
                simulationRunnable.simulationTime(), projectName);

        startTimeMillis = System.currentTimeMillis();
        // TODO check if first output update has to be called in update for external call!!
        // TODO FloatingCars do not need this call. First output line for t=0 is written twice to file
        // simOutput.timeStep(simulationRunnable.timeStep(), simulationRunnable.simulationTime(),
        // simulationRunnable.iterationCount());
        simulationRunnable.runToCompletion();
    }

    /**
     * Returns true if the simulation has finished.
     */
    public boolean isFinished() {
        if (simulationRunnable.simulationTime() > 60.0 && roadNetwork.vehicleCount() == obstacleCount) {
            return true;
        }
        return false;
    }

    @Override
    public void simulationComplete(double simulationTime) {
        LOG.info(String.format("Simulator.run: stop after time = %.2fs = %.2fh of simulation project=%s",
                simulationTime, simulationTime / 3600, projectName));
        final double elapsedTime = 0.001 * (System.currentTimeMillis() - startTimeMillis);
        LOG.info(String.format(
                "time elapsed = %.3fs --> simulation time warp = %.2f, time per 1000 update steps=%.3fs",
                elapsedTime, simulationTime / elapsedTime,
                1000 * elapsedTime / simulationRunnable.iterationCount()));
    }

    @Override
    public void timeStep(double dt, double simulationTime, long iterationCount) {
        if (iterationCount % 200 == 0) {
            if (LOG.isInfoEnabled()) {
                LOG.info(String.format("Simulator.update :time = %.2fs = %.2fh, dt = %.2fs, projectName=%s",
                        simulationTime, simulationTime / 3600, dt, projectName));
            }
        }

        //        trafficLights.timeStep(dt, simulationTime, iterationCount);
        //        roadNetwork.timeStep(dt, simulationTime, iterationCount);
        //        if (simOutput != null) {
        //            simOutput.timeStep(dt, simulationTime, iterationCount);
        //        }

        for (SimulationTimeStep sTimeStep : this.timeSteps) {
            sTimeStep.timeStep(dt, simulationTime, iterationCount);
        }
    }

    /**
     * @return the trafficLights
     */
    public TrafficLights getTrafficLights() {
        return trafficLights;
    }

    /**
     * @return the timeStep
     */
    public double getTimeStep() {
        return timeStep;
    }

    /**
     * @param timeStep the timeStep to set
     */
    public void setTimeStep(double timeStep) {
        this.timeStep = timeStep;
    }

    public void addTimeStep(SimulationTimeStep simulationTimeStep, int index) {
        this.timeSteps.add(index, simulationTimeStep);
    }
}