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

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

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

Powered by Google App Engine
This is Rietveld 408576698