• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import {
17  BaseStruct,
18  ns2x,
19  Render,
20  RequestMessage,
21  isFrameContainPoint,
22  drawLoadingFrame,
23} from './ProcedureWorkerCommon';
24import { TraceRow } from '../../component/trace/base/TraceRow';
25
26export class EnergyPowerRender extends Render {
27  renderMainThread(
28    powerReq: { useCache: boolean; context: CanvasRenderingContext2D; type: string; appName: string },
29    row: TraceRow<EnergyPowerStruct>
30  ) {
31    let list = row.dataList;
32    let filter = row.dataListCache;
33    power(
34      list,
35      filter,
36      TraceRow.range!.startNS,
37      TraceRow.range!.endNS,
38      TraceRow.range!.totalNS,
39      row.frame,
40      powerReq.useCache || !TraceRow.range!.refresh,
41      powerReq.appName
42    );
43    drawLoadingFrame(powerReq.context, row.dataListCache, row);
44    powerReq.context.beginPath();
45    let find = false;
46    for (let i = 0; i < list.length; i++) {
47      let re = list[i];
48      EnergyPowerStruct.draw(powerReq, i, re, row);
49      if (row.isHover && re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) {
50        EnergyPowerStruct.hoverEnergyPowerStruct = re;
51        find = true;
52      }
53    }
54    if (!find && row.isHover) EnergyPowerStruct.hoverEnergyPowerStruct = undefined;
55    TraceRow.range!.refresh = true;
56    if (EnergyPowerStruct.maxPower != 0) {
57      let s = EnergyPowerStruct.maxPower + 'mAs';
58      let textMetrics = powerReq.context.measureText(s);
59      powerReq.context.globalAlpha = 1.0;
60      powerReq.context.fillStyle = '#f0f0f0';
61      powerReq.context.fillRect(0, 5, textMetrics.width + 8, 18);
62      powerReq.context.globalAlpha = 1;
63      powerReq.context.fillStyle = '#333';
64      powerReq.context.textBaseline = 'middle';
65      powerReq.context.fillText(s, 4, 5 + 9);
66    }
67    powerReq.context.closePath();
68    let spApplication = document.getElementsByTagName('sp-application')[0];
69    let isDark = spApplication.hasAttribute('dark');
70    drawLegend(powerReq, isDark);
71  }
72}
73
74export function drawLegend(req: any, isDark?: boolean) {
75  let textList = ['CPU', 'LOCATION', 'GPU', 'DISPLAY', 'CAMERA', 'BLUETOOTH', 'FLASHLIGHT', 'AUDIO', 'WIFISCAN'];
76  for (let index = 0; index < textList.length; index++) {
77    let text = req.context.measureText(textList[index]);
78    req.context.fillStyle = EnergyPowerStruct.getHistogramColor(textList[index]);
79    req.context.globalAlpha = 1;
80    let canvasEndX = req.context.canvas.clientWidth - EnergyPowerStruct.OFFSET_WIDTH;
81    let textColor = isDark ? '#FFFFFF' : '#333';
82    if (index == 0) {
83      req!.context.fillRect(canvasEndX - EnergyPowerStruct.powerItemNumber * 80, 12, 8, 8);
84      req.context.globalAlpha = 1;
85      req.context.fillStyle = textColor;
86      req.context.textBaseline = 'middle';
87      req.context.fillText(textList[index], canvasEndX - EnergyPowerStruct.powerItemNumber * 80 + 10, 18);
88      EnergyPowerStruct.currentTextWidth = canvasEndX - EnergyPowerStruct.powerItemNumber * 80 + 40 + text.width;
89    } else {
90      req!.context.fillRect(EnergyPowerStruct.currentTextWidth, 12, 8, 8);
91      req.context.globalAlpha = 1;
92      req.context.fillStyle = textColor;
93      req.context.textBaseline = 'middle';
94      req!.context.fillText(textList[index], EnergyPowerStruct.currentTextWidth + 12, 18);
95      EnergyPowerStruct.currentTextWidth = EnergyPowerStruct.currentTextWidth + 40 + text.width;
96    }
97  }
98  req.context.fillStyle = '#333';
99}
100
101export function power(
102  list: Array<any>,
103  res: Array<any>,
104  startNS: number,
105  endNS: number,
106  totalNS: number,
107  frame: any,
108  use: boolean,
109  appName: string
110): void {
111  EnergyPowerStruct.maxPower = 0;
112  list.length = 0;
113  let firstData = [];
114  if (use && res.length > 0) {
115    for (let index = 0; index < res.length; index++) {
116      let item = res[index];
117      let obj = item[appName];
118      if (obj != undefined && obj.ts + 1000000000 > (startNS || 0) && (obj.ts || 0) < (endNS || 0)) {
119        firstData.push(obj);
120      }
121    }
122    let array = firstData.sort((a, b) => a.ts - b.ts);
123    setFirstDataArray(array, list);
124    computeMaxPower(array, list, startNS, endNS, totalNS, frame);
125  }
126}
127
128function setFirstDataArray(array: any[], list: Array<any>): void {
129  array.forEach((item) => {
130    if (
131      list.length > 0 &&
132      item.ts + 500000000 >= list[list.length - 1].ts &&
133      item.ts - 500000000 <= list[list.length - 1].ts
134    ) {
135      list[list.length - 1].cpu = item.cpu === 0 ? list[list.length - 1].cpu : item.cpu;
136      list[list.length - 1].location = item.location === 0 ? list[list.length - 1].location : item.location;
137      list[list.length - 1].gpu = item.gpu === 0 ? list[list.length - 1].gpu : item.gpu;
138      list[list.length - 1].display = item.display === 0 ? list[list.length - 1].display : item.display;
139      list[list.length - 1].camera = item.camera === 0 ? list[list.length - 1].camera : item.camera;
140      list[list.length - 1].bluetooth = item.bluetooth === 0 ? list[list.length - 1].bluetooth : item.bluetooth;
141      list[list.length - 1].flashlight = item.flashlight === 0 ? list[list.length - 1].flashlight : item.flashlight;
142      list[list.length - 1].audio = item.audio === 0 ? list[list.length - 1].audio : item.audio;
143      list[list.length - 1].wifiscan = item.wifiscan === 0 ? list[list.length - 1].wifiscan : item.wifiscan;
144    } else {
145      list.push(item);
146    }
147  });
148}
149
150function computeMaxPower(
151  array: Array<any>,
152  list: Array<any>,
153  startNS: number,
154  endNS: number,
155  totalNS: number,
156  frame: any
157): void {
158  array.forEach((item) => {
159    if (list.indexOf(item) >= 0) {
160      EnergyPowerStruct.setPowerFrame(item, 5, startNS || 0, endNS || 0, totalNS || 0, frame);
161      let max =
162        (item.cpu || 0) +
163        (item.location || 0) +
164        (item.gpu || 0) +
165        (item.display || 0) +
166        (item.camera || 0) +
167        (item.bluetooth || 0) +
168        (item.flashlight || 0) +
169        (item.audio || 0) +
170        (item.wifiscan || 0);
171      if (max > EnergyPowerStruct.maxPower) {
172        EnergyPowerStruct.maxPower = max;
173      }
174    }
175  });
176}
177
178export class EnergyPowerStruct extends BaseStruct {
179  static maxPower: number = 0;
180  static maxPowerName: string = '0';
181  static powerItemNumber: number = 9;
182  static currentTextWidth: number = 0;
183  static rowHeight: number = 200;
184  static appName: string | undefined;
185  static hoverEnergyPowerStruct: EnergyPowerStruct | undefined;
186  static selectEnergyPowerStruct: EnergyPowerStruct | undefined;
187  static OFFSET_WIDTH: number = 266;
188  name: string | undefined;
189  appKey: string | undefined;
190  eventValue: string | undefined;
191  eventName: string | undefined;
192  id: number | undefined;
193  ts: number = 0;
194  cpu: number = 0;
195  location: number = 0;
196  gpu: number = 0;
197  display: number = 0;
198  camera: number = 0;
199  bluetooth: number = 0;
200  flashlight: number = 0;
201  audio: number = 0;
202  wifiscan: number = 0;
203
204  static draw(req: any, index: number, data: EnergyPowerStruct, row: TraceRow<EnergyPowerStruct>) {
205    if (data.frame) {
206      req!.context.globalAlpha = 1.0;
207      req!.context.lineWidth = 1;
208      this.currentTextWidth = 0;
209      let cpuHeight = this.drawHistogram(req, data, -1, data.cpu!, 'CPU', row.frame);
210      let locationHeight = this.drawHistogram(req, data, cpuHeight, data.location!, 'LOCATION', row.frame);
211      let gpuHeight = this.drawHistogram(req, data, cpuHeight - locationHeight, data.gpu!, 'GPU', row.frame);
212      let dHight = cpuHeight - locationHeight - gpuHeight;
213      let displayHeight = this.drawHistogram(req, data, dHight, data.display!, 'DISPLAY', row.frame);
214      let cHight = cpuHeight - locationHeight - gpuHeight - displayHeight;
215      let cameraHeight = this.drawHistogram(req, data, cHight, data.camera!, 'CAMERA', row.frame);
216      let bHeight = cpuHeight - locationHeight - gpuHeight - displayHeight - cameraHeight;
217      let bluetoothHeight = this.drawHistogram(req, data, bHeight, data.bluetooth!, 'BLUETOOTH', row.frame);
218      let fHeight = cpuHeight - locationHeight - gpuHeight - displayHeight - cameraHeight - bluetoothHeight;
219      let flashlightHeight = this.drawHistogram(req, data, fHeight, data.flashlight!, 'FLASHLIGHT', row.frame);
220      let aHeight =
221        cpuHeight - locationHeight - gpuHeight - displayHeight - cameraHeight - bluetoothHeight - flashlightHeight;
222      let audioHeight = this.drawHistogram(req, data, aHeight, data.audio!, 'AUDIO', row.frame);
223      let wHeight =
224        cpuHeight -
225        locationHeight -
226        gpuHeight -
227        displayHeight -
228        cameraHeight -
229        bluetoothHeight -
230        flashlightHeight -
231        audioHeight;
232      let wifiHeight = this.drawHistogram(req, data, wHeight, data.wifiscan!, 'WIFISCAN', row.frame);
233      let maxPointY = this.drawPolyline(req, index, data, row.frame, wifiHeight);
234      let startNS = TraceRow.range!.startNS;
235      let endNS = TraceRow.range!.endNS;
236      let totalNS = TraceRow.range!.totalNS;
237      if (data.ts === EnergyPowerStruct.hoverEnergyPowerStruct?.ts) {
238        let endPointX = ns2x((data.ts || 0) + 500000000, startNS, endNS, totalNS, row.frame);
239        let startPointX = ns2x((data.ts || 0) - 500000000, startNS, endNS, totalNS, row.frame);
240        let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX;
241        req.context.globalAlpha = 1;
242        req!.context.lineWidth = 2;
243        req.context.fillStyle = '#333';
244        req!.context.strokeRect(startPointX, maxPointY, frameWidth, req.context.canvas.width - maxPointY);
245      }
246    }
247    req!.context.globalAlpha = 1.0;
248    req!.context.lineWidth = 1;
249  }
250
251  static drawHistogram(
252    req: RequestMessage,
253    data: EnergyPowerStruct,
254    height: number,
255    itemValue: number,
256    textItem: string,
257    rowFrame: any
258  ): number {
259    let endPointX = ns2x(
260      (data.ts || 0) + 500000000,
261      TraceRow.range!.startNS,
262      TraceRow.range!.endNS,
263      TraceRow.range!.totalNS,
264      rowFrame
265    );
266    let startPointX = ns2x(
267      (data.ts || 0) - 500000000,
268      TraceRow.range!.startNS,
269      TraceRow.range!.endNS,
270      TraceRow.range!.totalNS,
271      rowFrame
272    );
273    let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX;
274    let histogramColor = this.getHistogramColor(textItem);
275    req!.context.fillStyle = histogramColor;
276    req!.context.strokeStyle = histogramColor;
277    let dataHeight: number = Math.floor(((itemValue || 0) * (this.rowHeight - 40)) / EnergyPowerStruct.maxPower);
278    if (itemValue != 0 && dataHeight < 15) {
279      dataHeight = 15;
280    }
281    let drawStartY = 0;
282
283    if (height == -1) {
284      drawStartY = data.frame!.y + this.rowHeight - dataHeight + 4;
285      req!.context.fillRect(startPointX, drawStartY, frameWidth, dataHeight);
286      return drawStartY;
287    } else {
288      drawStartY = height - dataHeight;
289      req!.context.fillRect(startPointX, drawStartY, frameWidth, dataHeight);
290      if (textItem == 'WIFISCAN') {
291        return drawStartY;
292      }
293      return dataHeight;
294    }
295  }
296
297  static drawPolyline(req: RequestMessage, index: number, data: EnergyPowerStruct, rowFrame: any, totalHeight: number) {
298    let pointX = ns2x(data.ts || 0, TraceRow.range!.startNS, TraceRow.range!.endNS, TraceRow.range!.totalNS, rowFrame);
299    let maxHeight =
300      (data.cpu || 0) +
301      (data.location || 0) +
302      (data.gpu || 0) +
303      (data.display || 0) +
304      (data.camera || 0) +
305      (data.bluetooth || 0) +
306      (data.flashlight || 0) +
307      (data.audio || 0) +
308      (data.wifiscan || 0);
309    let drawHeight: number = Math.floor(((maxHeight || 0) * (this.rowHeight - 40)) / EnergyPowerStruct.maxPower);
310    let drawY = data.frame!.y + this.rowHeight - drawHeight + 5;
311    req!.context.fillStyle = '#ED6F21';
312    req!.context.strokeStyle = '#ED6F21';
313
314    if (index == 0) {
315      req.context.beginPath();
316      req.context.arc(pointX, totalHeight, 4, 0, 2 * Math.PI);
317      req.context.fill();
318      req.context.moveTo(pointX, totalHeight);
319    } else {
320      req.context.lineTo(pointX, totalHeight);
321      req.context.stroke();
322      req.context.beginPath();
323      req.context.arc(pointX, totalHeight, 4, 0, 2 * Math.PI);
324      req.context.fill();
325    }
326    return totalHeight;
327  }
328
329  static setPowerFrame(powerNode: any, padding: number, startNS: number, endNS: number, totalNS: number, frame: any) {
330    let startPointX: number;
331    let endPointX: number;
332    if ((powerNode.ts || 0) < startNS) {
333      startPointX = 0;
334    } else {
335      startPointX = ns2x((powerNode.ts || 0) - 500000000, startNS, endNS, totalNS, frame);
336    }
337    if (powerNode.ts + 500000000 > endNS) {
338      endPointX = frame.width;
339    } else {
340      endPointX = ns2x(powerNode.ts + 500000000, startNS, endNS, totalNS, frame);
341    }
342    let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX;
343    if (!powerNode.frame) {
344      powerNode.frame = {};
345    }
346    powerNode.frame.x = Math.floor(startPointX);
347    powerNode.frame.y = frame.y + padding;
348    powerNode.frame.width = Math.ceil(frameWidth);
349    powerNode.frame.height = Math.floor(frame.height - padding * 2);
350  }
351
352  static getHistogramColor(textItem: string): string {
353    switch (textItem) {
354      case 'CPU':
355        return '#92D6CC';
356      case 'LOCATION':
357        return '#61CFBE';
358      case 'GPU':
359        return '#86C5E3';
360      case 'DISPLAY':
361        return '#46B1E3';
362      case 'CAMERA':
363        return '#C386F0';
364      case 'BLUETOOTH':
365        return '#8981F7';
366      case 'AUDIO':
367        return '#AC49F5';
368      case 'WIFISCAN':
369        return '#92C4BD';
370      default:
371        return '#564AF7';
372    }
373  }
374}
375