Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(703)

Side by Side Diff: chrome/browser/resources/chromeos/power.js

Issue 196613002: Revert of [chromeos/about:power] Collect cpuidle and cpufreq stats (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * Plot a line graph of data versus time on a HTML canvas element. 6 * Plot a line graph of data versus time on a HTML canvas element.
7 * 7 *
8 * @param {HTMLCanvasElement} plotCanvas The canvas on which the line graph is 8 * @param {HTMLCanvasElement} canvas The canvas on which the line graph is
9 * drawn. 9 * drawn.
10 * @param {HTMLCanvasElement} legendCanvas The canvas on which the legend for
11 * the line graph is drawn.
12 * @param {Array.<number>} tData The time (in seconds) in the past when the 10 * @param {Array.<number>} tData The time (in seconds) in the past when the
13 * corresponding data in plots was sampled. 11 * corresponding data in plots was sampled.
14 * @param {Array.<{data: Array.<number>, color: string}>} plots An 12 * @param {Array.<{data: Array.<number>, color: string}>} plots An
15 * array of plots to plot on the canvas. The field 'data' of a plot is an 13 * array of plots to plot on the canvas. The field 'data' of a plot is an
16 * array of samples to be plotted as a line graph with color speficied by 14 * array of samples to be plotted as a line graph with color speficied by
17 * the field 'color'. The elements in the 'data' array are ordered 15 * the field 'color'. The elements in the 'data' array are ordered
18 * corresponding to their sampling time in the argument 'tData'. Also, the 16 * corresponding to their sampling time in the argument 'tData'. Also, the
19 * number of elements in the 'data' array should be the same as in the time 17 * number of elements in the 'data' array should be the same as in the time
20 * array 'tData' above. 18 * array 'tData' above.
21 * @param {number} yMin Minimum bound of y-axis 19 * @param {number} yMin Minimum bound of y-axis
22 * @param {number} yMax Maximum bound of y-axis. 20 * @param {number} yMax Maximum bound of y-axis.
23 * @param {integer} yPrecision An integer value representing the number of 21 * @param {integer} yPrecision An integer value representing the number of
24 * digits of precision the y-axis data should be printed with. 22 * digits of precision the y-axis data should be printed with.
25 */ 23 */
26 function plotLineGraph( 24 function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) {
27 plotCanvas, legendCanvas, tData, plots, yMin, yMax, yPrecision) {
28 var textFont = '12px Arial'; 25 var textFont = '12px Arial';
29 var textHeight = 12; 26 var textHeight = 12;
30 var padding = 5; // Pixels 27 var padding = 5; // Pixels
31 var errorOffsetPixels = 15; 28 var errorOffsetPixels = 15;
32 var gridColor = '#ccc'; 29 var gridColor = '#ccc';
33 var plotCtx = plotCanvas.getContext('2d'); 30 var ctx = canvas.getContext('2d');
34 var size = tData.length; 31 var size = tData.length;
35 32
36 function drawText(ctx, text, x, y) { 33 function drawText(text, x, y) {
37 ctx.font = textFont; 34 ctx.font = textFont;
38 ctx.fillStyle = '#000'; 35 ctx.fillStyle = '#000';
39 ctx.fillText(text, x, y); 36 ctx.fillText(text, x, y);
40 } 37 }
41 38
42 function printErrorText(ctx, text) { 39 function printErrorText(text) {
43 ctx.clearRect(0, 0, plotCanvas.width, plotCanvas.height); 40 ctx.clearRect(0, 0, canvas.width, canvas.height);
44 drawText(ctx, text, errorOffsetPixels, errorOffsetPixels); 41 drawText(text, errorOffsetPixels, errorOffsetPixels);
45 } 42 }
46 43
47 if (size < 2) { 44 if (size < 2) {
48 printErrorText(plotCtx, 45 printErrorText(loadTimeData.getString('notEnoughDataAvailableYet'));
49 loadTimeData.getString('notEnoughDataAvailableYet'));
50 return; 46 return;
51 } 47 }
52 48
53 for (var count = 0; count < plots.length; count++) { 49 for (var count = 0; count < plots.length; count++) {
54 if (plots[count].data.length != size) { 50 if (plots[count].data.length != size) {
55 throw new Error('Mismatch in time and plot data.'); 51 throw new Error('Mismatch in time and plot data.');
56 } 52 }
57 } 53 }
58 54
59 function valueToString(value) { 55 function valueToString(value) {
60 if (Math.abs(value) < 1) { 56 return Number(value).toPrecision(yPrecision);
61 return Number(value).toFixed(yPrecision - 1);
62 } else {
63 return Number(value).toPrecision(yPrecision);
64 }
65 } 57 }
66 58
67 function getTextWidth(ctx, text) { 59 function getTextWidth(text) {
68 ctx.font = textFont; 60 ctx.font = textFont;
69 // For now, all text is drawn to the left of vertical lines, or centered. 61 // For now, all text is drawn to the left of vertical lines, or centered.
70 // Add a 2 pixel padding so that there is some spacing between the text 62 // Add a 2 pixel padding so that there is some spacing between the text
71 // and the vertical line. 63 // and the vertical line.
72 return Math.round(ctx.measureText(text).width) + 2; 64 return Math.round(ctx.measureText(text).width) + 2;
73 } 65 }
74 66
75 function drawHighlightText(ctx, text, x, y, color) { 67 function drawHighlightText(text, x, y, color) {
76 ctx.strokeStyle = '#000'; 68 ctx.strokeStyle = '#000';
77 ctx.strokeRect(x, y - textHeight, getTextWidth(ctx, text), textHeight); 69 ctx.strokeRect(x, y - textHeight, getTextWidth(text), textHeight);
78 ctx.fillStyle = color; 70 ctx.fillStyle = color;
79 ctx.fillRect(x, y - textHeight, getTextWidth(ctx, text), textHeight); 71 ctx.fillRect(x, y - textHeight, getTextWidth(text), textHeight);
80 ctx.fillStyle = '#fff'; 72 ctx.fillStyle = '#fff';
81 ctx.fillText(text, x, y); 73 ctx.fillText(text, x, y);
82 } 74 }
83 75
84 function drawLine(ctx, x1, y1, x2, y2, color) { 76 function drawLine(x1, y1, x2, y2, color) {
85 ctx.save(); 77 ctx.save();
86 ctx.beginPath(); 78 ctx.beginPath();
87 ctx.moveTo(x1, y1); 79 ctx.moveTo(x1, y1);
88 ctx.lineTo(x2, y2); 80 ctx.lineTo(x2, y2);
89 ctx.strokeStyle = color; 81 ctx.strokeStyle = color;
90 ctx.stroke(); 82 ctx.stroke();
91 ctx.restore(); 83 ctx.restore();
92 } 84 }
93 85
94 // The strokeRect method of the 2d context of a plotCanvas draws a bounding 86 // The strokeRect method of the 2d context of a canvas draws a bounding
95 // rectangle with an offset origin and greater dimensions. Hence, use this 87 // rectangle with an offset origin and greater dimensions. Hence, use this
96 // function to draw a rect at the desired location with desired dimensions. 88 // function to draw a rect at the desired location with desired dimensions.
97 function drawRect(ctx, x, y, width, height, color) { 89 function drawRect(x, y, width, height, color) {
98 drawLine(ctx, x, y, x + width - 1, y, color); 90 drawLine(x, y, x + width - 1, y, color);
99 drawLine(ctx, x, y, x, y + height - 1, color); 91 drawLine(x, y, x, y + height - 1, color);
100 drawLine(ctx, x, y + height - 1, x + width - 1, y + height - 1, color); 92 drawLine(x, y + height - 1, x + width - 1, y + height - 1, color);
101 drawLine(ctx, x + width - 1, y, x + width - 1, y + height - 1, color); 93 drawLine(x + width - 1, y, x + width - 1, y + height - 1, color);
102 }
103
104 function drawLegend() {
105 // Show a legend only if at least one individual plot has a name.
106 var valid = false;
107 for (var i = 0; i < plots.length; i++) {
108 if (plots[i].name != null) {
109 valid = true;
110 break;
111 }
112 }
113 if (!valid) {
114 legendCanvas.hidden = true;
115 return;
116 }
117
118 var padding = 2;
119 var legendSquareSide = 12;
120 var legendCtx = legendCanvas.getContext('2d');
121 var xLoc = padding;
122 var yLoc = padding;
123 // Adjust the height of the canvas before drawing on it.
124 for (var i = 0; i < plots.length; i++) {
125 if (plots[i].name == null) {
126 continue;
127 }
128 var legendText = ' - ' + plots[i].name;
129 xLoc += legendSquareSide + getTextWidth(legendCtx, legendText) +
130 2 * padding;
131 if (i < plots.length - 1) {
132 var xLocNext = xLoc +
133 getTextWidth(legendCtx, ' - ' + plots[i + 1].name) +
134 legendSquareSide;
135 if (xLocNext >= legendCanvas.width) {
136 xLoc = padding;
137 yLoc = yLoc + 2 * padding + textHeight;
138 }
139 }
140 }
141
142 legendCanvas.height = yLoc + textHeight + padding;
143
144 xLoc = padding;
145 yLoc = padding;
146 // Go over the plots again, this time drawing the legends.
147 for (var i = 0; i < plots.length; i++) {
148 legendCtx.fillStyle = plots[i].color;
149 legendCtx.fillRect(xLoc, yLoc, legendSquareSide, legendSquareSide);
150 xLoc += legendSquareSide;
151
152 var legendText = ' - ' + plots[i].name;
153 drawText(legendCtx, legendText, xLoc, yLoc + textHeight - 1);
154 xLoc += getTextWidth(legendCtx, legendText) + 2 * padding;
155
156 if (i < plots.length - 1) {
157 var xLocNext = xLoc +
158 getTextWidth(legendCtx, ' - ' + plots[i + 1].name) +
159 legendSquareSide;
160 if (xLocNext >= legendCanvas.width) {
161 xLoc = padding;
162 yLoc = yLoc + 2 * padding + textHeight;
163 }
164 }
165 }
166 } 94 }
167 95
168 var yMinStr = valueToString(yMin); 96 var yMinStr = valueToString(yMin);
169 var yMaxStr = valueToString(yMax); 97 var yMaxStr = valueToString(yMax);
170 var yHalfStr = valueToString((yMax + yMin) / 2); 98 var yHalfStr = valueToString((yMax + yMin) / 2);
171 var yMinWidth = getTextWidth(plotCtx, yMinStr); 99 var yMinWidth = getTextWidth(yMinStr);
172 var yMaxWidth = getTextWidth(plotCtx, yMaxStr); 100 var yMaxWidth = getTextWidth(yMaxStr);
173 var yHalfWidth = getTextWidth(plotCtx, yHalfStr); 101 var yHalfWidth = getTextWidth(yHalfStr);
174 102
175 var xMinStr = tData[0]; 103 var xMinStr = tData[0];
176 var xMaxStr = tData[size - 1]; 104 var xMaxStr = tData[size - 1];
177 var xMinWidth = getTextWidth(plotCtx, xMinStr); 105 var xMinWidth = getTextWidth(xMinStr);
178 var xMaxWidth = getTextWidth(plotCtx, xMaxStr); 106 var xMaxWidth = getTextWidth(xMaxStr);
179 107
180 var xOrigin = padding + Math.max(yMinWidth, 108 var xOrigin = padding + Math.max(yMinWidth,
181 yMaxWidth, 109 yMaxWidth,
182 Math.round(xMinWidth / 2)); 110 Math.round(xMinWidth / 2));
183 var yOrigin = padding + textHeight; 111 var yOrigin = padding + textHeight;
184 var width = plotCanvas.width - xOrigin - Math.floor(xMaxWidth / 2) - padding; 112 var width = canvas.width - xOrigin - Math.floor(xMaxWidth / 2) - padding;
185 if (width < size) { 113 if (width < size) {
186 plotCanvas.width += size - width; 114 canvas.width += size - width;
187 width = size; 115 width = size;
188 } 116 }
189 var height = plotCanvas.height - yOrigin - textHeight - padding; 117 var height = canvas.height - yOrigin - textHeight - padding;
190 118
191 function drawPlots() { 119 function drawPlots() {
192 // Start fresh. 120 // Start fresh.
193 plotCtx.clearRect(0, 0, plotCanvas.width, plotCanvas.height); 121 ctx.clearRect(0, 0, canvas.width, canvas.height);
194 122
195 // Draw the bounding rectangle. 123 // Draw the bounding rectangle.
196 drawRect(plotCtx, xOrigin, yOrigin, width, height, gridColor); 124 drawRect(xOrigin, yOrigin, width, height, gridColor);
197 125
198 // Draw the x and y bound values. 126 // Draw the x and y bound values.
199 drawText(plotCtx, yMaxStr, xOrigin - yMaxWidth, yOrigin + textHeight); 127 drawText(yMaxStr, xOrigin - yMaxWidth, yOrigin + textHeight);
200 drawText(plotCtx, yMinStr, xOrigin - yMinWidth, yOrigin + height); 128 drawText(yMinStr, xOrigin - yMinWidth, yOrigin + height);
201 drawText(plotCtx, 129 drawText(xMinStr, xOrigin - xMinWidth / 2, yOrigin + height + textHeight);
202 xMinStr, 130 drawText(xMaxStr,
203 xOrigin - xMinWidth / 2,
204 yOrigin + height + textHeight);
205 drawText(plotCtx,
206 xMaxStr,
207 xOrigin + width - xMaxWidth / 2, 131 xOrigin + width - xMaxWidth / 2,
208 yOrigin + height + textHeight); 132 yOrigin + height + textHeight);
209 133
210 // Draw y-level (horizontal) lines. 134 // Draw y-level (horizontal) lines.
211 drawLine(plotCtx, 135 drawLine(xOrigin + 1, yOrigin + height / 4,
212 xOrigin + 1, yOrigin + height / 4,
213 xOrigin + width - 2, yOrigin + height / 4, 136 xOrigin + width - 2, yOrigin + height / 4,
214 gridColor); 137 gridColor);
215 drawLine(plotCtx, 138 drawLine(xOrigin + 1, yOrigin + height / 2,
216 xOrigin + 1, yOrigin + height / 2,
217 xOrigin + width - 2, yOrigin + height / 2, gridColor); 139 xOrigin + width - 2, yOrigin + height / 2, gridColor);
218 drawLine(plotCtx, 140 drawLine(xOrigin + 1, yOrigin + 3 * height / 4,
219 xOrigin + 1, yOrigin + 3 * height / 4,
220 xOrigin + width - 2, yOrigin + 3 * height / 4, 141 xOrigin + width - 2, yOrigin + 3 * height / 4,
221 gridColor); 142 gridColor);
222 143
223 // Draw half-level value. 144 // Draw half-level value.
224 drawText(plotCtx, 145 drawText(yHalfStr,
225 yHalfStr,
226 xOrigin - yHalfWidth, 146 xOrigin - yHalfWidth,
227 yOrigin + height / 2 + textHeight / 2); 147 yOrigin + height / 2 + textHeight / 2);
228 148
229 // Draw the plots. 149 // Draw the plots.
230 var yValRange = yMax - yMin; 150 var yValRange = yMax - yMin;
231 for (var count = 0; count < plots.length; count++) { 151 for (var count = 0; count < plots.length; count++) {
232 var plot = plots[count]; 152 var plot = plots[count];
233 var yData = plot.data; 153 var yData = plot.data;
234 plotCtx.strokeStyle = plot.color; 154 ctx.strokeStyle = plot.color;
235 plotCtx.beginPath(); 155 ctx.beginPath();
236 var beginPath = true; 156 var beginPath = true;
237 for (var i = 0; i < size; i++) { 157 for (var i = 0; i < size; i++) {
238 var val = yData[i]; 158 var val = yData[i];
239 if (typeof val === 'string') { 159 if (typeof val === 'string') {
240 // Stroke the plot drawn so far and begin a fresh plot. 160 // Stroke the plot drawn so far and begin a fresh plot.
241 plotCtx.stroke(); 161 ctx.stroke();
242 plotCtx.beginPath(); 162 ctx.beginPath();
243 beginPath = true; 163 beginPath = true;
244 continue; 164 continue;
245 } 165 }
246 var xPos = xOrigin + Math.floor(i / (size - 1) * (width - 1)); 166 var xPos = xOrigin + Math.floor(i / (size - 1) * (width - 1));
247 var yPos = yOrigin + height - 1 - 167 var yPos = yOrigin + height - 1 -
248 Math.round((val - yMin) / yValRange * (height - 1)); 168 Math.round((val - yMin) / yValRange * (height - 1));
249 if (beginPath) { 169 if (beginPath) {
250 plotCtx.moveTo(xPos, yPos); 170 ctx.moveTo(xPos, yPos);
251 // A simple move to does not print anything. Hence, draw a little 171 // A simple move to does not print anything. Hence, draw a little
252 // square here to mark a beginning. 172 // square here to mark a beginning.
253 plotCtx.fillStyle = '#000'; 173 ctx.fillStyle = '#000';
254 plotCtx.fillRect(xPos - 1, yPos - 1, 2, 2); 174 ctx.fillRect(xPos - 1, yPos - 1, 2, 2);
255 beginPath = false; 175 beginPath = false;
256 } else { 176 } else {
257 plotCtx.lineTo(xPos, yPos); 177 ctx.lineTo(xPos, yPos);
258 if (i === size - 1 || typeof yData[i + 1] === 'string') { 178 if (i === size - 1 || typeof yData[i + 1] === 'string') {
259 // Draw a little square to mark an end to go with the start 179 // Draw a little square to mark an end to go with the start
260 // markers from above. 180 // markers from above.
261 plotCtx.fillStyle = '#000'; 181 ctx.fillStyle = '#000';
262 plotCtx.fillRect(xPos - 1, yPos - 1, 2, 2); 182 ctx.fillRect(xPos - 1, yPos - 1, 2, 2);
263 } 183 }
264 } 184 }
265 } 185 }
266 plotCtx.stroke(); 186 ctx.stroke();
267 } 187 }
268 188
269 // Paint the missing time intervals with |gridColor|. 189 // Paint the missing time intervals with |gridColor|.
270 // Pick one of the plots to look for missing time intervals. 190 // Pick one of the plots to look for missing time intervals.
271 function drawMissingRect(start, end) {
272 var xLeft = xOrigin + Math.floor(start / (size - 1) * (width - 1));
273 var xRight = xOrigin + Math.floor(end / (size - 1) * (width - 1));
274 plotCtx.fillStyle = gridColor;
275 // The x offsets below are present so that the blank space starts
276 // and ends between two valid samples.
277 plotCtx.fillRect(xLeft + 1, yOrigin, xRight - xLeft - 2, height - 1);
278 }
279 var inMissingInterval = false; 191 var inMissingInterval = false;
280 var intervalStart; 192 var intervalStart;
281 for (var i = 0; i < size; i++) { 193 for (var i = 0; i < size; i++) {
282 if (typeof plots[0].data[i] === 'string') { 194 if (typeof plots[0].data[i] === 'string') {
283 if (!inMissingInterval) { 195 if (!inMissingInterval) {
284 inMissingInterval = true; 196 inMissingInterval = true;
285 // The missing interval should actually start from the previous 197 // The missing interval should actually start from the previous
286 // sample. 198 // sample.
287 intervalStart = Math.max(i - 1, 0); 199 intervalStart = Math.max(i - 1, 0);
288 } 200 }
289
290 if (i == size - 1) {
291 // If this is the last sample, just draw missing rect.
292 drawMissingRect(intervalStart, i);
293 }
294 } else if (inMissingInterval) { 201 } else if (inMissingInterval) {
295 inMissingInterval = false; 202 inMissingInterval = false;
296 drawMissingRect(intervalStart, i); 203 var xLeft = xOrigin +
204 Math.floor(intervalStart / (size - 1) * (width - 1));
205 var xRight = xOrigin + Math.floor(i / (size - 1) * (width - 1));
206 ctx.fillStyle = gridColor;
207 // The x offsets below are present so that the blank space starts
208 // and ends between two valid samples.
209 ctx.fillRect(xLeft + 1, yOrigin, xRight - xLeft - 2, height - 1);
297 } 210 }
298 } 211 }
299 } 212 }
300 213
301 function drawTimeGuide(tDataIndex) { 214 function drawTimeGuide(tDataIndex) {
302 var x = xOrigin + tDataIndex / (size - 1) * (width - 1); 215 var x = xOrigin + tDataIndex / (size - 1) * (width - 1);
303 drawLine(plotCtx, x, yOrigin, x, yOrigin + height - 1, '#000'); 216 drawLine(x, yOrigin, x, yOrigin + height - 1, '#000');
304 drawText(plotCtx, 217 drawText(tData[tDataIndex],
305 tData[tDataIndex], 218 x - getTextWidth(tData[tDataIndex]) / 2,
306 x - getTextWidth(plotCtx, tData[tDataIndex]) / 2,
307 yOrigin - 2); 219 yOrigin - 2);
308 220
309 for (var count = 0; count < plots.length; count++) { 221 for (var count = 0; count < plots.length; count++) {
310 var yData = plots[count].data; 222 var yData = plots[count].data;
311 223
312 // Draw small black square on the plot where the time guide intersects 224 // Draw small black square on the plot where the time guide intersects
313 // it. 225 // it.
314 var val = yData[tDataIndex]; 226 var val = yData[tDataIndex];
315 var yPos, valStr; 227 var yPos, valStr;
316 if (typeof val === 'string') { 228 if (typeof val === 'string') {
317 yPos = yOrigin + Math.round(height / 2); 229 yPos = yOrigin + Math.round(height / 2);
318 valStr = val; 230 valStr = val;
319 } else { 231 } else {
320 yPos = yOrigin + height - 1 - 232 yPos = yOrigin + height - 1 -
321 Math.round((val - yMin) / (yMax - yMin) * (height - 1)); 233 Math.round((val - yMin) / (yMax - yMin) * (height - 1));
322 valStr = valueToString(val); 234 valStr = valueToString(val);
323 } 235 }
324 plotCtx.fillStyle = '#000'; 236 ctx.fillStyle = '#000';
325 plotCtx.fillRect(x - 2, yPos - 2, 4, 4); 237 ctx.fillRect(x - 2, yPos - 2, 4, 4);
326 238
327 // Draw the val to right of the intersection. 239 // Draw the val to right of the intersection.
328 var yLoc; 240 var yLoc;
329 if (yPos - textHeight / 2 < yOrigin) { 241 if (yPos - textHeight / 2 < yOrigin) {
330 yLoc = yOrigin + textHeight; 242 yLoc = yOrigin + textHeight;
331 } else if (yPos + textHeight / 2 >= yPos + height) { 243 } else if (yPos + textHeight / 2 >= yPos + height) {
332 yLoc = yOrigin + height - 1; 244 yLoc = yOrigin + height - 1;
333 } else { 245 } else {
334 yLoc = yPos + textHeight / 2; 246 yLoc = yPos + textHeight / 2;
335 } 247 }
336 drawHighlightText(plotCtx, valStr, x + 5, yLoc, plots[count].color); 248 drawHighlightText(valStr, x + 5, yLoc, plots[count].color);
337 } 249 }
338 } 250 }
339 251
340 function onMouseOverOrMove(event) { 252 function onMouseOverOrMove(event) {
341 drawPlots(); 253 drawPlots();
342 254
343 var boundingRect = plotCanvas.getBoundingClientRect(); 255 var boundingRect = canvas.getBoundingClientRect();
344 var x = Math.round(event.clientX - boundingRect.left); 256 var x = event.clientX - boundingRect.left;
345 var y = Math.round(event.clientY - boundingRect.top); 257 var y = event.clientY - boundingRect.top;
346 if (x < xOrigin || x >= xOrigin + width || 258 if (x < xOrigin || x >= xOrigin + width ||
347 y < yOrigin || y >= yOrigin + height) { 259 y < yOrigin || y >= yOrigin + height) {
348 return; 260 return;
349 } 261 }
350 262
351 if (width == size) { 263 if (width == size) {
352 drawTimeGuide(x - xOrigin); 264 drawTimeGuide(x - xOrigin);
353 } else { 265 } else {
354 drawTimeGuide(Math.round((x - xOrigin) / (width - 1) * (size - 1))); 266 drawTimeGuide(Math.round((x - xOrigin) / (width - 1) * (size - 1)));
355 } 267 }
356 } 268 }
357 269
358 function onMouseOut(event) { 270 function onMouseOut(event) {
359 drawPlots(); 271 drawPlots();
360 } 272 }
361 273
362 drawLegend();
363 drawPlots(); 274 drawPlots();
364 plotCanvas.addEventListener('mouseover', onMouseOverOrMove); 275 canvas.addEventListener('mouseover', onMouseOverOrMove);
365 plotCanvas.addEventListener('mousemove', onMouseOverOrMove); 276 canvas.addEventListener('mousemove', onMouseOverOrMove);
366 plotCanvas.addEventListener('mouseout', onMouseOut); 277 canvas.addEventListener('mouseout', onMouseOut);
367 } 278 }
368 279
369 var sleepSampleInterval = 30 * 1000; // in milliseconds. 280 var sleepSampleInterval = 30 * 1000; // in milliseconds.
370 var sleepText = loadTimeData.getString('systemSuspended'); 281 var sleepText = loadTimeData.getString('systemSuspended');
371 var invalidDataText = loadTimeData.getString('invalidData');
372 var offlineText = loadTimeData.getString('offlineText');
373
374 var plotColors = ['Red', 'Blue', 'Green', 'Gold', 'CadetBlue', 'LightCoral',
375 'LightSlateGray', 'Peru', 'DarkRed', 'LawnGreen', 'Tan'];
376
377 /**
378 * Add canvases for plotting to |plotsDiv|. For every header in |headerArray|,
379 * one canvas for the plot and one for its legend are added.
380 *
381 * @param {Array.<string>} headerArray Headers for the different plots to be
382 * added to |plotsDiv|.
383 * @param {HTMLDivElement} plotsDiv The div element into which the canvases
384 * are added.
385 * @return {<string>: {plotCanvas: <HTMLCanvasElement>,
386 * legendCanvas: <HTMLCanvasElement>} Returns an object
387 * with the headers as 'keys'. Each element is an object containing the
388 * legend canvas and the plot canvas that have been added to |plotsDiv|.
389 */
390 function addCanvases(headerArray, plotsDiv) {
391 // Remove the contents before adding new ones.
392 while (plotsDiv.firstChild != null) {
393 plotsDiv.removeChild(plotsDiv.firstChild);
394 }
395 var width = Math.floor(plotsDiv.getBoundingClientRect().width);
396 var canvases = {};
397 for (var i = 0; i < headerArray.length; i++) {
398 var header = document.createElement('h4');
399 header.textContent = headerArray[i];
400 plotsDiv.appendChild(header);
401
402 var legendCanvas = document.createElement('canvas');
403 legendCanvas.width = width;
404 legendCanvas.height = 5;
405 plotsDiv.appendChild(legendCanvas);
406
407 var plotCanvasDiv = document.createElement('div');
408 plotCanvasDiv.style.overflow = 'auto';
409 plotCanvasDiv.style.maxWidth = String(width) + 'px';
410 plotsDiv.appendChild(plotCanvasDiv);
411
412 plotCanvas = document.createElement('canvas');
413 plotCanvas.width = width;
414 plotCanvas.height = 200;
415 plotCanvasDiv.appendChild(plotCanvas);
416
417 canvases[headerArray[i]] = {plot: plotCanvas, legend: legendCanvas};
418 }
419 return canvases;
420 }
421 282
422 /** 283 /**
423 * Add samples in |sampleArray| to individual plots in |plots|. If the system 284 * Add samples in |sampleArray| to individual plots in |plots|. If the system
424 * resumed from a sleep/suspend, then "suspended" sleep samples are added to 285 * resumed from a sleep/suspend, then "suspended" sleep samples are added to
425 * the plot for the sleep duration. 286 * the plot for the sleep duration.
426 * 287 *
427 * @param {Array.<{data: Array.<number>, color: string}>} plots An 288 * @param {Array.<{data: Array.<number>, color: string}>} plots An
428 * array of plots to plot on the canvas. The field 'data' of a plot is an 289 * array of plots to plot on the canvas. The field 'data' of a plot is an
429 * array of samples to be plotted as a line graph with color speficied by 290 * array of samples to be plotted as a line graph with color speficied by
430 * the field 'color'. The elements in the 'data' array are ordered 291 * the field 'color'. The elements in the 'data' array are ordered
431 * corresponding to their sampling time in the argument 'tData'. Also, the 292 * corresponding to their sampling time in the argument 'tData'. Also, the
432 * number of elements in the 'data' array should be the same as in the time 293 * number of elements in the 'data' array should be the same as in the time
433 * array 'tData' below. 294 * array 'tData' below.
434 * @param {Array.<number>} tData The time (in seconds) in the past when the 295 * @param {Array.<number>} tData The time (in seconds) in the past when the
435 * corresponding data in plots was sampled. 296 * corresponding data in plots was sampled.
436 * @param {Array.<number>} sampleArray The array of samples wherein each 297 * @param {Array.<number>} sampleArray The array of samples wherein each
437 * element corresponds to the individual plot in |plots|. 298 * element corresponds to the individual plot in |plots|.
438 * @param {number} sampleTime Time in milliseconds since the epoch when the 299 * @param {number} sampleTime Time in milliseconds since the epoch when the
439 * samples in |sampleArray| were captured. 300 * samples in |sampleArray| were captured.
440 * @param {number} previousSampleTime Time in milliseconds since the epoch 301 * @param {number} previousSampleTime Time in milliseconds since the epoch
441 * when the sample prior to the current sample was captured. 302 * when the sample prior to the current sample was captured.
442 * @param {Array.<{time: number, sleepDuration: number}>} systemResumedArray An 303 * @param {Array.<{time: number, sleepDuration: number}>} systemResumedArray An
443 * array objects corresponding to system resume events. The 'time' field is 304 * array objects corresponding to system resume events. The 'time' field is
444 * for the time in milliseconds since the epoch when the system resumed. The 305 * for the time in milliseconds since the epoch when the system resumed. The
445 * 'sleepDuration' field is for the time in milliseconds the system spent 306 * 'sleepDuration' field is for the time in milliseconds the system spent
446 * in sleep/suspend state. 307 * in sleep/suspend state.
447 */ 308 */
448 function addTimeDataSample(plots, tData, absTime, sampleArray, 309 function addTimeDataSample(plots, tData, sampleArray,
449 sampleTime, previousSampleTime, 310 sampleTime, previousSampleTime,
450 systemResumedArray) { 311 systemResumedArray) {
451 for (var i = 0; i < plots.length; i++) { 312 for (var i = 0; i < plots.length; i++) {
452 if (plots[i].data.length != tData.length) { 313 if (plots[i].data.length != tData.length) {
453 throw new Error('Mismatch in time and plot data.'); 314 throw new Error('Mismatch in time and plot data.');
454 } 315 }
455 } 316 }
456 317
457 var time; 318 var time;
458 if (tData.length == 0) { 319 if (tData.length == 0) {
459 time = new Date(sampleTime); 320 time = new Date(sampleTime);
460 absTime[0] = sampleTime;
461 tData[0] = time.toLocaleTimeString(); 321 tData[0] = time.toLocaleTimeString();
462 for (var i = 0; i < plots.length; i++) { 322 for (var i = 0; i < plots.length; i++) {
463 plots[i].data[0] = sampleArray[i]; 323 plots[i].data[0] = sampleArray[i];
464 } 324 }
465 return; 325 return;
466 } 326 }
467 327
468 for (var i = 0; i < systemResumedArray.length; i++) { 328 for (var i = 0; i < systemResumedArray.length; i++) {
469 var resumeTime = systemResumedArray[i].time; 329 var resumeTime = systemResumedArray[i].time;
470 var sleepDuration = systemResumedArray[i].sleepDuration; 330 var sleepDuration = systemResumedArray[i].sleepDuration;
471 var sleepStartTime = resumeTime - sleepDuration; 331 var sleepStartTime = resumeTime - sleepDuration;
472 if (resumeTime < sampleTime) { 332 if (resumeTime < sampleTime && sleepStartTime > previousSampleTime) {
473 if (sleepStartTime < previousSampleTime) {
474 // This can happen if pending callbacks were handled before actually
475 // suspending.
476 sleepStartTime = previousSampleTime + 1000;
477 }
478 // Add sleep samples for every |sleepSampleInterval|. 333 // Add sleep samples for every |sleepSampleInterval|.
479 var sleepSampleTime = sleepStartTime; 334 var sleepSampleTime = sleepStartTime;
480 while (sleepSampleTime < resumeTime) { 335 while (sleepSampleTime < resumeTime) {
481 time = new Date(sleepSampleTime); 336 time = new Date(sleepSampleTime);
482 absTime.push(sleepSampleTime);
483 tData.push(time.toLocaleTimeString()); 337 tData.push(time.toLocaleTimeString());
484 for (var j = 0; j < plots.length; j++) { 338 for (var j = 0; j < plots.length; j++) {
485 plots[j].data.push(sleepText); 339 plots[j].data.push(sleepText);
486 } 340 }
487 sleepSampleTime += sleepSampleInterval; 341 sleepSampleTime += sleepSampleInterval;
488 } 342 }
489 } 343 }
490 } 344 }
491 345
492 time = new Date(sampleTime); 346 time = new Date(sampleTime);
493 absTime.push(sampleTime);
494 tData.push(time.toLocaleTimeString()); 347 tData.push(time.toLocaleTimeString());
495 for (var i = 0; i < plots.length; i++) { 348 for (var i = 0; i < plots.length; i++) {
496 plots[i].data.push(sampleArray[i]); 349 plots[i].data.push(sampleArray[i]);
497 } 350 }
498 } 351 }
499 352
500 /** 353 /**
501 * Display the battery charge vs time on a line graph. 354 * Display the battery charge vs time on a line graph.
502 * 355 *
503 * @param {Array.<{time: number, 356 * @param {Array.<{time: number,
504 * batteryPercent: number, 357 * batteryPercent: number,
505 * batteryDischargeRate: number, 358 * batteryDischargeRate: number,
506 * externalPower: number}>} powerSupplyArray An array of objects 359 * externalPower: number}>} powerSupplyArray An array of objects
507 * with fields representing the battery charge, time when the charge 360 * with fields representing the battery charge, time when the charge
508 * measurement was taken, and whether there was external power connected at 361 * measurement was taken, and whether there was external power connected at
509 * that time. 362 * that time.
510 * @param {Array.<{time: ?, sleepDuration: ?}>} systemResumedArray An array 363 * @param {Array.<{time: ?, sleepDuration: ?}>} systemResumedArray An array
511 * objects with fields 'time' and 'sleepDuration'. Each object corresponds 364 * objects with fields 'time' and 'sleepDuration'. Each object corresponds
512 * to a system resume event. The 'time' field is for the time in 365 * to a system resume event. The 'time' field is for the time in
513 * milliseconds since the epoch when the system resumed. The 'sleepDuration' 366 * milliseconds since the epoch when the system resumed. The 'sleepDuration'
514 * field is for the time in milliseconds the system spent in sleep/suspend 367 * field is for the time in milliseconds the system spent in sleep/suspend
515 * state. 368 * state.
516 */ 369 */
517 function showBatteryChargeData(powerSupplyArray, systemResumedArray) { 370 function showBatteryChargeData(powerSupplyArray, systemResumedArray) {
518 var chargeTimeData = []; 371 var chargeTimeData = [];
519 var chargeAbsTime = [];
520 var chargePlot = [ 372 var chargePlot = [
521 { 373 {
522 name: loadTimeData.getString('batteryChargePercentageHeader'),
523 color: '#0000FF', 374 color: '#0000FF',
524 data: [] 375 data: []
525 } 376 }
526 ]; 377 ];
527 var dischargeRateTimeData = []; 378 var dischargeRateTimeData = [];
528 var dischargeRateAbsTime = [];
529 var dischargeRatePlot = [ 379 var dischargeRatePlot = [
530 { 380 {
531 name: loadTimeData.getString('dischargeRateLegendText'),
532 color: '#FF0000', 381 color: '#FF0000',
533 data: [] 382 data: []
534 } 383 }
535 ]; 384 ];
536 var minDischargeRate = 1000; // A high unrealistic number to begin with. 385 var minDischargeRate = 1000; // A high unrealistic number to begin with.
537 var maxDischargeRate = -1000; // A low unrealistic number to begin with. 386 var maxDischargeRate = -1000; // A low unrealistic number to begin with.
538 for (var i = 0; i < powerSupplyArray.length; i++) { 387 for (var i = 0; i < powerSupplyArray.length; i++) {
539 var j = Math.max(i - 1, 0); 388 var j = Math.max(i - 1, 0);
540 389
541 addTimeDataSample(chargePlot, 390 addTimeDataSample(chargePlot, chargeTimeData,
542 chargeTimeData,
543 chargeAbsTime,
544 [powerSupplyArray[i].batteryPercent], 391 [powerSupplyArray[i].batteryPercent],
545 powerSupplyArray[i].time, 392 powerSupplyArray[i].time,
546 powerSupplyArray[j].time, 393 powerSupplyArray[j].time,
547 systemResumedArray); 394 systemResumedArray);
548 395
549 var dischargeRate = powerSupplyArray[i].batteryDischargeRate; 396 var dischargeRate = powerSupplyArray[i].batteryDischargeRate;
550 minDischargeRate = Math.min(dischargeRate, minDischargeRate); 397 minDischargeRate = Math.min(dischargeRate, minDischargeRate);
551 maxDischargeRate = Math.max(dischargeRate, maxDischargeRate); 398 maxDischargeRate = Math.max(dischargeRate, maxDischargeRate);
552 addTimeDataSample(dischargeRatePlot, 399 addTimeDataSample(dischargeRatePlot,
553 dischargeRateTimeData, 400 dischargeRateTimeData,
554 dischargeRateAbsTime,
555 [dischargeRate], 401 [dischargeRate],
556 powerSupplyArray[i].time, 402 powerSupplyArray[i].time,
557 powerSupplyArray[j].time, 403 powerSupplyArray[j].time,
558 systemResumedArray); 404 systemResumedArray);
559 } 405 }
560 if (minDischargeRate == maxDischargeRate) { 406 if (minDischargeRate == maxDischargeRate) {
561 // This means that all the samples had the same value. Hence, offset the 407 // This means that all the samples had the same value. Hence, offset the
562 // extremes by a bit so that the plot looks good. 408 // extremes by a bit so that the plot looks good.
563 minDischargeRate -= 1; 409 minDischargeRate -= 1;
564 maxDischargeRate += 1; 410 maxDischargeRate += 1;
565 } 411 }
566 412
567 plotsDiv = $('battery-charge-plots-div'); 413 var chargeCanvas = $('battery-charge-percentage-canvas');
568 414 var dischargeRateCanvas = $('battery-discharge-rate-canvas');
569 canvases = addCanvases( 415 plotLineGraph(chargeCanvas, chargeTimeData, chargePlot, 0.00, 100.00, 3);
570 [loadTimeData.getString('batteryChargePercentageHeader'), 416 plotLineGraph(dischargeRateCanvas,
571 loadTimeData.getString('batteryDischargeRateHeader')], 417 dischargeRateTimeData,
572 plotsDiv); 418 dischargeRatePlot,
573 419 minDischargeRate,
574 batteryChargeCanvases = canvases[ 420 maxDischargeRate,
575 loadTimeData.getString('batteryChargePercentageHeader')]; 421 3);
576 plotLineGraph(
577 batteryChargeCanvases['plot'],
578 batteryChargeCanvases['legend'],
579 chargeTimeData,
580 chargePlot,
581 0.00,
582 100.00,
583 3);
584
585 dischargeRateCanvases = canvases[
586 loadTimeData.getString('batteryDischargeRateHeader')];
587 plotLineGraph(
588 dischargeRateCanvases['plot'],
589 dischargeRateCanvases['legend'],
590 dischargeRateTimeData,
591 dischargeRatePlot,
592 minDischargeRate,
593 maxDischargeRate,
594 3);
595 }
596
597 /**
598 * Shows state occupancy data (CPU idle or CPU freq state occupancy) on a set of
599 * plots on the about:power UI.
600 *
601 * @param {Array.<{Array.<{
602 * time: number,
603 * cpuOnline:boolean,
604 * timeInState: {<string>: number}>}>} timeInStateData Array of arrays
605 * where each array corresponds to a CPU on the system. The elements of the
606 * individual arrays contain state occupancy samples.
607 * @param {Array.<{time: ?, sleepDuration: ?}>} systemResumedArray An array
608 * objects with fields 'time' and 'sleepDuration'. Each object corresponds
609 * to a system resume event. The 'time' field is for the time in
610 * milliseconds since the epoch when the system resumed. The 'sleepDuration'
611 * field is for the time in milliseconds the system spent in sleep/suspend
612 * state.
613 * @param {string} i18nHeaderString The header string to be displayed with each
614 * plot. For example, CPU idle data will have its own header format, and CPU
615 * freq data will have its header format.
616 * @param {string} unitString This is the string capturing the unit, if any,
617 * for the different states. Note that this is not the unit of the data
618 * being plotted.
619 * @param {HTMLDivElement} plotsDivId The div element in which the plots should
620 * be added.
621 */
622 function showStateOccupancyData(timeInStateData,
623 systemResumedArray,
624 i18nHeaderString,
625 unitString,
626 plotsDivId) {
627 var cpuPlots = [];
628 for (var cpu = 0; cpu < timeInStateData.length; cpu++) {
629 var cpuData = timeInStateData[cpu];
630 if (cpuData.length == 0) {
631 cpuPlots[cpu] = {plots: [], tData: []};
632 continue;
633 }
634 tData = [];
635 absTime = [];
636 // Each element of |plots| is an array of samples, one for each of the CPU
637 // states. The number of states is dicovered by looking at the first
638 // sample for which the CPU is online.
639 var plots = [];
640 var stateIndexMap = [];
641 var stateCount = 0;
642 for (var i = 0; i < cpuData.length; i++) {
643 if (cpuData[i].cpuOnline) {
644 for (var state in cpuData[i].timeInState) {
645 var stateName = state;
646 if (unitString != null) {
647 stateName += ' ' + unitString;
648 }
649 plots.push({
650 name: stateName,
651 data: [],
652 color: plotColors[stateCount]
653 });
654 stateIndexMap.push(state);
655 stateCount += 1;
656 }
657 break;
658 }
659 }
660 // If stateCount is 0, then it means the CPU has been offline
661 // throughout. Just add a single plot for such a case.
662 if (stateCount == 0) {
663 plots.push({
664 name: null,
665 data: [],
666 color: null
667 });
668 stateCount = 1; // Some invalid state!
669 }
670
671 // Pass the samples through the function addTimeDataSample to add 'sleep'
672 // samples.
673 for (var i = 0; i < cpuData.length; i++) {
674 var sample = cpuData[i];
675 var valArray = [];
676 for (var j = 0; j < stateCount; j++) {
677 if (sample.cpuOnline) {
678 valArray[j] = sample.timeInState[stateIndexMap[j]];
679 } else {
680 valArray[j] = offlineText;
681 }
682 }
683
684 var k = Math.max(i - 1, 0);
685 addTimeDataSample(plots,
686 tData,
687 absTime,
688 valArray,
689 sample.time,
690 cpuData[k].time,
691 systemResumedArray);
692 }
693
694 // Calculate the percentage occupancy of each state. A valid number is
695 // possible only if two consecutive samples are valid/numbers.
696 for (var k = 0; k < stateCount; k++) {
697 var stateData = plots[k].data;
698 // Skip the first sample as there is no previous sample.
699 for (var i = stateData.length - 1; i > 0; i--) {
700 if (typeof stateData[i] === 'number') {
701 if (typeof stateData[i - 1] === 'number') {
702 stateData[i] = (stateData[i] - stateData[i - 1]) /
703 (absTime[i] - absTime[i - 1]) * 100;
704 } else {
705 stateData[i] = invalidDataText;
706 }
707 }
708 }
709 }
710
711 // Remove the first sample from the time and data arrays.
712 tData.shift();
713 for (var k = 0; k < stateCount; k++) {
714 plots[k].data.shift();
715 }
716 cpuPlots[cpu] = {plots: plots, tData: tData};
717 }
718
719 headers = [];
720 for (var cpu = 0; cpu < timeInStateData.length; cpu++) {
721 headers[cpu] =
722 'CPU ' + cpu + ' ' + loadTimeData.getString(i18nHeaderString);
723 }
724
725 canvases = addCanvases(headers, $(plotsDivId));
726 for (var cpu = 0; cpu < timeInStateData.length; cpu++) {
727 cpuCanvases = canvases[headers[cpu]];
728 plotLineGraph(cpuCanvases['plot'],
729 cpuCanvases['legend'],
730 cpuPlots[cpu]['tData'],
731 cpuPlots[cpu]['plots'],
732 0,
733 100,
734 3);
735 }
736 }
737
738 function showCpuIdleData(idleStateData, systemResumedArray) {
739 showStateOccupancyData(idleStateData,
740 systemResumedArray,
741 'idleStateOccupancyPercentageHeader',
742 null,
743 'cpu-idle-plots-div');
744 }
745
746 function showCpuFreqData(freqStateData, systemResumedArray) {
747 showStateOccupancyData(freqStateData,
748 systemResumedArray,
749 'frequencyStateOccupancyPercentageHeader',
750 'MHz',
751 'cpu-freq-plots-div');
752 } 422 }
753 423
754 function requestBatteryChargeData() { 424 function requestBatteryChargeData() {
755 chrome.send('requestBatteryChargeData'); 425 chrome.send('requestBatteryChargeData');
756 } 426 }
757 427
758 function requestCpuIdleData() {
759 chrome.send('requestCpuIdleData');
760 }
761
762 function requestCpuFreqData() {
763 chrome.send('requestCpuFreqData');
764 }
765
766 /**
767 * Return a callback for the 'Show'/'Hide' buttons for each section of the
768 * about:power page.
769 *
770 * @param {string} sectionId The ID of the section which is to be shown or
771 * hidden.
772 * @param {string} buttonId The ID of the 'Show'/'Hide' button.
773 * @param {function} requestFunction The function which should be invoked on
774 * 'Show' to request for data from chrome.
775 * @return {function} The button callback function.
776 */
777 function showHideCallback(sectionId, buttonId, requestFunction) {
778 return function() {
779 if ($(sectionId).hidden) {
780 $(sectionId).hidden = false;
781 $(buttonId).textContent = loadTimeData.getString('hideButton');
782 requestFunction();
783 } else {
784 $(sectionId).hidden = true;
785 $(buttonId).textContent = loadTimeData.getString('showButton');
786 }
787 }
788 }
789
790 var powerUI = { 428 var powerUI = {
791 showBatteryChargeData: showBatteryChargeData, 429 showBatteryChargeData: showBatteryChargeData
792 showCpuIdleData: showCpuIdleData,
793 showCpuFreqData: showCpuFreqData
794 }; 430 };
795 431
796 document.addEventListener('DOMContentLoaded', function() { 432 document.addEventListener('DOMContentLoaded', function() {
797 $('battery-charge-section').hidden = true; 433 requestBatteryChargeData();
798 $('battery-charge-show-button').onclick = showHideCallback(
799 'battery-charge-section',
800 'battery-charge-show-button',
801 requestBatteryChargeData);
802 $('battery-charge-reload-button').onclick = requestBatteryChargeData; 434 $('battery-charge-reload-button').onclick = requestBatteryChargeData;
803
804 $('cpu-idle-section').hidden = true;
805 $('cpu-idle-show-button').onclick = showHideCallback(
806 'cpu-idle-section', 'cpu-idle-show-button', requestCpuIdleData);
807 $('cpu-idle-reload-button').onclick = requestCpuIdleData;
808
809 $('cpu-freq-section').hidden = true;
810 $('cpu-freq-show-button').onclick = showHideCallback(
811 'cpu-freq-section', 'cpu-freq-show-button', requestCpuFreqData);
812 $('cpu-freq-reload-button').onclick = requestCpuFreqData;
813 }); 435 });
OLDNEW
« no previous file with comments | « chrome/browser/resources/chromeos/power.html ('k') | chrome/browser/ui/webui/chromeos/power_ui.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698