• 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 { SpSystemTrace } from '../SpSystemTrace';
17import { TraceRow } from '../trace/base/TraceRow';
18import { renders } from '../../database/ui-worker/ProcedureWorker';
19import { info } from '../../../log/Log';
20import { XpowerRender, XpowerStruct } from '../../database/ui-worker/ProcedureWorkerXpower';
21import { XpowerStatisticRender, XpowerStatisticStruct } from '../../database/ui-worker/ProcedureWorkerXpowerStatistic';
22import { XpowerAppDetailRender, XpowerAppDetailStruct } from '../../database/ui-worker/ProcedureWorkerXpowerAppDetail';
23import { XpowerWifiRender, XpowerWifiStruct } from '../../database/ui-worker/ProcedureWorkerXpowerWifi';
24import { ColorUtils } from '../trace/base/ColorUtils';
25import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU';
26import { xpowerDataSender } from '../../database/data-trafic/xpower/XpowerDataSender';
27import { xpowerStatisticDataSender } from '../../database/data-trafic/xpower/XpowerStatisticDataSender';
28import { xpowerWifiDataSender } from '../../database/data-trafic/xpower/XpowerWifiDataSender';
29import { xpowerAppDetailDataSender } from '../../database/data-trafic/xpower/XpowerAppDetailDataSender';
30import {
31  XpowerThreadCountRender,
32  XpowerThreadCountStruct,
33} from '../../database/ui-worker/ProcedureWorkerXpowerThreadCount';
34import {
35  XpowerThreadInfoRender,
36  XpowerThreadInfoStruct,
37} from '../../database/ui-worker/ProcedureWorkerXpowerThreadInfo';
38import {
39  xpowerThreadCountDataSender,
40  xpowerThreadInfoDataSender,
41} from '../../database/data-trafic/xpower/XpowerThreadSender';
42import {
43  xpowerGpuFreqDataSender,
44  xpowerGpuFreqCountDataSender,
45} from '../../database/data-trafic/xpower/XpowerGpuFrequencySender';
46import { queryTraceConfig, queryXpowerData, queryXpowerMeasureData, queryFreq } from '../../database/sql/Xpower.sql';
47import { BaseStruct } from '../../bean/BaseStruct';
48import {
49  XpowerGpuFreqCountRender,
50  XpowerGpuFreqCountStruct,
51} from '../../database/ui-worker/ProcedureWorkerXpowerGpuFreqCount';
52import { XpowerGpuFreqStruct, XpowerGpuFreqRender } from '../../database/ui-worker/ProcedureWorkerXpowerGpuFreq';
53export const THREAD_ENERGY = 'thread_energy';
54export const THREAD_LOAD = 'thread_loads';
55const ROW_HEIGHT = 200;
56
57export class SpXpowerChart {
58  private readonly trace: SpSystemTrace;
59  private rowFolder!: TraceRow<BaseStruct>;
60  private systemFolder!: TraceRow<BaseStruct>;
61  private bundleNameFolder!: TraceRow<BaseStruct>;
62  private threadInfoStructMap = new Map<number, XpowerThreadInfoStruct[]>();
63  private threadEnergyStructMap = new Map<number, XpowerThreadInfoStruct[]>();
64  private threadLoadStructMap = new Map<number, XpowerThreadInfoStruct[]>();
65  private gpuFreqStructMap = new Map<number, XpowerGpuFreqStruct[]>();
66  private valueTypeList: string[] = [THREAD_ENERGY, THREAD_LOAD];
67
68  constructor(trace: SpSystemTrace) {
69    this.trace = trace;
70  }
71
72  async init(traceId?: string): Promise<void> {
73    let XpowerMeasureData = await queryXpowerMeasureData(traceId);
74    if (XpowerMeasureData.length <= 0) {
75      return;
76    }
77    let xpowerList = await queryXpowerData(traceId);
78    if (xpowerList.length <= 0) {
79      return;
80    }
81    await this.initXpowerFolder(traceId);
82    let systemGroupRowType = TraceRow.ROW_TYPE_XPOWER_SYSTEM_GROUP;
83    await this.initFolder('system', systemGroupRowType, 'System', traceId);
84    await this.initSystemData(this.systemFolder, xpowerList, traceId);
85    let traceConfig = await queryTraceConfig(traceId);
86    let bundleNameRowType = TraceRow.ROW_TYPE_XPOWER_BUNDLE_NAME_GROUP;
87    for (let i = 0; i < traceConfig.length; i++) {
88      if (traceConfig[i].traceSource === 'xpower_config') {
89        await this.initFolder('bundleName', bundleNameRowType, traceConfig[i].value, traceId);
90        await this.initXpowerStatisticData(this.bundleNameFolder, traceId);
91        await this.initXpowerWifiData(this.bundleNameFolder, traceId);
92        await this.initXpowerAppDetatilDisplayData(this.bundleNameFolder, traceId);
93        await this.initGpuFreqCountData(this.bundleNameFolder, traceId);
94        await this.initGpuFreqData(this.bundleNameFolder, traceId);
95        await this.initThreadCountData(this.bundleNameFolder, traceId);
96        for (let value of this.valueTypeList) {
97          await this.initThreadInfoData(this.bundleNameFolder, value, traceId);
98        }
99        break;
100      }
101    }
102  }
103
104  initXpowerFolder = async (traceId?: string): Promise<void> => {
105    let xpowerFolder = TraceRow.skeleton(traceId);
106    xpowerFolder.rowId = 'Xpowers';
107    xpowerFolder.index = 0;
108    xpowerFolder.rowType = TraceRow.ROW_TYPE_XPOWER;
109    xpowerFolder.rowParentId = '';
110    xpowerFolder.style.height = '40px';
111    xpowerFolder.folder = true;
112    xpowerFolder.name = 'Xpower';
113    xpowerFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler;
114    xpowerFolder.selectChangeHandler = this.trace.selectChangeHandler;
115    xpowerFolder.supplier = (): Promise<BaseStruct[]> => new Promise<Array<BaseStruct>>((resolve) => resolve([]));
116    xpowerFolder.onThreadHandler = (useCache): void => {
117      xpowerFolder.canvasSave(this.trace.canvasPanelCtx!);
118      if (xpowerFolder.expansion) {
119        this.trace.canvasPanelCtx?.clearRect(0, 0, xpowerFolder.frame.width, xpowerFolder.frame.height);
120      } else {
121        (renders.empty as EmptyRender).renderMainThread(
122          {
123            context: this.trace.canvasPanelCtx,
124            useCache: useCache,
125            type: '',
126          },
127          xpowerFolder
128        );
129      }
130      xpowerFolder.canvasRestore(this.trace.canvasPanelCtx!, this.trace);
131    };
132    this.rowFolder = xpowerFolder;
133    this.trace.rowsEL?.appendChild(xpowerFolder);
134  };
135
136  private initFolder = async (rowId: string, rowType: string, name: string, traceId?: string): Promise<void> => {
137    let folder = TraceRow.skeleton(traceId);
138    folder.rowId = rowId;
139    folder.rowParentId = 'Xpowers';
140    folder.rowHidden = !this.rowFolder.expansion;
141    folder.rowType = rowType;
142    folder.folder = true;
143    folder.name = name;
144    folder.folderPaddingLeft = 20;
145    folder.style.height = '40px';
146    folder.favoriteChangeHandler = this.trace.favoriteChangeHandler;
147    folder.selectChangeHandler = this.trace.selectChangeHandler;
148    folder.supplier = (): Promise<BaseStruct[]> => new Promise<Array<BaseStruct>>((resolve) => resolve([]));
149    folder.onThreadHandler = (useCache): void => {
150      folder.canvasSave(this.trace.canvasPanelCtx!);
151      if (folder.expansion) {
152        this.trace.canvasPanelCtx?.clearRect(0, 0, folder.frame.width, folder.frame.height);
153      } else {
154        (renders.empty as EmptyRender).renderMainThread(
155          {
156            context: this.trace.canvasPanelCtx,
157            useCache: useCache,
158            type: '',
159          },
160          folder
161        );
162      }
163      folder.canvasRestore(this.trace.canvasPanelCtx!, this.trace);
164    };
165    if (rowType === TraceRow.ROW_TYPE_XPOWER_SYSTEM_GROUP) {
166      this.systemFolder = folder;
167    } else if (rowType === TraceRow.ROW_TYPE_XPOWER_BUNDLE_NAME_GROUP) {
168      this.bundleNameFolder = folder;
169    }
170    this.rowFolder?.addChildTraceRow(folder);
171  };
172
173  private xpowerSupplierFrame(
174    traceRow: TraceRow<XpowerStruct>,
175    it: {
176      name: string;
177      num: number;
178      maxValue?: number;
179      minValue?: number;
180    }
181  ): void {
182    traceRow.supplierFrame = (): Promise<XpowerStruct[]> => {
183      let promiseData = null;
184      // @ts-ignore
185      promiseData = xpowerDataSender(it.name, traceRow);
186      if (promiseData === null) {
187        // @ts-ignore
188        return new Promise<Array<unknown>>((resolve) => resolve([]));
189      } else {
190        // @ts-ignore
191        return promiseData.then((resultXpower: Array<unknown>) => {
192          for (let j = 0; j < resultXpower.length; j++) {
193            if (j > 0) {
194              // @ts-ignore
195              resultXpower[j].delta = (resultXpower[j].value || 0) - (resultXpower[j - 1].value || 0);
196            } else {
197              // @ts-ignore
198              resultXpower[j].delta = 0;
199            }
200          }
201          return resultXpower;
202        });
203      }
204    };
205  }
206
207  private xpowerThreadHandler(
208    traceRow: TraceRow<XpowerStruct>,
209    it: {
210      name: string;
211      num: number;
212      maxValue?: number;
213      minValue?: number;
214    },
215    xpowerId: number
216  ): void {
217    traceRow.onThreadHandler = (useCache): void => {
218      let context: CanvasRenderingContext2D;
219      if (traceRow.currentContext) {
220        context = traceRow.currentContext;
221      } else {
222        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
223      }
224      traceRow.canvasSave(context);
225      (renders.xpower as XpowerRender).renderMainThread(
226        {
227          context: context,
228          useCache: useCache,
229          type: it.name,
230          maxValue: it.maxValue === 0 ? 1 : it.maxValue!,
231          minValue: it.minValue || 0,
232          index: xpowerId,
233          maxName: it.maxValue!.toString(),
234        },
235        traceRow
236      );
237      traceRow.canvasRestore(context, this.trace);
238    };
239  }
240
241  async initSystemData(
242    folder: TraceRow<BaseStruct>,
243    xpowerList: Array<{
244      name: string;
245      num: number;
246      maxValue: number;
247      minValue: number;
248    }>,
249    traceId?: string
250  ): Promise<void> {
251    info('xpowerList data size is: ', xpowerList!.length);
252    XpowerStruct.maxValue = xpowerList.map((item) => item.num).reduce((a, b) => Math.max(a, b));
253    for (let i = 0; i < xpowerList.length; i++) {
254      const it = xpowerList[i];
255      let traceRow = TraceRow.skeleton<XpowerStruct>(traceId);
256      traceRow.rowId = it.name;
257      traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_SYSTEM;
258      traceRow.rowParentId = folder.rowId;
259      traceRow.style.height = '40px';
260      traceRow.name = it.name;
261      traceRow.rowHidden = !folder.expansion;
262      traceRow.folderTextLeft = 40;
263      if (it.name === 'ThermalReport.ShellTemp') {
264        it.maxValue = it.maxValue / 1000;
265      }
266      traceRow.xpowerRowTitle = convertTitle(it.name);
267      traceRow.setAttribute('children', '');
268      traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
269      traceRow.selectChangeHandler = this.trace.selectChangeHandler;
270      this.xpowerSupplierFrame(traceRow, it);
271      traceRow.getCacheData = (args: unknown): Promise<XpowerStruct[]> | undefined => {
272        let result: Promise<XpowerStruct[]> | undefined;
273        result = xpowerDataSender(it.name, traceRow, args);
274        return result;
275      };
276      traceRow.focusHandler = (ev): void => {
277        this.trace?.displayTip(
278          traceRow,
279          XpowerStruct.hoverXpowerStruct,
280          `<span>${
281            it.name === 'ThermalReport.ShellTemp'
282              ? XpowerStruct.hoverXpowerStruct?.value!
283              : it.name === 'ThermalReport.ThermalLevel'
284              ? convertHoverValue(String(XpowerStruct.hoverXpowerStruct?.value!))
285              : ColorUtils.formatNumberComma(XpowerStruct.hoverXpowerStruct?.value!)
286          }</span>`
287        );
288      };
289      traceRow.findHoverStruct = (): void => {
290        XpowerStruct.hoverXpowerStruct = traceRow.getHoverStruct();
291      };
292      this.xpowerThreadHandler(traceRow, it, i);
293      folder.addChildTraceRow(traceRow);
294    }
295  }
296
297  async initThreadCountData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> {
298    let traceRow = TraceRow.skeleton<XpowerThreadCountStruct>(traceId);
299    traceRow.rowId = 'ThreadCount';
300    traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_THREAD_COUNT;
301    traceRow.rowParentId = folder.rowId;
302    traceRow.style.height = '40px';
303    traceRow.name = 'Thread Count';
304    traceRow.rowHidden = !folder.expansion;
305    traceRow.folderTextLeft = 40;
306    traceRow.xpowerRowTitle = convertTitle(traceRow.name);
307    traceRow.setAttribute('children', '');
308    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
309    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
310    this.xpowerThreadCountSupplierFrame(traceRow);
311    traceRow.getCacheData = (args: unknown): Promise<XpowerThreadCountStruct[]> | undefined => {
312      let result: Promise<XpowerThreadCountStruct[]> | undefined;
313      result = xpowerThreadCountDataSender(traceRow, args);
314      return result;
315    };
316    traceRow.focusHandler = (ev): void => {
317      this.trace?.displayTip(
318        traceRow,
319        XpowerThreadCountStruct.hoverXpowerStruct,
320        `<span>Count:${XpowerThreadCountStruct.hoverXpowerStruct?.value}</span>`
321      );
322    };
323    traceRow.findHoverStruct = (): void => {
324      XpowerThreadCountStruct.hoverXpowerStruct = traceRow.getHoverStruct();
325    };
326    traceRow.onThreadHandler = (useCache): void => {
327      let context: CanvasRenderingContext2D;
328      if (traceRow.currentContext) {
329        context = traceRow.currentContext;
330      } else {
331        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
332      }
333      traceRow.canvasSave(context);
334      (renders.xpowerThreadCount as XpowerThreadCountRender).renderMainThread(
335        {
336          context: context,
337          useCache: useCache,
338        },
339        traceRow
340      );
341      traceRow.canvasRestore(context, this.trace);
342    };
343    folder.addChildTraceRow(traceRow);
344  }
345
346  private xpowerThreadCountSupplierFrame(traceRow: TraceRow<XpowerThreadCountStruct>): void {
347    traceRow.supplierFrame = (): Promise<XpowerThreadCountStruct[]> => {
348      return xpowerThreadCountDataSender(traceRow).then((resultXpower: Array<XpowerThreadCountStruct>) => {
349        return resultXpower;
350      });
351    };
352  }
353
354  async initGpuFreqCountData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> {
355    let traceRow = TraceRow.skeleton<XpowerGpuFreqCountStruct>(traceId);
356    traceRow.rowId = 'GpuFreqCount';
357    traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_GPU_COUNT;
358    traceRow.rowParentId = folder.rowId;
359    traceRow.style.height = '40px';
360    traceRow.name = 'Gpu Freq Count';
361    traceRow.rowHidden = !folder.expansion;
362    traceRow.folderTextLeft = 40;
363    traceRow.xpowerRowTitle = convertTitle(traceRow.name);
364    traceRow.setAttribute('children', '');
365    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
366    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
367    this.xpowerGpuFreqCountSupplierFrame(traceRow);
368    traceRow.getCacheData = (args: unknown): Promise<XpowerGpuFreqCountStruct[]> | undefined => {
369      let result: Promise<XpowerGpuFreqCountStruct[]> | undefined;
370      result = xpowerGpuFreqCountDataSender(traceRow, args);
371      return result;
372    };
373    traceRow.focusHandler = (ev): void => {
374      this.trace?.displayTip(
375        traceRow,
376        XpowerGpuFreqCountStruct.hoverXpowerStruct,
377        `<span>Count:${XpowerGpuFreqCountStruct.hoverXpowerStruct?.value}</span>`
378      );
379    };
380    traceRow.findHoverStruct = (): void => {
381      XpowerGpuFreqCountStruct.hoverXpowerStruct = traceRow.getHoverStruct();
382    };
383    traceRow.onThreadHandler = (useCache): void => {
384      let context: CanvasRenderingContext2D;
385      if (traceRow.currentContext) {
386        context = traceRow.currentContext;
387      } else {
388        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
389      }
390      traceRow.canvasSave(context);
391      (renders.xpowerGpuFreqCount as XpowerGpuFreqCountRender).renderMainThread(
392        {
393          context: context,
394          useCache: useCache,
395        },
396        traceRow
397      );
398      traceRow.canvasRestore(context, this.trace);
399    };
400    folder.addChildTraceRow(traceRow);
401  }
402
403  private xpowerGpuFreqCountSupplierFrame(traceRow: TraceRow<XpowerGpuFreqCountStruct>): void {
404    traceRow.supplierFrame = (): Promise<XpowerGpuFreqCountStruct[]> => {
405      return xpowerGpuFreqCountDataSender(traceRow).then((resultXpower: Array<XpowerGpuFreqCountStruct>) => {
406        return resultXpower;
407      });
408    };
409  }
410
411  async initThreadInfoData(folder: TraceRow<BaseStruct>, valueType: string, traceId?: string): Promise<void> {
412    let value = '';
413    let rowId = '';
414    let rowName = '';
415    if (valueType === THREAD_ENERGY) {
416      value = 'Energy';
417      rowId = 'thread_energy';
418      rowName = 'Thread Energy';
419    } else if (valueType === THREAD_LOAD) {
420      value = 'Load';
421      rowId = 'thread_loads';
422      rowName = 'Thread Load';
423    }
424    let traceRow = TraceRow.skeleton<XpowerThreadInfoStruct>(traceId);
425    traceRow.rowId = rowId;
426    traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_THREAD_INFO;
427    traceRow.rowParentId = folder.rowId;
428    traceRow.style.height = `${ROW_HEIGHT}px`;
429    traceRow.name = rowName;
430    traceRow.rowHidden = !folder.expansion;
431    traceRow.folderTextLeft = 40;
432    traceRow.xpowerRowTitle = convertTitle(traceRow.name);
433    traceRow.setAttribute('children', '');
434    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
435    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
436    this.xpowerThreadInfoSupplierFrame(traceRow, valueType);
437    traceRow.getCacheData = (args: unknown): Promise<XpowerThreadInfoStruct[]> | undefined => {
438      let result: Promise<XpowerThreadInfoStruct[]>;
439      result = xpowerThreadInfoDataSender(valueType, traceRow, args);
440      return result.then((resultXpower: Array<XpowerThreadInfoStruct>) => {
441        for (let index = 0; index < resultXpower.length; index++) {
442          let item = resultXpower[index];
443          item.threadName = SpSystemTrace.DATA_DICT.get(resultXpower[index].threadNameId!) || '';
444          item.valueType = valueType;
445        }
446        return resultXpower;
447      });
448    };
449    traceRow.focusHandler = (ev): void => {
450      this.setTips(traceRow, value);
451    };
452    traceRow.findHoverStruct = (): void => {
453      XpowerThreadInfoStruct.hoverXpowerStruct = traceRow.getHoverStruct();
454    };
455    this.xpowerThreadInfoThreadHandler(traceRow, valueType);
456    folder.addChildTraceRow(traceRow);
457  }
458
459  private xpowerThreadInfoSupplierFrame(traceRow: TraceRow<XpowerThreadInfoStruct>, valueType: string): void {
460    traceRow.supplierFrame = (): Promise<XpowerThreadInfoStruct[]> => {
461      let promiseData = xpowerThreadInfoDataSender(valueType, traceRow);
462      if (promiseData === null) {
463        return new Promise<Array<XpowerThreadInfoStruct>>((resolve) => resolve([]));
464      } else {
465        return promiseData.then((resultXpower: Array<XpowerThreadInfoStruct>) => {
466          for (let index = 0; index < resultXpower.length; index++) {
467            let item = resultXpower[index];
468            item.threadName = SpSystemTrace.DATA_DICT.get(resultXpower[index].threadNameId!) || '';
469            item.valueType = valueType;
470          }
471          return this.getThreadInfoDrawData(resultXpower, valueType);
472        });
473      }
474    };
475  }
476
477  private setDataMap(resultXpower: Array<XpowerThreadInfoStruct>): Map<number, Array<XpowerThreadInfoStruct>> {
478    let threadInfoStructMap = new Map();
479    resultXpower.forEach((item: XpowerThreadInfoStruct) => {
480      const startNS = item.startNS;
481      if (threadInfoStructMap.has(startNS)) {
482        const data = threadInfoStructMap.get(startNS)!;
483        data.push(item);
484      } else {
485        const data: XpowerThreadInfoStruct[] = [];
486        data.push(item);
487        threadInfoStructMap.set(startNS, data);
488      }
489    });
490    return threadInfoStructMap;
491  }
492
493  private getThreadInfoDrawData(
494    resultXpower: Array<XpowerThreadInfoStruct>,
495    valueType: string
496  ): XpowerThreadInfoStruct[] {
497    let newArr: XpowerThreadInfoStruct[] = [];
498    let sumOfRemainingValues = 0;
499    let sumOfRemainingTimes = 0;
500    let maxValue = 0;
501    let itemArraySum = 0;
502    this.threadInfoStructMap = this.setDataMap(resultXpower);
503    for (let itemArray of this.threadInfoStructMap.values()) {
504      itemArray.sort((a, b) => {
505        return b.value - a.value;
506      });
507      if (itemArray.length > 10) {
508        newArr = JSON.parse(JSON.stringify(itemArray.slice(0, 10)));
509        sumOfRemainingValues = itemArray.slice(9).reduce((acc, obj) => acc + obj.value, 0);
510        sumOfRemainingTimes = itemArray.slice(9).reduce((acc, obj) => acc + obj.threadTime, 0);
511        newArr[9].value = sumOfRemainingValues;
512        newArr[9].threadTime = sumOfRemainingTimes;
513        newArr[9].threadName = 'other';
514      } else {
515        newArr = JSON.parse(JSON.stringify(itemArray));
516      }
517      itemArraySum = newArr.reduce((acc, obj) => acc + obj.value, 0);
518      if (itemArraySum > maxValue) {
519        maxValue = itemArraySum;
520      }
521      this.threadInfoStructMap.set(itemArray[0].startNS, newArr);
522    }
523    if (valueType === THREAD_ENERGY) {
524      XpowerThreadInfoStruct.energyMaxValue = maxValue;
525      this.threadEnergyStructMap = this.threadInfoStructMap;
526      XpowerThreadInfoStruct.threadEnergyStructMap = this.setDataMap(resultXpower);
527    } else if (valueType === THREAD_LOAD) {
528      XpowerThreadInfoStruct.loadMaxValue = maxValue;
529      this.threadLoadStructMap = this.threadInfoStructMap;
530      XpowerThreadInfoStruct.threadLoadStructMap = this.setDataMap(resultXpower);
531    }
532    let resultXpowerLit = Array.from(this.threadInfoStructMap.values()).reduce(
533      (acc, valueArray) => acc.concat(valueArray),
534      []
535    );
536    //@ts-ignore
537    return resultXpowerLit;
538  }
539
540  private setTips(traceRow: TraceRow<XpowerThreadInfoStruct>, value: string): void {
541    let tipsHtml = '';
542    if (XpowerThreadInfoStruct.hoverXpowerStruct) {
543      let hoverData: XpowerThreadInfoStruct[] = [];
544      let unit = '';
545      if (XpowerThreadInfoStruct.hoverXpowerStruct.valueType === THREAD_ENERGY) {
546        hoverData = this.threadEnergyStructMap!.get(XpowerThreadInfoStruct.hoverXpowerStruct.startNS) || [];
547        unit = 'mAh';
548      } else if (XpowerThreadInfoStruct.hoverXpowerStruct.valueType === THREAD_LOAD) {
549        hoverData = this.threadLoadStructMap!.get(XpowerThreadInfoStruct.hoverXpowerStruct.startNS) || [];
550        unit = '%';
551      }
552      hoverData = [...hoverData].reverse();
553      for (let i = 0; i < hoverData.length; i++) {
554        if (hoverData[i].value > 0) {
555          tipsHtml += `<div style=" display: flex; flex-wrap: nowrap; justify-content: space-between;">
556                <div style=" line-height: 20px; flex-grow: 2; flex-shrink: 1; flex-basis: auto;">Name: ${
557                  hoverData[i].threadName! || ''
558                }</div>
559                <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;">&nbsp;&nbsp;Duration: ${
560                  hoverData[i].threadTime! || 0
561                }&nbsp;ms</div>
562                <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;">&nbsp;&nbsp;${value}: ${
563            hoverData[i].value! || 0
564          }&nbsp;${unit}</div>
565            </div>`;
566        }
567      }
568    }
569    this.trace?.displayTip(traceRow, XpowerThreadInfoStruct.hoverXpowerStruct, `${tipsHtml}`);
570  }
571
572  private xpowerThreadInfoThreadHandler(traceRow: TraceRow<XpowerThreadInfoStruct>, valueType: string): void {
573    traceRow.onThreadHandler = (useCache): void => {
574      let context: CanvasRenderingContext2D;
575      if (traceRow.currentContext) {
576        context = traceRow.currentContext;
577      } else {
578        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
579      }
580      traceRow.canvasSave(context);
581      (renders.xpowerThreadInfo as XpowerThreadInfoRender).renderMainThread(
582        {
583          context: context,
584          useCache: useCache,
585          type: valueType,
586        },
587        traceRow
588      );
589      traceRow.canvasRestore(context, this.trace);
590    };
591  }
592
593  private xpowerGpuFreqSupplierFrame(traceRow: TraceRow<XpowerGpuFreqStruct>): void {
594    traceRow.supplierFrame = (): Promise<XpowerGpuFreqStruct[]> => {
595      let promiseData = xpowerGpuFreqDataSender(traceRow);
596      if (promiseData === null) {
597        return new Promise<Array<XpowerGpuFreqStruct>>((resolve) => resolve([]));
598      } else {
599        return promiseData.then((resultXpower: Array<XpowerGpuFreqStruct>) => {
600          return this.getGpuFreqDrawData(resultXpower);
601        });
602      }
603    };
604  }
605
606  private setGpuFreqDataMap(resultXpower: Array<XpowerGpuFreqStruct>): Map<number, Array<XpowerGpuFreqStruct>> {
607    let gpuFreqStructMap = new Map();
608    resultXpower.forEach((item: XpowerGpuFreqStruct, index: number) => {
609      const startNS = item.startNS;
610      if (gpuFreqStructMap.has(startNS)) {
611        const data = gpuFreqStructMap.get(startNS)!;
612        data.push(item);
613      } else {
614        const data = Array<XpowerGpuFreqStruct>();
615        data.push(item);
616        gpuFreqStructMap.set(startNS, data);
617      }
618      let hoverHtml = '';
619      if (item.runTime > 0) {
620        hoverHtml = `<div style=" display: flex; flex-wrap: nowrap; justify-content: space-between;">
621            <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;">frequency: ${
622              item.frequency! || 0
623            }</div>
624            <div style=" line-height: 20px; flex-grow: 2; flex-shrink: 1; flex-basis: auto;">&nbsp;&nbsp;runTime: ${
625              item.runTime! || 0
626            }&nbsp;ms</div>
627            <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;">&nbsp;&nbsp;idleTime: ${
628              item.idleTime! || 0
629            }&nbsp;ms</div>
630        </div>`;
631      }
632      item.hoverHtml = hoverHtml;
633    });
634    return gpuFreqStructMap;
635  }
636
637  private sumTime(arr: Array<XpowerGpuFreqStruct>): Array<XpowerGpuFreqStruct> {
638    return arr.reduce((accumulator: Array<XpowerGpuFreqStruct>, current) => {
639      const { frequency, runTime, idleTime } = current;
640      const existingEntry = accumulator.find((entry) => entry.frequency === frequency);
641      if (existingEntry) {
642        existingEntry.runTime += runTime;
643        existingEntry.idleTime += idleTime;
644      } else {
645        accumulator.push({ ...current });
646      }
647      return accumulator;
648    }, []);
649  }
650
651  private getGpuFreqDrawData(resultXpower: Array<XpowerGpuFreqStruct>): XpowerGpuFreqStruct[] {
652    this.gpuFreqStructMap = new Map();
653    this.gpuFreqStructMap = this.setGpuFreqDataMap(resultXpower);
654    for (let itemArray of this.gpuFreqStructMap.values()) {
655      itemArray = this.sumTime(itemArray);
656      itemArray.sort((a, b) => {
657        return b.runTime - a.runTime;
658      });
659      this.gpuFreqStructMap.set(itemArray[0].startNS, itemArray);
660    }
661    XpowerGpuFreqStruct.gpuFreqStructMap = this.gpuFreqStructMap;
662    let resultXpowerLit = Array.from(this.gpuFreqStructMap.values()).reduce(
663      (acc, valueArray) => acc.concat(valueArray),
664      []
665    );
666    return resultXpowerLit;
667  }
668
669  private xpowerStatisticThreadHandler(traceRow: TraceRow<XpowerStatisticStruct>): void {
670    traceRow.onThreadHandler = (useCache): void => {
671      let context: CanvasRenderingContext2D;
672      if (traceRow.currentContext) {
673        context = traceRow.currentContext;
674      } else {
675        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
676      }
677      traceRow.canvasSave(context);
678      (renders.xpowerStatistic as XpowerStatisticRender).renderMainThread(
679        {
680          context: context,
681          useCache: useCache,
682        },
683        traceRow
684      );
685      traceRow.canvasRestore(context, this.trace);
686    };
687  }
688
689  async initXpowerStatisticData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> {
690    let traceRow = TraceRow.skeleton<XpowerStatisticStruct>(traceId);
691    traceRow.rowId = 'Statistic';
692    traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_STATISTIC;
693    traceRow.rowParentId = folder.rowId;
694    traceRow.style.height = '200px';
695    traceRow.setAttribute('height', '200px');
696    let element = traceRow.shadowRoot?.querySelector('.root') as HTMLDivElement;
697    element!.style.height = '200px';
698    traceRow.name = 'Statistic';
699    traceRow.rowHidden = !folder.expansion;
700    traceRow.folderTextLeft = 40;
701    traceRow.xpowerRowTitle = convertTitle(traceRow.name);
702    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
703    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
704    traceRow.setAttribute('children', '');
705
706    let list = [
707      'audio',
708      'bluetooth',
709      'camera',
710      'cpu',
711      'display',
712      'flashlight',
713      'gpu',
714      'location',
715      'wifiscan',
716      'wifi',
717      'modem',
718    ];
719    traceRow.rowSettingCheckBoxList = list;
720    traceRow.addRowSettingCheckBox();
721    traceRow.rowSetting = 'enable';
722    traceRow.rowSettingPopoverDirection = 'bottomLeft';
723    traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => {
724      this.trace.refreshCanvas(false);
725    };
726    traceRow.supplierFrame = (): Promise<XpowerStatisticStruct[]> =>
727      xpowerStatisticDataSender(traceRow).then((res): XpowerStatisticStruct[] => {
728        res.forEach((item) => {
729          item.typeStr = SpSystemTrace.DATA_DICT.get(item.type)!;
730        });
731        return res;
732      });
733    traceRow.getCacheData = (args: unknown): Promise<XpowerStatisticStruct[]> | undefined => {
734      let result: Promise<XpowerStatisticStruct[]> | undefined;
735      result = xpowerStatisticDataSender(traceRow, args);
736      return result;
737    };
738    traceRow.focusHandler = (ev): void => {
739      this.trace?.displayTip(
740        traceRow,
741        XpowerStatisticStruct.hoverXpowerStruct,
742        XpowerStatisticStruct.hoverXpowerStruct?.hoverHtml!
743      );
744    };
745    this.xpowerStatisticThreadHandler(traceRow);
746    folder.addChildTraceRow(traceRow);
747  }
748
749  private xpowerAppDetailThreadHandler(traceRow: TraceRow<XpowerAppDetailStruct>): void {
750    traceRow.onThreadHandler = (useCache): void => {
751      let context: CanvasRenderingContext2D;
752      if (traceRow.currentContext) {
753        context = traceRow.currentContext;
754      } else {
755        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
756      }
757      traceRow.canvasSave(context);
758      (renders.xpowerAppDetail as XpowerAppDetailRender).renderMainThread(
759        {
760          context: context,
761          useCache: useCache,
762        },
763        traceRow
764      );
765      traceRow.canvasRestore(context, this.trace);
766    };
767  }
768
769  async initXpowerAppDetatilDisplayData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> {
770    let traceRow = TraceRow.skeleton<XpowerAppDetailStruct>(traceId);
771    traceRow.rowId = 'AppDetailDisplay';
772    traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_APP_DETAIL_DISPLAY;
773    traceRow.rowParentId = folder.rowId;
774    traceRow.style.height = '200px';
775    traceRow.setAttribute('height', '200px');
776    let element = traceRow.shadowRoot?.querySelector('.root') as HTMLDivElement;
777    element!.style.height = '200px';
778    traceRow.name = 'Display';
779    traceRow.rowHidden = !folder.expansion;
780    traceRow.folderTextLeft = 40;
781    traceRow.xpowerRowTitle = convertTitle(traceRow.name);
782    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
783    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
784    traceRow.setAttribute('children', '');
785
786    let list = ['180hz', '120hz', '90hz', '60hz', '45hz', '30hz', '24hz', '15hz', '10hz', '5hz', '1hz'];
787    traceRow.rowSettingCheckBoxList = list;
788    traceRow.addRowSettingCheckBox();
789    traceRow.rowSetting = 'enable';
790    traceRow.rowSettingPopoverDirection = 'bottomLeft';
791    traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => {
792      this.trace.refreshCanvas(false);
793    };
794
795    traceRow.supplierFrame = (): Promise<XpowerAppDetailStruct[]> =>
796      xpowerAppDetailDataSender(traceRow).then((res): XpowerAppDetailStruct[] => {
797        return res;
798      });
799    traceRow.getCacheData = (args: unknown): Promise<XpowerAppDetailStruct[]> | undefined => {
800      let result: Promise<XpowerAppDetailStruct[]> | undefined;
801      result = xpowerAppDetailDataSender(traceRow, args);
802      return result;
803    };
804    traceRow.focusHandler = (ev): void => {
805      this.trace?.displayTip(
806        traceRow,
807        XpowerAppDetailStruct.hoverXpowerStruct,
808        XpowerAppDetailStruct.hoverXpowerStruct?.hoverHtml!
809      );
810    };
811    this.xpowerAppDetailThreadHandler(traceRow);
812    folder.addChildTraceRow(traceRow);
813  }
814
815  private xpowerWifiThreadHandler(traceRow: TraceRow<XpowerWifiStruct>, name: string): void {
816    traceRow.onThreadHandler = (useCache): void => {
817      let context: CanvasRenderingContext2D;
818      if (traceRow.currentContext) {
819        context = traceRow.currentContext;
820      } else {
821        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
822      }
823      traceRow.canvasSave(context);
824      (renders.xpowerWifi as XpowerWifiRender).renderMainThread(
825        {
826          context: context,
827          useCache: useCache,
828          name: name,
829        },
830        traceRow
831      );
832      traceRow.canvasRestore(context, this.trace);
833    };
834  }
835
836  async initXpowerWifiData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> {
837    let name = ['WIFIPackets', 'WIFIBytes'];
838    for (let it of name) {
839      let traceRow = TraceRow.skeleton<XpowerWifiStruct>(traceId);
840      traceRow.rowId = it;
841      traceRow.rowType = it === name[0] ? TraceRow.ROW_TYPE_XPOWER_WIFI_PACKETS : TraceRow.ROW_TYPE_XPOWER_WIFI_BYTES;
842      traceRow.rowParentId = folder.rowId;
843      traceRow.style.height = '100px';
844      traceRow.setAttribute('height', '100px');
845      let element = traceRow.shadowRoot?.querySelector('.root') as HTMLDivElement;
846      element!.style.height = '100px';
847      traceRow.name = it;
848      traceRow.rowHidden = !folder.expansion;
849      traceRow.folderTextLeft = 40;
850      traceRow.xpowerRowTitle = convertTitle(traceRow.name);
851      traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
852      traceRow.selectChangeHandler = this.trace.selectChangeHandler;
853      traceRow.setAttribute('children', '');
854      traceRow.rowSettingCheckBoxList = ['tx', 'rx'];
855      traceRow.addRowSettingCheckBox();
856      traceRow.rowSetting = 'enable';
857      traceRow.rowSettingPopoverDirection = 'bottomLeft';
858      traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => {
859        this.trace.refreshCanvas(false);
860      };
861      traceRow.supplierFrame = (): Promise<XpowerWifiStruct[]> =>
862        xpowerWifiDataSender(traceRow, it).then((res): XpowerWifiStruct[] => {
863          return res;
864        });
865      traceRow.getCacheData = (args: unknown): Promise<XpowerWifiStruct[]> | undefined => {
866        let result: Promise<XpowerWifiStruct[]> | undefined;
867        result = xpowerWifiDataSender(traceRow, it, args);
868        return result;
869      };
870      traceRow.focusHandler = (ev): void => {
871        it === 'WIFIPackets'
872          ? this.trace?.displayTip(
873              traceRow,
874              XpowerWifiStruct.hoverPacketsStruct,
875              XpowerWifiStruct.hoverPacketsStruct?.hoverHtmlPackets!
876            )
877          : this.trace?.displayTip(
878              traceRow,
879              XpowerWifiStruct.hoverBytesStruct,
880              XpowerWifiStruct.hoverBytesStruct?.hoverHtmlBytes!
881            );
882      };
883      this.xpowerWifiThreadHandler(traceRow, it);
884      folder.addChildTraceRow(traceRow);
885    }
886  }
887
888  private xpowerGpuFreqThreadHandler(traceRow: TraceRow<XpowerGpuFreqStruct>): void {
889    traceRow.onThreadHandler = (useCache): void => {
890      let context: CanvasRenderingContext2D;
891      if (traceRow.currentContext) {
892        context = traceRow.currentContext;
893      } else {
894        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
895      }
896      traceRow.canvasSave(context);
897      (renders.xpowerGpuFreq as XpowerGpuFreqRender).renderMainThread(
898        {
899          context: context,
900          useCache: useCache,
901        },
902        traceRow
903      );
904      traceRow.canvasRestore(context, this.trace);
905    };
906  }
907
908  async initGpuFreqData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> {
909    let traceRow = TraceRow.skeleton<XpowerGpuFreqStruct>(traceId);
910    traceRow.rowId = 'gpu-frequency';
911    traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_GPU_FREQUENCY;
912    traceRow.rowParentId = folder.rowId;
913    traceRow.style.height = `${ROW_HEIGHT}px`;
914    traceRow.name = 'GPU Freq';
915    traceRow.rowHidden = !folder.expansion;
916    traceRow.folderTextLeft = 40;
917    traceRow.xpowerRowTitle = convertTitle(traceRow.name);
918    traceRow.setAttribute('children', '');
919    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
920    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
921    let freqList = await queryFreq();
922    // @ts-ignore
923    const values = freqList.map((item) => item.frequency.toString());
924    let freqSet = new Set(values);
925    traceRow.rowSettingCheckBoxList = [...freqSet];
926    traceRow.addRowSettingCheckBox();
927    traceRow.rowSetting = 'enable';
928    traceRow.rowSettingPopoverDirection = 'bottomLeft';
929    traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => {
930      this.trace.refreshCanvas(false);
931    };
932    this.xpowerGpuFreqSupplierFrame(traceRow);
933    traceRow.getCacheData = (args: unknown): Promise<XpowerGpuFreqStruct[]> | undefined => {
934      let result: Promise<XpowerGpuFreqStruct[]> | undefined;
935      result = xpowerGpuFreqDataSender(traceRow, args);
936      return result;
937    };
938    traceRow.focusHandler = (ev): void => {
939      let html =
940        (XpowerGpuFreqStruct.hoverXpowerStruct &&
941          XpowerGpuFreqStruct.hoverMap.get(XpowerGpuFreqStruct.hoverXpowerStruct.startNS)) ||
942        '';
943      this.trace?.displayTip(traceRow, XpowerGpuFreqStruct.hoverXpowerStruct, html);
944    };
945    this.xpowerGpuFreqThreadHandler(traceRow);
946    folder.addChildTraceRow(traceRow);
947  }
948}
949
950// 鼠标悬浮时转换xpower泳道名
951export function convertTitle(title: string): string {
952  switch (title) {
953    case 'Battery.Capacity':
954      return '电池容量(单位mAh)';
955    case 'Battery.Charge':
956      return '充电状态(充电1,非充电0)';
957    case 'Battery.GasGauge':
958      return '电池剩余电量(单位mAh)';
959    case 'Battery.Level':
960      return '电池百分比';
961    case 'Battery.RealCurrent':
962      return '实时电流(单位mAh,充电时为正数,耗电时为负数)';
963    case 'Battery.Screen':
964      return '屏幕状态(亮屏1,灭屏0)';
965    case 'ThermalReport.ShellTemp':
966      return '外壳温度(单位℃)';
967    case 'ThermalReport.ThermalLevel':
968      return '温度等级';
969    case 'Statistic':
970        return '器件统计数据';
971    case 'WIFIPackets':
972        return 'wifi传输数据包个数';
973    case 'WIFIBytes':
974        return 'wifi字节数';
975    case 'Display':
976        return '屏幕刷新率';
977    case 'Gpu Freq Count':
978        return 'GPU频率计数';
979    case 'GPU Freq':
980        return 'GPU频率时间';
981    case 'Thread Count':
982        return '时间线程数';
983    case 'Thread Energy':
984        return '线程能耗';
985    case 'Thread Load':
986        return '线程负载';
987    default:
988      return title;
989  }
990}
991
992// 鼠标悬浮ThermalReport.ThermalLevel泳道时转换悬浮框内容
993export function convertHoverValue(value: string): string {
994  switch (value) {
995    case '0':
996      return 'COOL';
997    case '1':
998      return 'WARM';
999    case '2':
1000      return 'HOT';
1001    case '3':
1002      return 'OVERHEATED';
1003    default:
1004      return value;
1005  }
1006}
1007