/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package staticContent.evaluation.simulator; import java.util.HashMap; import java.util.Map; import java.util.PriorityQueue; import staticContent.evaluation.simulator.annotations.property.BoolSimulationProperty; import staticContent.evaluation.simulator.annotations.property.DoubleSimulationProperty; import staticContent.evaluation.simulator.annotations.property.IntSimulationProperty; import staticContent.evaluation.simulator.annotations.property.StringSimulationProperty; import staticContent.evaluation.simulator.annotations.property.requirements.SimulationEndRealTimeEndRequirement; import staticContent.evaluation.simulator.core.ExperimentConfig; import staticContent.evaluation.simulator.core.binding.gMixBinding; import staticContent.evaluation.simulator.core.event.Event; import staticContent.evaluation.simulator.core.networkComponent.AbstractClient; import staticContent.evaluation.simulator.core.networkComponent.DistantProxy; import staticContent.evaluation.simulator.core.networkComponent.IdGenerator; import staticContent.evaluation.simulator.core.networkComponent.Identifiable; import staticContent.evaluation.simulator.core.networkComponent.Mix; import staticContent.evaluation.simulator.core.networkComponent.NetworkConnection; import staticContent.evaluation.simulator.core.statistics.GeneralStatistics; import staticContent.evaluation.simulator.core.statistics.ResultSet; import staticContent.evaluation.simulator.core.statistics.Statistics; import staticContent.framework.config.Paths; import staticContent.framework.config.Settings; import staticContent.framework.launcher.CommandLineParameters; import staticContent.framework.launcher.GMixTool; import staticContent.framework.util.Util; import userGeneratedContent.simulatorPlugIns.pluginRegistry.ClientSendStyle; import userGeneratedContent.simulatorPlugIns.pluginRegistry.OutputStrategy; import userGeneratedContent.simulatorPlugIns.pluginRegistry.PlotType; import userGeneratedContent.simulatorPlugIns.pluginRegistry.StatisticsType; import userGeneratedContent.simulatorPlugIns.pluginRegistry.Topology; import userGeneratedContent.simulatorPlugIns.pluginRegistry.TrafficSource; import userGeneratedContent.simulatorPlugIns.plugins.outputStrategy.OutputStrategyImpl; import gnu.trove.TDoubleArrayList; public class Simulator extends GMixTool implements Identifiable { //private static Logger logger = Logger.getLogger(Simulator.class); private final int numericIdentifier; public static Settings settings; gMixBinding gmixbind = null; @BoolSimulationProperty( name = "Debug output", key = "DEBUG_OUTPUT", inject = "0:SIMULATION,Simulation", // value=false, isStatic = true, info = "If this option is enabled the simulator will generate debug output. Simulations with debug output may perform significantly longer!") public static boolean DEBUG_ON = true; private static long now = 0; private static Simulator currentSimulator = null; private static CommandLineParameters commandLineParameters; private final PriorityQueue<Event> eventQueue = new PriorityQueue<Event>(); private HashMap<String, AbstractClient> clients; private HashMap<String, Mix> mixes; private HashMap<String, NetworkConnection> networkConnections; private DistantProxy distantProxy; private AbstractClient[] clientByIdArray; private static boolean firstRun = true; //private static XMLResource generalConfig; private volatile boolean stopSimulation = false; private int voteStopCounter = 0; @IntSimulationProperty( name = "Recording start (ms)", key = "START_RECORDING_STATISTICS_AT", inject = "1:SIMULATION,Simulation", isStatic = true, min = 0) public long ts_recordStatisticsStart = Util.NOT_SET; // timestamp public long ts_recordStatisticsEnd = Util.NOT_SET; @DoubleSimulationProperty( name = "Real time limit (s)", key = "REAL_TIME_LIMIT_IN_SEC", inject = "5:SIMULATION,Simulation", isStatic = true, min = 0, enable_requirements = SimulationEndRealTimeEndRequirement.class) int delay; // TODO: pass through constructor @StringSimulationProperty( name = "Experiments to perform", key = "DESIRED_EVALUATIONS", inject = "2:SIMULATION,Simulation", possibleValues = "@StatisticsType", multiSelection = true, isStatic = true, info = "Multiselection is possible") private static String desiredExperiments; // Requirement @StringSimulationProperty( name = "Simulation end condition", key = "SIMULATION_END", inject = "3:SIMULATION,Simulation", possibleValues = "REAL_TIME_END,SIMULATION_TIME_END,END_OF_TRACE_FILE_REACHED", isStatic = true) private static String endCondition; public static Statistics trafficSourceStatistics; private Topology topology; private TrafficSource trafficSource; public ResultSet results; public static void reset() { Simulator.firstRun = true; } /*public Simulator(CommandLineParameters params, boolean isGUI) { this(params.setSimGuiTrue()); }*/ public Simulator(CommandLineParameters params) { Simulator.commandLineParameters = params; now = 0; this.numericIdentifier = IdGenerator.getId(); this.results = null; if (firstRun) { firstRun = false; if (commandLineParameters.useSimGui){ // GUI call; note: gui dows Simulator.settings = new Settings(Paths.SIM_PROPERTY_FILE_PATH); Simulator.settings.addProperties(commandLineParameters.passthroughParameters); Simulator.DEBUG_ON = Simulator.settings.getPropertyAsBoolean("DEBUG_OUTPUT"); Simulator.currentSimulator = this; Statistics.setSimulator(this); Simulator.trafficSourceStatistics = new Statistics(this); this.results = performExperimentReturnResults(Simulator.settings, settings.getProperty("EXPERIMENTS_TO_PERFORM")); }/* else if (commandLineParameters.globalConfigFile != null) { Simulator.settings = new Settings(Paths.SIM_PROPERTY_FILE_PATH); Simulator.settings.addProperties(Paths.SIM_EXPERIMENT_DEFINITION_FOLDER_PATH +commandLineParameters.globalConfigFile); Simulator.desiredExperiments = settings.getProperty("EXPERIMENTS_TO_PERFORM"); Simulator.DEBUG_ON = Simulator.settings.getPropertyAsBoolean("DEBUG_OUTPUT"); Simulator.currentSimulator = this; Statistics.setSimulator(this); Simulator.trafficSourceStatistics = new Statistics(this); performExperiment(Simulator.settings, settings.getProperty("EXPERIMENTS_TO_PERFORM")); }*/ else { Simulator.settings = new Settings(Paths.SIM_PROPERTY_FILE_PATH); if (commandLineParameters.userHasProvidedACustomConfigFile) settings.setProperty("EXPERIMENTS_TO_PERFORM", commandLineParameters.configFile); else if (commandLineParameters.passthroughParameters.length > 0) settings.setProperty("EXPERIMENTS_TO_PERFORM", commandLineParameters.passthroughParameters[0]); Simulator.desiredExperiments = settings.getProperty("EXPERIMENTS_TO_PERFORM"); Simulator.DEBUG_ON = Simulator.settings.getPropertyAsBoolean("DEBUG_OUTPUT"); for (String desiredExperiment: desiredExperiments.split(",")) { Simulator.settings = new Settings(Paths.SIM_PROPERTY_FILE_PATH); Simulator.settings.addProperties(Paths.SIM_EXPERIMENT_DEFINITION_FOLDER_PATH +desiredExperiment); Simulator.currentSimulator = this; Statistics.setSimulator(this); Simulator.trafficSourceStatistics = new Statistics(this); performExperiment(Simulator.settings, desiredExperiment); } } } else { Statistics.setSimulator(this); Simulator.trafficSourceStatistics = new Statistics(this); Simulator.currentSimulator = this; } } private void executeSimulationScript(String simulationScript) { // init traffic source this.trafficSource = TrafficSource.getTrafficSource(); this.clientByIdArray = this.trafficSource.createClientsArray(); // init topology this.topology = Topology.getTopology(); this.topology.constructor(this.clientByIdArray); this.clients = this.topology.getClients(); this.mixes = this.topology.getMixes(); this.networkConnections = this.topology.getNetworkConnections(); this.distantProxy = this.topology.getDistantProxy(); // create output strategies and sending styles: if (this.mixes.size() == 0) { for (AbstractClient client:this.clients.values()) { client.setSendStyle(ClientSendStyle.getInstance(client)); } } else { for (Mix mix:this.mixes.values()) { OutputStrategyImpl os = OutputStrategy.getInstance(mix); mix.setOutputStrategy(os); if (mix.isFirstMix()) { for (AbstractClient client:this.clients.values()) { client.setSendStyle(os.getClientSendStyle(client)); } } } } Simulator.endCondition = Simulator.settings.getProperty("SIMULATION_END"); if (Simulator.endCondition.equals("REAL_TIME_END")) { new StopSimulationTimer(this); } } public int getNumberOfClients() { return this.clientByIdArray.length; } private void performSimulation() { Event event; this.trafficSource.startSending(); while (true) { if (gMixBinding.shouldStop()){ return; } event = this.eventQueue.poll(); // get next event if ((event == null) || this.stopSimulation) { // stop simulation if (event == null) { System.out.println("### simulation finished (no more events to simulate)"); } else { System.out.println("### simulation finished"); } if (this.ts_recordStatisticsEnd == Util.NOT_SET) { this.ts_recordStatisticsEnd = getNow(); } return; } else if (event.isCanceled()) { continue; } else { // execute event now = event.getExecutionTime(); assert event.getTarget() != null; event.setExecuted(); event.getTarget().executeEvent(event); } } } private long sequenceCounter = 0; // callingInstance = DEBUG public void scheduleEvent(Event e, Object callingInstance) { //System.out.println("Received ScheduleTask from " +callingInstance +" (now: " +e.getExecutionTime() +") " +e.getAttachment()); if (e.getExecutionTime() < now) { throw new RuntimeException("ERROR: executionTime < now (" +e.getExecutionTime() +" < " +now); } else { if (this.sequenceCounter == Long.MAX_VALUE) { System.out.println("RESETTING SEQUENCE_COUNTER"); this.sequenceCounter = this.eventQueue.size() + 1; long diff = Long.MAX_VALUE - this.eventQueue.size(); for (Event event:this.eventQueue) { event.setSequenceNumber(event.getSequenceNumber() - diff); } } this.sequenceCounter++; e.setSequenceNumber(this.sequenceCounter); this.eventQueue.add(e); } } public void unscheduleEvent(Event e) { e.cancel(); } /** * @return the now */ public static long getNow() { return now; } public static Simulator getSimulator() { return currentSimulator; } public HashMap<String, AbstractClient> getClients() { return this.clients; } public AbstractClient getClientById(int clientId) { return this.clientByIdArray[clientId]; } public HashMap<String, Mix> getMixes() { return this.mixes; } /** * @return the networkConnections */ public Map<String, NetworkConnection> getNetworkConnections() { return this.networkConnections; } public DistantProxy getDistantProxy() { return this.distantProxy; } public void stopSimulation(String reason) { if (this.stopSimulation) { return; } this.stopSimulation = true; System.out.println("### stopping simulation. reason: " +reason); } public void voteForStop() { this.voteStopCounter++; if (this.voteStopCounter == this.clients.size()) { this.stopSimulation("end of trace reached (variable SIMULATION_END in experiment config)"); } } private static void performExperiment(Settings settings, String expScriptName) { expScriptName = Util.removeFileExtension(expScriptName); ExperimentConfig ep = new ExperimentConfig(settings); saveConfigToDisk(ep, expScriptName); ResultSet resultSet = generateResultSet(ep); storeAndPlotResults(resultSet); StatisticsType.reset(); } private static ResultSet performExperimentReturnResults(Settings settings, String expScriptName) { ExperimentConfig ep = new ExperimentConfig(settings); saveConfigToDisk(ep, expScriptName); ResultSet resultSet = generateResultSet(ep); storeAndPlotResults(resultSet); StatisticsType.reset(); return resultSet; } private static void saveConfigToDisk(ExperimentConfig ep, String expScriptName) { String description = "# " +ep.experimentStart +" experiment:\n"; if (!ep.useSecondPropertyToVary) { description += "# effect of " +ep.propertyToVary +" on ["; for (StatisticsType st: ep.desiredStatisticsTypes) { description += " " +st; } description += "]\n"; } else { description += "# effect of " +ep.propertyToVary +" and " +ep.secondPropertyToVary +" on ["; for (StatisticsType st: ep.desiredStatisticsTypes) { description += " " +st; } description += "]\n"; } String config = ""; for (String property: settings.getPropertiesObject().stringPropertyNames()) { config += property + " = " +settings.getProperty(property) + "\n"; } Util.writeToFile( description + config, Paths.SIM_OUTPUT_FOLDER_PATH + ep.experimentStart + "-config-" +expScriptName +".txt"); } private static void storeAndPlotResults(ResultSet resultSet) { for (PlotType plotType:resultSet.getDesiredPlotTypes()) { plotType.plot(resultSet); } } private static ResultSet generateResultSet(ExperimentConfig ep) { Simulator simulator; ResultSet resultSet = new ResultSet(ep); TDoubleArrayList[][][] results = resultSet.results; long startOfExperiment = System.currentTimeMillis(); // generate results and store them in ResultSet for (int i=0; i<ep.values.length; i++) { // for each value of the parameter(s) to vary // set value(s) of the parameter(s) to vary String value = ep.values[i]; settings.setProperty(ep.propertyToVary, value); String secondValue = null; if (ep.useSecondPropertyToVary) { secondValue = ep.valuesForSecondProperty[i]; settings.setProperty(ep.secondPropertyToVary, secondValue); } System.out.println(); if (!ep.useSecondPropertyToVary) { System.out.println("### STARTING NEW RUN SERIES (" +(i+1) +"/" +(ep.values.length) +"): " +ep.propertyToVary +"=" +value); } else { System.out.println("### STARTING NEW RUN SERIES (" +(i+1) +"/" +(ep.values.length) +"): " +ep.propertyToVary +"=" +value +", " +ep.secondPropertyToVary +"=" +secondValue); } for (int j=0; j<ep.runs; j++) { // for each validation run System.out.println("### STARTING RUN " +(j+1) +"/" +ep.runs); simulator = new Simulator(commandLineParameters); simulator.executeSimulationScript(ep.simulationScript); long start = System.currentTimeMillis(); simulator.performSimulation(); resultSet.simulationTime[i][j] = simulator.ts_recordStatisticsEnd - simulator.ts_recordStatisticsStart; resultSet.numberOfClients[i] = simulator.clients.size(); resultSet.numberOfMixes[i] = simulator.mixes.size(); for (AbstractClient client: simulator.clients.values()) client.close(); System.out.println("### FINISHED RUN " +(j+1) +"/" +ep.runs +" (execution time: " +(System.currentTimeMillis() - start) +" ms, simulation time: " +resultSet.simulationTime[i][j] +"ms)"); // calculate results for (StatisticsType st: ep.desiredStatisticsTypes) { results[i][st.ordinal()][j] = GeneralStatistics.getResult(st); } GeneralStatistics.reset(); AbstractClient.reset(); } // end for each validation run } // end for each value of the parameter to vary System.out.println("### total execution time of experiment: " +(System.currentTimeMillis() - startOfExperiment)); return resultSet; } @Override public int getGlobalId() { return this.numericIdentifier; } private class StopSimulationTimer extends Thread { private final Simulator simulator; private long sleepUntil; public StopSimulationTimer(Simulator simulator) { this.simulator = simulator; this.start(); } @Override public void run() { int delay = (int) Math.round(Simulator.settings.getPropertyAsDouble("REAL_TIME_LIMIT_IN_SEC") * 1000d); this.sleepUntil = System.currentTimeMillis() + delay; while (System.currentTimeMillis() < this.sleepUntil) { try { Thread.sleep(Math.max(0, this.sleepUntil - System.currentTimeMillis())); } catch (InterruptedException e) { continue; } } this.simulator.stopSimulation("real-time limit reached (variable REAL_TIME_LIMIT_IN_SEC in experiment config)"); } } /** * Comment * * @param args Not used. */ public static void main(String[] args) { new Simulator(new CommandLineParameters(args)); } public void setBinging(gMixBinding gMixBinding) { gmixbind = gMixBinding; } }