| 1 | package de.uka.ipd.sdq.sensorframework.adapter; |
| 2 | |
| 3 | import java.util.HashMap; |
| 4 | |
| 5 | import de.uka.ipd.sdq.codegen.simudatavisualisation.datatypes.Histogram; |
| 6 | import de.uka.ipd.sdq.codegen.simudatavisualisation.datatypes.HistogramBucketInformation; |
| 7 | import de.uka.ipd.sdq.sensorframework.entities.Measurement; |
| 8 | import de.uka.ipd.sdq.sensorframework.entities.SensorAndMeasurements; |
| 9 | import de.uka.ipd.sdq.sensorframework.entities.TimeSpanMeasurement; |
| 10 | |
| 11 | /**Adapter for TimeSpanSensors to Histograms. |
| 12 | * @author groenda |
| 13 | */ |
| 14 | public class TimeSpanToHistogramAdapter extends DataAdapter { |
| 15 | |
| 16 | /** The identifier for the property "Histogram Width". */ |
| 17 | public static final String HISTOGRAM_WIDTH = "HISTOGRAM_WIDTH"; |
| 18 | |
| 19 | // private static final String ACTIVEDE_FILTERS = "ACTIVEDE_FILTERS"; |
| 20 | // private FilteredMeasurementsCollection measurements; |
| 21 | /** Information about the TimeSpanSensor and the measurements. */ |
| 22 | private SensorAndMeasurements samInformation; |
| 23 | |
| 24 | /**Initializes a new adapter for the provided TimeSpanSensor. |
| 25 | * @param sensorAndMeasurements Information about the TimeSpanSensor and the measurements. |
| 26 | */ |
| 27 | public TimeSpanToHistogramAdapter( |
| 28 | final SensorAndMeasurements sensorAndMeasurements) { |
| 29 | super(); |
| 30 | this.samInformation = sensorAndMeasurements; |
| 31 | |
| 32 | /* Check if there would be at least to different buckets for the |
| 33 | * histogram as JFreeChart otherwise displays a bar with default |
| 34 | * width and the small values are very difficult to identify. |
| 35 | */ |
| 36 | TimeSpanMeasurement timeSpanMeasurement = null; |
| 37 | double minValue = Double.MAX_VALUE, maxValue = Double.MIN_VALUE; |
| 38 | for (Measurement measurement : samInformation.getMeasurements()) { |
| 39 | timeSpanMeasurement = (TimeSpanMeasurement) measurement; |
| 40 | minValue = (minValue < timeSpanMeasurement.getTimeSpan()) |
| 41 | ? minValue : timeSpanMeasurement.getTimeSpan(); |
| 42 | maxValue = (maxValue > timeSpanMeasurement.getTimeSpan()) |
| 43 | ? maxValue : timeSpanMeasurement.getTimeSpan(); |
| 44 | } |
| 45 | if (maxValue - minValue < Histogram.DEFAULT_BUCKET_WIDTH) { |
| 46 | //whole histogram would be in one bucket, thus set bucket width to 1/2 of total width. |
| 47 | this.adapterProperties.put(HISTOGRAM_WIDTH, |
| 48 | (maxValue - minValue) / 2.0); |
| 49 | } else { |
| 50 | double currentNumberOfBuckets = (maxValue - minValue)/Histogram.DEFAULT_BUCKET_WIDTH; |
| 51 | if (currentNumberOfBuckets > Histogram.MAXIMUM_NUMBER_OF_BUCKETS){ |
| 52 | //do not show too many buckets (not more than Histogram.MAXIMUM_NUMBER_OF_BUCKETS) |
| 53 | //Divide by max number to get the factor to make the buckets larger by. |
| 54 | double widthFactor = currentNumberOfBuckets / Histogram.MAXIMUM_NUMBER_OF_BUCKETS; |
| 55 | |
| 56 | //Also, only use whole numbers for bucket width. Thus, round up after applying the factor. |
| 57 | //TODO: Only round up if the number is larger than 1 |
| 58 | double newNumberOfBuckets = Math.ceil(Histogram.DEFAULT_BUCKET_WIDTH * widthFactor); |
| 59 | this.adapterProperties.put(HISTOGRAM_WIDTH, |
| 60 | newNumberOfBuckets); |
| 61 | } else { |
| 62 | this.adapterProperties.put(HISTOGRAM_WIDTH, |
| 63 | Histogram.DEFAULT_BUCKET_WIDTH); |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | // this.properties.put(ACTIVEDE_FILTERS, false); |
| 68 | // this.measurements = new FilteredMeasurementsCollection( |
| 69 | // sensorAndMeasurements); |
| 70 | } |
| 71 | |
| 72 | /** {@inheritDoc} |
| 73 | */ |
| 74 | public Object getAdaptedObject() { |
| 75 | // SensorAndMeasurements values = (Boolean) properties |
| 76 | // .get(ACTIVEDE_FILTERS) ? (SensorAndMeasurements) measurements |
| 77 | // .getAdaptedObject() : myValues; |
| 78 | |
| 79 | double histWidth = (Double) adapterProperties.get(HISTOGRAM_WIDTH); |
| 80 | if (histWidth <= 0) { |
| 81 | throw new RuntimeException("Histogram width must be > 0"); |
| 82 | } |
| 83 | |
| 84 | Histogram histogram = new Histogram( |
| 85 | samInformation.getSensor().getSensorName(), histWidth); |
| 86 | HashMap<Integer, Integer> histClasses = new HashMap<Integer, Integer>(); |
| 87 | |
| 88 | int maxHistClass = |
| 89 | assignMeasurementsToClasses(histClasses, histWidth); |
| 90 | storeClassifiedMeasurementsInHistogram( |
| 91 | histogram, histClasses, maxHistClass); |
| 92 | |
| 93 | return histogram; |
| 94 | } |
| 95 | |
| 96 | /**Stores the classified measurements in a histogram. |
| 97 | * @param histogram The histogram in which the information is stored. |
| 98 | * @param histClasses The classes and the frequency of the measurements |
| 99 | * per class. |
| 100 | * @param maxHistClass The class with the higher number in which the |
| 101 | * frequency is greater than 0. |
| 102 | */ |
| 103 | private void storeClassifiedMeasurementsInHistogram(Histogram histogram, |
| 104 | final HashMap<Integer, Integer> histClasses, |
| 105 | final int maxHistClass) { |
| 106 | double histWidth = histogram.getBucketWidth(); |
| 107 | boolean firstValueFound = false; |
| 108 | for (int i = 0; i <= maxHistClass; i++) { |
| 109 | Integer histClass = histClasses.get(i); |
| 110 | if (histClass == null) { |
| 111 | /* only append classes with 0 probability if there is at least |
| 112 | * one class with probability >=0 in the left hand side of the |
| 113 | * current class. |
| 114 | */ |
| 115 | if (firstValueFound) { |
| 116 | histogram.addEntity(new HistogramBucketInformation(0.0, |
| 117 | i * histWidth)); |
| 118 | } |
| 119 | } else { |
| 120 | firstValueFound = true; |
| 121 | // calculate probability and store class |
| 122 | histogram.addEntity(new HistogramBucketInformation( |
| 123 | histClass.doubleValue() |
| 124 | / (double) samInformation.getMeasurements().size(), |
| 125 | i * histWidth)); |
| 126 | } |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | /**Assigns the measurement to histogram classes. |
| 131 | * The number of measurements is stored for each of the classes. |
| 132 | * @param histClasses Receives the number of measurements for each class. |
| 133 | * @param histWidth width of the classes of a histogram. |
| 134 | * @return number of the highest class. |
| 135 | */ |
| 136 | private int assignMeasurementsToClasses( |
| 137 | HashMap<Integer, Integer> histClasses, final double histWidth) { |
| 138 | int maxHistClass = 0; |
| 139 | int histogramClassNumber; |
| 140 | for (Measurement measurement : samInformation.getMeasurements()) { |
| 141 | TimeSpanMeasurement tsm = (TimeSpanMeasurement) measurement; |
| 142 | /* checks to which class the measurement belongs. |
| 143 | * The class number always starts at 0, which means the interval |
| 144 | * [0..histWidth/2). Uses integer rounding and comparison for |
| 145 | * performance reasons instead of the easier to understand Floor() |
| 146 | */ |
| 147 | histogramClassNumber = (int) (tsm.getTimeSpan() / histWidth); |
| 148 | // increases number of measurements for the class |
| 149 | Integer classNumber = histClasses.get(histogramClassNumber); |
| 150 | if (classNumber == null) { |
| 151 | histClasses.put(histogramClassNumber, 1); |
| 152 | } else { |
| 153 | histClasses.put(histogramClassNumber, classNumber.intValue() |
| 154 | + 1); |
| 155 | } |
| 156 | /*Store the number of the highest class in which a measurement was |
| 157 | * found |
| 158 | */ |
| 159 | if (maxHistClass < histogramClassNumber) { |
| 160 | maxHistClass = histogramClassNumber; |
| 161 | } |
| 162 | } |
| 163 | return maxHistClass; |
| 164 | } |
| 165 | |
| 166 | // public FilteredMeasurementsCollection getMeasurements() { |
| 167 | // return measurements; |
| 168 | // } |
| 169 | } |