/* * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab * * This file is part of PeerfactSim.KOM. * * PeerfactSim.KOM 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 * any later version. * * PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see . * */ package de.tud.kom.p2psim.impl.topology.movement; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Random; import de.tud.kom.p2psim.api.network.SimNetInterface; import de.tud.kom.p2psim.api.topology.movement.MovementModel; import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator; import de.tud.kom.p2psim.impl.network.routed.RoutedNetLayer; import de.tud.kom.p2psim.impl.topology.PositionVector; import de.tud.kom.p2psim.impl.topology.movement.vehicular.sumo.simulation.controller.traci.TraciSimulationController; import de.tud.kom.p2psim.impl.topology.movement.vehicular.sumo.simulation.controller.xml.XMLSimulationController; import de.tud.kom.p2psim.impl.vehicular.DefaultVehicleInformationComponent; import de.tudarmstadt.maki.simonstrator.api.Event; import de.tudarmstadt.maki.simonstrator.api.EventHandler; import de.tudarmstadt.maki.simonstrator.api.Host; import de.tudarmstadt.maki.simonstrator.api.Randoms; import de.tudarmstadt.maki.simonstrator.api.Time; import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID; import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location; import de.tudarmstadt.maki.simonstrator.api.component.vehicular.VehicleInformationComponent; import de.tudarmstadt.maki.simonstrator.api.component.vehicular.api.SimulationSetupExtractor; import de.tudarmstadt.maki.simonstrator.api.component.vehicular.api.VehicleController; import de.tudarmstadt.maki.simonstrator.api.component.vehicular.roadnetwork.RoadNetwork; import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor; public class VehicleMovementModel implements MovementModel, EventHandler { private Random _knownRoutesRandom = Randoms.getRandom(getClass()); private static final Location DEFAULT_LOCATION = new PositionVector(Double.NaN, Double.NaN); private boolean _reuseComponents = false; private static VehicleMovementModel MOVEMENT; private long timeBetweenMoveOperations; private final List components; private final Queue freeComponents; private final Map idComponentMatcher; private final Map hostVehicleIDMatching = new HashMap<>(); private final int offsetX; private final int offsetY; private final int width; private final int height; private double scenarioWidth = 0; private double scenarioHeight = 0; private final String sumoExe; private final String sumoConfigFile; private final String sumoTrace; private String sumoIntersections; private final long timestepConversion = Time.SECOND; private boolean initialized = false; private VehicleController _controller; private SimulationSetupExtractor _extractor; private double _percentageOfKnownRoutes = 1; /** * Constructor for the movement model using the sumo TraCI API * @param timeBetweenMoveOperations The time between two movement operations. * @param sumoExe The path to the executable of sumo * @param sumoConfigFile The path to the configuration file of the scenario * @param offsetX The offset that should be used. If no offset is required, offset 0 shall be used. * @param offsetY The offset that should be used. If no offset is required, offset 0 shall be used. * @param width The width of the scenario. * @param height The height of the scenario. */ @XMLConfigurableConstructor({ "timeBetweenMoveOperations", "sumoExe", "sumoConfigFile", "offsetX", "offsetY", "width", "height" }) public VehicleMovementModel(long timeBetweenMoveOperations, String sumoExe, String sumoConfigFile, String offsetX, String offsetY, String width, String height) { MOVEMENT = this; this.timeBetweenMoveOperations = timeBetweenMoveOperations; this.components = new LinkedList<>(); this.freeComponents = new LinkedList<>(); this.idComponentMatcher = new HashMap<>(); this.sumoExe = sumoExe; this.sumoConfigFile = sumoConfigFile; this.sumoTrace = null; this.offsetX = Integer.parseInt(offsetX); this.offsetY = Integer.parseInt(offsetY); this.width = Integer.parseInt(width); this.height = Integer.parseInt(height); } /** * Constructor for the movement model using the a generated sumo trace file * @param timeBetweenMoveOperations The time between two movement operations. * @param sumoTrace The path to the vehicle movement file (*.xml) * @param sumoIntersections The path to the intersections file (*.csv) * @param offsetX The offset that should be used. If no offset is required, offset 0 shall be used. * @param offsetY The offset that should be used. If no offset is required, offset 0 shall be used. * @param width The width of the scenario. * @param height The height of the scenario. */ @XMLConfigurableConstructor({ "timeBetweenMoveOperations", "sumoTrace", "sumoIntersections", "offsetX", "offsetY", "width", "height" }) public VehicleMovementModel(long timeBetweenMoveOperations, String sumoTrace, String sumoIntersections, int offsetX, int offsetY, int width, int height) { MOVEMENT = this; this.timeBetweenMoveOperations = timeBetweenMoveOperations; this.components = new LinkedList<>(); this.freeComponents = new LinkedList<>(); this.idComponentMatcher = new HashMap<>(); this.sumoExe = null; this.sumoConfigFile = null; this.sumoTrace = sumoTrace; this.sumoIntersections = sumoIntersections; this.offsetX = offsetX; this.offsetY = offsetY; this.width = width; this.height = height; } /** * @param pPercentageOfKnownRoutes the percentageOfKnownRoutes to set */ public void setPercentageOfKnownRoutes(double pPercentageOfKnownRoutes) { _percentageOfKnownRoutes = pPercentageOfKnownRoutes; } public void setReuseComponents(boolean pReuseComponents) { _reuseComponents = pReuseComponents; if (_reuseComponents) { System.err.println("WARNING: Enabling the reuse of components might cause strange behaviors of your simulation!"); } } /** * Adding an additional component to be moved by this movement model * @param comp The component to be added. */ @Override public final void addComponent(SimLocationActuator comp) { components.add(comp); freeComponents.add(comp); comp.updateCurrentLocation(DEFAULT_LOCATION); } /** * Returns the time between movement operations * @return time between movement operations */ public long getTimeBetweenMoveOperations() { return timeBetweenMoveOperations; } /** * Set the time between movement operations * @param time the time between movement operations */ @Override public void setTimeBetweenMoveOperations(long time) { this.timeBetweenMoveOperations = time; } /** * Place a component at the correct location * @param actuator The component to be placed. */ @Override public void placeComponent(SimLocationActuator actuator) { if (!initialized) { initializeModel(); VehicleMovementModel.getRoadNetwork(); initialized = true; } // Initial placement actuator.updateCurrentLocation(DEFAULT_LOCATION); } /** * Initializes the movement model by executing BonnMotion and parsing the * resulting movement trace. */ protected void initializeModel() { // Schedule first step if (!initialized) { Event.scheduleWithDelay(timeBetweenMoveOperations, this, null, 0); if (sumoExe != null) { TraciSimulationController simulationController = TraciSimulationController.createSimulationController(sumoExe, sumoConfigFile); _controller = simulationController; _controller.setObservedArea(offsetX, offsetY, offsetX + width, offsetY + height); _controller.init(); _controller.nextStep(); _extractor = simulationController; } else { XMLSimulationController simulationController; if (sumoIntersections == null || sumoIntersections.equals("")) { simulationController = new XMLSimulationController(sumoTrace); } else { simulationController = new XMLSimulationController(sumoTrace, sumoIntersections); } _controller = simulationController; _controller.setObservedArea(offsetX, offsetY, offsetX + width, offsetY + height); _controller.init(); _controller.nextStep(); _extractor = simulationController; } scenarioWidth = _extractor.getScenarioWidth(); scenarioHeight = _extractor.getScenarioHeight(); System.out.println("Initialization: done."); } } /** * Used for the periodical updates of the vehicle positions * @param content not used in this case, should be null * @param type not used in this case, should be 0 */ @Override public void eventOccurred(Object content, int type) { /* * One event for all nodes (synchronized movement), as this boosts * simulation performance due to less recalculations in the network * models. */ long currentTime = Time.getCurrentTime() / timestepConversion; while (_controller.getStep() - _controller.getStart() < currentTime) { if (!_controller.nextStep()) { return; } } List allVehicles = _controller.getAllVehicles(); for (int i = 0; i < allVehicles.size(); i++) { String vehicle = allVehicles.get(i); Location position = _controller.getVehiclePosition(vehicle); if (position == null) { allVehicles.remove(i--); continue; } SimLocationActuator component = requestSimActuator(vehicle); try { RoutedNetLayer routedNetLayer = component.getHost().getComponent(RoutedNetLayer.class); for (SimNetInterface netInterface : routedNetLayer.getSimNetworkInterfaces()) { if (netInterface.isOffline()) { netInterface.goOnline(); } } } catch (ComponentNotAvailableException e) { e.printStackTrace(); } component.updateCurrentLocation(new PositionVector(position.getLongitudeOrX(), position.getLatitudeOrY())); component.setMovementSpeed(_controller.getVehicleSpeed(vehicle)); } if (allVehicles.size() != idComponentMatcher.size()) { ArrayList registeredVehicles = new ArrayList<>(idComponentMatcher.keySet()); for (int i = 0; i < registeredVehicles.size(); i++) { String vehicle = registeredVehicles.get(i); if (!allVehicles.contains(vehicle)) { addFreeHost(vehicle); } } } if (Time.getCurrentTime() < 5 * Time.SECOND) { for (SimLocationActuator simLocationActuator : freeComponents) { simLocationActuator.updateCurrentLocation(DEFAULT_LOCATION); } } // Reschedule next step Event.scheduleWithDelay(timeBetweenMoveOperations, this, null, 0); } /** * Remove a vehicle from the set of moved components as the vehicle has stopped driving * @param vehicleID The stopped vehicle */ private void addFreeHost(String vehicleID) { if (idComponentMatcher.containsKey(vehicleID)) { SimLocationActuator simLocationActuator = idComponentMatcher.remove(vehicleID); if (simLocationActuator != null) { try { VehicleInformationComponent vehicularHostComponent = simLocationActuator.getHost().getComponent(VehicleInformationComponent.class); vehicularHostComponent.resetVehicleID(); } catch (ComponentNotAvailableException e) { // Nothing to do here } hostVehicleIDMatching.remove(simLocationActuator.getHost().getId()); try { RoutedNetLayer routedNetLayer = simLocationActuator.getHost().getComponent(RoutedNetLayer.class); for (SimNetInterface netInterface : routedNetLayer.getSimNetworkInterfaces()) { if (netInterface.isOnline()) { netInterface.goOffline(); } } } catch (ComponentNotAvailableException e) { e.printStackTrace(); } simLocationActuator.updateCurrentLocation(DEFAULT_LOCATION); if (_reuseComponents) { freeComponents.add(simLocationActuator); } } } } /** * Request a component for a vehicle. If no component has been assigned to the vehicle yet, a new component is assigned. * @param vehicle The vehicle for which a component is requested. * @throws RuntimeException If no component can be assigned. * @return The component for the vehicle. */ private SimLocationActuator requestSimActuator(String vehicle) { if (!idComponentMatcher.containsKey(vehicle)) { SimLocationActuator simLocationActuator = freeComponents.poll(); if (simLocationActuator != null) { VehicleInformationComponent vehicularHostComponent; try { vehicularHostComponent = simLocationActuator.getHost().getComponent(VehicleInformationComponent.class); } catch (ComponentNotAvailableException e) { Host host = simLocationActuator.getHost(); boolean routeKnown; if (_knownRoutesRandom.nextDouble() < _percentageOfKnownRoutes) { routeKnown = true; } else { routeKnown = false; } vehicularHostComponent = new DefaultVehicleInformationComponent(host, _controller, _extractor, routeKnown); host.registerComponent(vehicularHostComponent); } vehicularHostComponent.setVehicleID(vehicle); idComponentMatcher.put(vehicle, simLocationActuator); hostVehicleIDMatching.put(simLocationActuator.getHost().getId(), vehicle); } else { throw new RuntimeException("Unable to assign new components. Please increase node amount" + (_reuseComponents?".":" or enable the reuse of components.")); } } return idComponentMatcher.get(vehicle); } public static RoadNetwork getRoadNetwork() { return MOVEMENT._extractor.getRoadNetwork(); } }