Java tutorial
/* * 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); } }