/*******************************************************************************
* 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.core.statistics;
import gnu.trove.TDoubleArrayList;
import java.math.BigDecimal;
import staticContent.evaluation.simulator.Simulator;
import staticContent.evaluation.simulator.annotations.property.IntSimulationProperty;
import staticContent.evaluation.simulator.annotations.property.requirements.SimulationEndSimulationTimeEndRequirement;
import staticContent.evaluation.simulator.core.event.Event;
import staticContent.evaluation.simulator.core.event.EventExecutor;
import staticContent.evaluation.simulator.core.networkComponent.Identifiable;
import staticContent.evaluation.simulator.core.statistics.aggregator.Aggregator.InputDataType;
import userGeneratedContent.simulatorPlugIns.pluginRegistry.StatisticsType;
public class Statistics implements EventExecutor {
private static final int INITIAL_SIZE = 10000;
private TDoubleArrayList[] recordedDoubleValues; // two dimensions (0:StatisticsType, 1:valueList)
private BigDecimal[] summedUpValues; // one dimension (0:StatisticsType)
private int[][] recordedBooleanValues; // three dimensions ([StatisticsType] [0:trueCtr, 1:falseCtr])
//private Identifiable owner;
private static boolean recordStatistics = false;
private static Simulator simulator;
@IntSimulationProperty( name = "Simulation time limit (ms)",
key = "SIMULATION_TIME_LIMIT_IN_MS",
inject = "4:SIMULATION,Simulation",
min = 0,
isStatic = true,
enable_requirements = SimulationEndSimulationTimeEndRequirement.class)
private static int recordStatisticsEnd;
public Statistics(Identifiable owner) { // is "owner" still needed?
//this.owner = owner;
GeneralStatistics.registerStatisticsObject(this);
recordedDoubleValues = new TDoubleArrayList[StatisticsType.values().length];
summedUpValues = new BigDecimal[StatisticsType.values().length];
recordedBooleanValues = new int[StatisticsType.values().length][];
}
// dummy constructor used for scheduling start and end of statistics recording (see below)
private Statistics(int i) {}
// DESIRED_EVALUATIONS
public static void setSimulator(Simulator simulator) {
Statistics.simulator = simulator;
simulator.ts_recordStatisticsStart = Simulator.settings.getPropertyAsInt("START_RECORDING_STATISTICS_AT");
Statistics s = new Statistics(0);
// set start
if (simulator.ts_recordStatisticsStart == 0) {
recordStatistics = true;
} else {
simulator.scheduleEvent(new Event(s, simulator.ts_recordStatisticsStart, StatisticsEvent.START_RECORDING), s);
}
// set fixed end (if specified)
if (Simulator.settings.getProperty("SIMULATION_END").equals("SIMULATION_TIME_END")) {
recordStatisticsEnd = Simulator.settings.getPropertyAsInt("SIMULATION_TIME_LIMIT_IN_MS");
simulator.scheduleEvent(new Event(s, recordStatisticsEnd, StatisticsEvent.STOP_RECORDING), s);
}
}
public void increment(double value, StatisticsType statisticsType) {
if (!statisticsType.isActivated || !recordStatistics)
return;
checkIfDataTypeAllowed(statisticsType, InputDataType.BIG_DECIMAL);
if (summedUpValues[statisticsType.ordinal()] == null) // first call
summedUpValues[statisticsType.ordinal()] = new BigDecimal(0);
summedUpValues[statisticsType.ordinal()] = summedUpValues[statisticsType.ordinal()].add(new BigDecimal(value));
}
public void addValue(boolean value, StatisticsType statisticsType) {
if (!statisticsType.isActivated || !recordStatistics)
return;
checkIfDataTypeAllowed(statisticsType, InputDataType.BOOLEAN);
if (recordedBooleanValues[statisticsType.ordinal()] == null)
recordedBooleanValues[statisticsType.ordinal()] = new int[2];
if (value)
recordedBooleanValues[statisticsType.ordinal()][0]++;
else
recordedBooleanValues[statisticsType.ordinal()][1]++;
}
public void addValue(double value, StatisticsType statisticsType) {
if (!statisticsType.isActivated || !recordStatistics)
return;
checkIfDataTypeAllowed(statisticsType, InputDataType.DOUBLE);
if (recordedDoubleValues[statisticsType.ordinal()] == null)
recordedDoubleValues[statisticsType.ordinal()] = new TDoubleArrayList(INITIAL_SIZE);
recordedDoubleValues[statisticsType.ordinal()].add(value);
}
public TDoubleArrayList getRecordedDoubleValues(StatisticsType statisticsType) throws NullPointerException {
checkIfDataAvailable(statisticsType, InputDataType.DOUBLE);
return recordedDoubleValues[statisticsType.ordinal()];
}
public int[] getRecordedBooleanValues(StatisticsType statisticsType) throws NullPointerException {
checkIfDataAvailable(statisticsType, InputDataType.BOOLEAN);
return recordedBooleanValues[statisticsType.ordinal()];
}
public BigDecimal getRecordedSum(StatisticsType statisticsType) throws NullPointerException {
checkIfDataAvailable(statisticsType, InputDataType.BIG_DECIMAL);
return summedUpValues[statisticsType.ordinal()];
}
public double getResult(StatisticsType statisticsType) throws NullPointerException {
if (containsDouble(statisticsType)) {
return statisticsType.sourceValueAggregator.aggregate(recordedDoubleValues[statisticsType.ordinal()]);
} else if (containsBigDecimal(statisticsType)) {
return summedUpValues[statisticsType.ordinal()].doubleValue();
} else if (containsBoolean(statisticsType)) {
TDoubleArrayList input = new TDoubleArrayList(2); // boolean aggregator needs special input format
input.add(recordedBooleanValues[statisticsType.ordinal()][0]);
input.add(recordedBooleanValues[statisticsType.ordinal()][1]);
return statisticsType.sourceValueAggregator.aggregate(input);
} else
throw new RuntimeException("ERROR: no results recorded for " +statisticsType +". Use containsData(StatisticsType) before calling this method.");
}
public boolean containsData(StatisticsType statisticsType) {
return containsBoolean(statisticsType) || containsBigDecimal(statisticsType) || containsDouble(statisticsType);
}
public boolean containsData(StatisticsType statisticsType, InputDataType inputDataType) {
if (inputDataType == InputDataType.BOOLEAN)
return containsBoolean(statisticsType);
else if (inputDataType == InputDataType.DOUBLE)
return containsDouble(statisticsType);
else if (inputDataType == InputDataType.BIG_DECIMAL)
return containsBigDecimal(statisticsType);
else
throw new RuntimeException("ERROR: unknown InputDataType: " +inputDataType);
}
public boolean containsBoolean(StatisticsType statisticsType) {
return recordedBooleanValues[statisticsType.ordinal()] != null;
}
public boolean containsBigDecimal(StatisticsType statisticsType) {
return summedUpValues[statisticsType.ordinal()] != null;
}
public boolean containsDouble(StatisticsType statisticsType) {
return recordedDoubleValues[statisticsType.ordinal()] != null;
}
private void checkIfDataTypeAllowed(StatisticsType statisticsType, InputDataType inputDataType) {
InputDataType typeAgg1 = statisticsType.sourceValueAggregator.inputDataType;
InputDataType typeAgg2 = statisticsType.sourceAggregators[0].inputDataType;
if (typeAgg1 == inputDataType)
return;
if (typeAgg1 == InputDataType.NONE)
if (typeAgg2 == InputDataType.NONE || typeAgg2 == inputDataType)
return;
throw new RuntimeException("ERROR: wrong data type for " +statisticsType
+". " +statisticsType +" supports " +inputDataType +" only. " +
"Use increment(double, StatisticsType) for BIG_DECIMAL, " +
"addValue(boolean, StatisticsType) for BOOLEAN and " +
"addValue(double, StatisticsType) for DOUBLE.");
}
private void checkIfDataAvailable(StatisticsType statisticsType, InputDataType inputDataType) {
if (!containsData(statisticsType, inputDataType))
throw new RuntimeException("ERROR: no " +inputDataType +" data recorded for StatisticsType " +statisticsType +". Use containsData(StatisticsType, InputDataType) to check if data is available.");
InputDataType typeAgg1 = statisticsType.sourceValueAggregator.inputDataType;
InputDataType typeAgg2 = statisticsType.sourceAggregators[0].inputDataType;
if (typeAgg1 == inputDataType)
return;
if (typeAgg1 == InputDataType.NONE)
if (typeAgg2 == InputDataType.NONE || typeAgg2 == inputDataType)
return;
throw new RuntimeException("ERROR: This statistics object contains " +
"data of another data-type! Use containsBoolean(StatisticsType), " +
"containsBigDecimal(StatisticsType) and " +
"containsDouble(StatisticsType) to determine the right data type " +
"before requesting data from this object.");
}
@Override
public void executeEvent(Event event) {
if (event.getEventType() == StatisticsEvent.START_RECORDING) {
recordStatistics = true;
System.out.println("### START recording statistics");
} else if (event.getEventType() == StatisticsEvent.STOP_RECORDING) {
System.out.println("### STOP recording statistics");
recordStatistics = false;
simulator.stopSimulation("simulation-time limit reached (variable SIMULATION_TIME_LIMIT_IN_MS in experiment config)");
} else {
throw new RuntimeException("ERROR! received unsupported event!" +event);
}
}
// added by Stefan Rode
public static void setRecordStatistics(boolean recordStatistics) {
Statistics.recordStatistics = recordStatistics;
}
}