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 | } |