• 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.setAttribute('children', '');
307    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
308    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
309    this.xpowerThreadCountSupplierFrame(traceRow);
310    traceRow.getCacheData = (args: unknown): Promise<XpowerThreadCountStruct[]> | undefined => {
311      let result: Promise<XpowerThreadCountStruct[]> | undefined;
312      result = xpowerThreadCountDataSender(traceRow, args);
313      return result;
314    };
315    traceRow.focusHandler = (ev): void => {
316      this.trace?.displayTip(
317        traceRow,
318        XpowerThreadCountStruct.hoverXpowerStruct,
319        `<span>Count:${XpowerThreadCountStruct.hoverXpowerStruct?.value}</span>`
320      );
321    };
322    traceRow.findHoverStruct = (): void => {
323      XpowerThreadCountStruct.hoverXpowerStruct = traceRow.getHoverStruct();
324    };
325    traceRow.onThreadHandler = (useCache): void => {
326      let context: CanvasRenderingContext2D;
327      if (traceRow.currentContext) {
328        context = traceRow.currentContext;
329      } else {
330        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
331      }
332      traceRow.canvasSave(context);
333      (renders.xpowerThreadCount as XpowerThreadCountRender).renderMainThread(
334        {
335          context: context,
336          useCache: useCache,
337        },
338        traceRow
339      );
340      traceRow.canvasRestore(context, this.trace);
341    };
342    folder.addChildTraceRow(traceRow);
343  }
344
345  private xpowerThreadCountSupplierFrame(traceRow: TraceRow<XpowerThreadCountStruct>): void {
346    traceRow.supplierFrame = (): Promise<XpowerThreadCountStruct[]> => {
347      return xpowerThreadCountDataSender(traceRow).then((resultXpower: Array<XpowerThreadCountStruct>) => {
348        return resultXpower;
349      });
350    };
351  }
352
353  async initGpuFreqCountData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> {
354    let traceRow = TraceRow.skeleton<XpowerGpuFreqCountStruct>(traceId);
355    traceRow.rowId = 'GpuFreqCount';
356    traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_GPU_COUNT;
357    traceRow.rowParentId = folder.rowId;
358    traceRow.style.height = '40px';
359    traceRow.name = 'Gpu Freq Count';
360    traceRow.rowHidden = !folder.expansion;
361    traceRow.folderTextLeft = 40;
362    traceRow.setAttribute('children', '');
363    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
364    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
365    this.xpowerGpuFreqCountSupplierFrame(traceRow);
366    traceRow.getCacheData = (args: unknown): Promise<XpowerGpuFreqCountStruct[]> | undefined => {
367      let result: Promise<XpowerGpuFreqCountStruct[]> | undefined;
368      result = xpowerGpuFreqCountDataSender(traceRow, args);
369      return result;
370    };
371    traceRow.focusHandler = (ev): void => {
372      this.trace?.displayTip(
373        traceRow,
374        XpowerGpuFreqCountStruct.hoverXpowerStruct,
375        `<span>Count:${XpowerGpuFreqCountStruct.hoverXpowerStruct?.value}</span>`
376      );
377    };
378    traceRow.findHoverStruct = (): void => {
379      XpowerGpuFreqCountStruct.hoverXpowerStruct = traceRow.getHoverStruct();
380    };
381    traceRow.onThreadHandler = (useCache): void => {
382      let context: CanvasRenderingContext2D;
383      if (traceRow.currentContext) {
384        context = traceRow.currentContext;
385      } else {
386        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
387      }
388      traceRow.canvasSave(context);
389      (renders.xpowerGpuFreqCount as XpowerGpuFreqCountRender).renderMainThread(
390        {
391          context: context,
392          useCache: useCache,
393        },
394        traceRow
395      );
396      traceRow.canvasRestore(context, this.trace);
397    };
398    folder.addChildTraceRow(traceRow);
399  }
400
401  private xpowerGpuFreqCountSupplierFrame(traceRow: TraceRow<XpowerGpuFreqCountStruct>): void {
402    traceRow.supplierFrame = (): Promise<XpowerGpuFreqCountStruct[]> => {
403      return xpowerGpuFreqCountDataSender(traceRow).then((resultXpower: Array<XpowerGpuFreqCountStruct>) => {
404        return resultXpower;
405      });
406    };
407  }
408
409  async initThreadInfoData(folder: TraceRow<BaseStruct>, valueType: string, traceId?: string): Promise<void> {
410    let value = '';
411    let rowId = '';
412    let rowName = '';
413    if (valueType === THREAD_ENERGY) {
414      value = 'Energy';
415      rowId = 'thread_energy';
416      rowName = 'Thread Energy';
417    } else if (valueType === THREAD_LOAD) {
418      value = 'Load';
419      rowId = 'thread_loads';
420      rowName = 'Thread Load';
421    }
422    let traceRow = TraceRow.skeleton<XpowerThreadInfoStruct>(traceId);
423    traceRow.rowId = rowId;
424    traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_THREAD_INFO;
425    traceRow.rowParentId = folder.rowId;
426    traceRow.style.height = `${ROW_HEIGHT}px`;
427    traceRow.name = rowName;
428    traceRow.rowHidden = !folder.expansion;
429    traceRow.folderTextLeft = 40;
430    traceRow.setAttribute('children', '');
431    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
432    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
433    this.xpowerThreadInfoSupplierFrame(traceRow, valueType);
434    traceRow.getCacheData = (args: unknown): Promise<XpowerThreadInfoStruct[]> | undefined => {
435      let result: Promise<XpowerThreadInfoStruct[]>;
436      result = xpowerThreadInfoDataSender(valueType, traceRow, args);
437      return result.then((resultXpower: Array<XpowerThreadInfoStruct>) => {
438        for (let index = 0; index < resultXpower.length; index++) {
439          let item = resultXpower[index];
440          item.threadName = SpSystemTrace.DATA_DICT.get(resultXpower[index].threadNameId!) || '';
441          item.valueType = valueType;
442        }
443        return resultXpower;
444      });
445    };
446    traceRow.focusHandler = (ev): void => {
447      this.setTips(traceRow, value);
448    };
449    traceRow.findHoverStruct = (): void => {
450      XpowerThreadInfoStruct.hoverXpowerStruct = traceRow.getHoverStruct();
451    };
452    this.xpowerThreadInfoThreadHandler(traceRow, valueType);
453    folder.addChildTraceRow(traceRow);
454  }
455
456  private xpowerThreadInfoSupplierFrame(traceRow: TraceRow<XpowerThreadInfoStruct>, valueType: string): void {
457    traceRow.supplierFrame = (): Promise<XpowerThreadInfoStruct[]> => {
458      let promiseData = xpowerThreadInfoDataSender(valueType, traceRow);
459      if (promiseData === null) {
460        return new Promise<Array<XpowerThreadInfoStruct>>((resolve) => resolve([]));
461      } else {
462        return promiseData.then((resultXpower: Array<XpowerThreadInfoStruct>) => {
463          for (let index = 0; index < resultXpower.length; index++) {
464            let item = resultXpower[index];
465            item.threadName = SpSystemTrace.DATA_DICT.get(resultXpower[index].threadNameId!) || '';
466            item.valueType = valueType;
467          }
468          return this.getThreadInfoDrawData(resultXpower, valueType);
469        });
470      }
471    };
472  }
473
474  private setDataMap(resultXpower: Array<XpowerThreadInfoStruct>): Map<number, Array<XpowerThreadInfoStruct>> {
475    let threadInfoStructMap = new Map();
476    resultXpower.forEach((item: XpowerThreadInfoStruct) => {
477      const startNS = item.startNS;
478      if (threadInfoStructMap.has(startNS)) {
479        const data = threadInfoStructMap.get(startNS)!;
480        data.push(item);
481      } else {
482        const data: XpowerThreadInfoStruct[] = [];
483        data.push(item);
484        threadInfoStructMap.set(startNS, data);
485      }
486    });
487    return threadInfoStructMap;
488  }
489
490  private getThreadInfoDrawData(
491    resultXpower: Array<XpowerThreadInfoStruct>,
492    valueType: string
493  ): XpowerThreadInfoStruct[] {
494    let newArr: XpowerThreadInfoStruct[] = [];
495    let sumOfRemainingValues = 0;
496    let sumOfRemainingTimes = 0;
497    let maxValue = 0;
498    let itemArraySum = 0;
499    this.threadInfoStructMap = this.setDataMap(resultXpower);
500    for (let itemArray of this.threadInfoStructMap.values()) {
501      itemArray.sort((a, b) => {
502        return b.value - a.value;
503      });
504      if (itemArray.length > 10) {
505        newArr = JSON.parse(JSON.stringify(itemArray.slice(0, 10)));
506        sumOfRemainingValues = itemArray.slice(9).reduce((acc, obj) => acc + obj.value, 0);
507        sumOfRemainingTimes = itemArray.slice(9).reduce((acc, obj) => acc + obj.threadTime, 0);
508        newArr[9].value = sumOfRemainingValues;
509        newArr[9].threadTime = sumOfRemainingTimes;
510        newArr[9].threadName = 'other';
511      } else {
512        newArr = JSON.parse(JSON.stringify(itemArray));
513      }
514      itemArraySum = newArr.reduce((acc, obj) => acc + obj.value, 0);
515      if (itemArraySum > maxValue) {
516        maxValue = itemArraySum;
517      }
518      this.threadInfoStructMap.set(itemArray[0].startNS, newArr);
519    }
520    if (valueType === THREAD_ENERGY) {
521      XpowerThreadInfoStruct.energyMaxValue = maxValue;
522      this.threadEnergyStructMap = this.threadInfoStructMap;
523      XpowerThreadInfoStruct.threadEnergyStructMap = this.setDataMap(resultXpower);
524    } else if (valueType === THREAD_LOAD) {
525      XpowerThreadInfoStruct.loadMaxValue = maxValue;
526      this.threadLoadStructMap = this.threadInfoStructMap;
527      XpowerThreadInfoStruct.threadLoadStructMap = this.setDataMap(resultXpower);
528    }
529    let resultXpowerLit = Array.from(this.threadInfoStructMap.values()).reduce(
530      (acc, valueArray) => acc.concat(valueArray),
531      []
532    );
533    //@ts-ignore
534    return resultXpowerLit;
535  }
536
537  private setTips(traceRow: TraceRow<XpowerThreadInfoStruct>, value: string): void {
538    let tipsHtml = '';
539    if (XpowerThreadInfoStruct.hoverXpowerStruct) {
540      let hoverData: XpowerThreadInfoStruct[] = [];
541      let unit = '';
542      if (XpowerThreadInfoStruct.hoverXpowerStruct.valueType === THREAD_ENERGY) {
543        hoverData = this.threadEnergyStructMap!.get(XpowerThreadInfoStruct.hoverXpowerStruct.startNS) || [];
544        unit = 'mAh';
545      } else if (XpowerThreadInfoStruct.hoverXpowerStruct.valueType === THREAD_LOAD) {
546        hoverData = this.threadLoadStructMap!.get(XpowerThreadInfoStruct.hoverXpowerStruct.startNS) || [];
547        unit = '%';
548      }
549      hoverData = [...hoverData].reverse();
550      for (let i = 0; i < hoverData.length; i++) {
551        if (hoverData[i].value > 0) {
552          tipsHtml += `<div style=" display: flex; flex-wrap: nowrap; justify-content: space-between;">
553                <div style=" line-height: 20px; flex-grow: 2; flex-shrink: 1; flex-basis: auto;">Name: ${
554                  hoverData[i].threadName! || ''
555                }</div>
556                <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;">&nbsp;&nbsp;Duration: ${
557                  hoverData[i].threadTime! || 0
558                }&nbsp;ms</div>
559                <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;">&nbsp;&nbsp;${value}: ${
560            hoverData[i].value! || 0
561          }&nbsp;${unit}</div>
562            </div>`;
563        }
564      }
565    }
566    this.trace?.displayTip(traceRow, XpowerThreadInfoStruct.hoverXpowerStruct, `${tipsHtml}`);
567  }
568
569  private xpowerThreadInfoThreadHandler(traceRow: TraceRow<XpowerThreadInfoStruct>, valueType: string): void {
570    traceRow.onThreadHandler = (useCache): void => {
571      let context: CanvasRenderingContext2D;
572      if (traceRow.currentContext) {
573        context = traceRow.currentContext;
574      } else {
575        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
576      }
577      traceRow.canvasSave(context);
578      (renders.xpowerThreadInfo as XpowerThreadInfoRender).renderMainThread(
579        {
580          context: context,
581          useCache: useCache,
582          type: valueType,
583        },
584        traceRow
585      );
586      traceRow.canvasRestore(context, this.trace);
587    };
588  }
589
590  private xpowerGpuFreqSupplierFrame(traceRow: TraceRow<XpowerGpuFreqStruct>): void {
591    traceRow.supplierFrame = (): Promise<XpowerGpuFreqStruct[]> => {
592      let promiseData = xpowerGpuFreqDataSender(traceRow);
593      if (promiseData === null) {
594        return new Promise<Array<XpowerGpuFreqStruct>>((resolve) => resolve([]));
595      } else {
596        return promiseData.then((resultXpower: Array<XpowerGpuFreqStruct>) => {
597          return this.getGpuFreqDrawData(resultXpower);
598        });
599      }
600    };
601  }
602
603  private setGpuFreqDataMap(resultXpower: Array<XpowerGpuFreqStruct>): Map<number, Array<XpowerGpuFreqStruct>> {
604    let gpuFreqStructMap = new Map();
605    resultXpower.forEach((item: XpowerGpuFreqStruct, index: number) => {
606      const startNS = item.startNS;
607      if (gpuFreqStructMap.has(startNS)) {
608        const data = gpuFreqStructMap.get(startNS)!;
609        data.push(item);
610      } else {
611        const data = Array<XpowerGpuFreqStruct>();
612        data.push(item);
613        gpuFreqStructMap.set(startNS, data);
614      }
615      let hoverHtml = '';
616      if (item.runTime > 0) {
617        hoverHtml = `<div style=" display: flex; flex-wrap: nowrap; justify-content: space-between;">
618            <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;">frequency: ${
619              item.frequency! || 0
620            }</div>
621            <div style=" line-height: 20px; flex-grow: 2; flex-shrink: 1; flex-basis: auto;">&nbsp;&nbsp;runTime: ${
622              item.runTime! || 0
623            }&nbsp;ms</div>
624            <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;">&nbsp;&nbsp;idleTime: ${
625              item.idleTime! || 0
626            }&nbsp;ms</div>
627        </div>`;
628      }
629      item.hoverHtml = hoverHtml;
630    });
631    return gpuFreqStructMap;
632  }
633
634  private sumTime(arr: Array<XpowerGpuFreqStruct>): Array<XpowerGpuFreqStruct> {
635    return arr.reduce((accumulator: Array<XpowerGpuFreqStruct>, current) => {
636      const { frequency, runTime, idleTime } = current;
637      const existingEntry = accumulator.find((entry) => entry.frequency === frequency);
638      if (existingEntry) {
639        existingEntry.runTime += runTime;
640        existingEntry.idleTime += idleTime;
641      } else {
642        accumulator.push({ ...current });
643      }
644      return accumulator;
645    }, []);
646  }
647
648  private getGpuFreqDrawData(resultXpower: Array<XpowerGpuFreqStruct>): XpowerGpuFreqStruct[] {
649    this.gpuFreqStructMap = new Map();
650    this.gpuFreqStructMap = this.setGpuFreqDataMap(resultXpower);
651    for (let itemArray of this.gpuFreqStructMap.values()) {
652      itemArray = this.sumTime(itemArray);
653      itemArray.sort((a, b) => {
654        return b.runTime - a.runTime;
655      });
656      this.gpuFreqStructMap.set(itemArray[0].startNS, itemArray);
657    }
658    XpowerGpuFreqStruct.gpuFreqStructMap = this.gpuFreqStructMap;
659    let resultXpowerLit = Array.from(this.gpuFreqStructMap.values()).reduce(
660      (acc, valueArray) => acc.concat(valueArray),
661      []
662    );
663    return resultXpowerLit;
664  }
665
666  private xpowerStatisticThreadHandler(traceRow: TraceRow<XpowerStatisticStruct>): void {
667    traceRow.onThreadHandler = (useCache): void => {
668      let context: CanvasRenderingContext2D;
669      if (traceRow.currentContext) {
670        context = traceRow.currentContext;
671      } else {
672        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
673      }
674      traceRow.canvasSave(context);
675      (renders.xpowerStatistic as XpowerStatisticRender).renderMainThread(
676        {
677          context: context,
678          useCache: useCache,
679        },
680        traceRow
681      );
682      traceRow.canvasRestore(context, this.trace);
683    };
684  }
685
686  async initXpowerStatisticData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> {
687    let traceRow = TraceRow.skeleton<XpowerStatisticStruct>(traceId);
688    traceRow.rowId = 'Statistic';
689    traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_STATISTIC;
690    traceRow.rowParentId = folder.rowId;
691    traceRow.style.height = '200px';
692    traceRow.setAttribute('height', '200px');
693    let element = traceRow.shadowRoot?.querySelector('.root') as HTMLDivElement;
694    element!.style.height = '200px';
695    traceRow.name = 'Statistic';
696    traceRow.rowHidden = !folder.expansion;
697    traceRow.folderTextLeft = 40;
698    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
699    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
700    traceRow.setAttribute('children', '');
701
702    let list = [
703      'audio',
704      'bluetooth',
705      'camera',
706      'cpu',
707      'display',
708      'flashlight',
709      'gpu',
710      'location',
711      'wifiscan',
712      'wifi',
713      'modem',
714    ];
715    traceRow.rowSettingCheckBoxList = list;
716    traceRow.addRowSettingCheckBox();
717    traceRow.rowSetting = 'enable';
718    traceRow.rowSettingPopoverDirection = 'bottomLeft';
719    traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => {
720      this.trace.refreshCanvas(false);
721    };
722    traceRow.supplierFrame = (): Promise<XpowerStatisticStruct[]> =>
723      xpowerStatisticDataSender(traceRow).then((res): XpowerStatisticStruct[] => {
724        res.forEach((item) => {
725          item.typeStr = SpSystemTrace.DATA_DICT.get(item.type)!;
726        });
727        return res;
728      });
729    traceRow.getCacheData = (args: unknown): Promise<XpowerStatisticStruct[]> | undefined => {
730      let result: Promise<XpowerStatisticStruct[]> | undefined;
731      result = xpowerStatisticDataSender(traceRow, args);
732      return result;
733    };
734    traceRow.focusHandler = (ev): void => {
735      this.trace?.displayTip(
736        traceRow,
737        XpowerStatisticStruct.hoverXpowerStruct,
738        XpowerStatisticStruct.hoverXpowerStruct?.hoverHtml!
739      );
740    };
741    this.xpowerStatisticThreadHandler(traceRow);
742    folder.addChildTraceRow(traceRow);
743  }
744
745  private xpowerAppDetailThreadHandler(traceRow: TraceRow<XpowerAppDetailStruct>): void {
746    traceRow.onThreadHandler = (useCache): void => {
747      let context: CanvasRenderingContext2D;
748      if (traceRow.currentContext) {
749        context = traceRow.currentContext;
750      } else {
751        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
752      }
753      traceRow.canvasSave(context);
754      (renders.xpowerAppDetail as XpowerAppDetailRender).renderMainThread(
755        {
756          context: context,
757          useCache: useCache,
758        },
759        traceRow
760      );
761      traceRow.canvasRestore(context, this.trace);
762    };
763  }
764
765  async initXpowerAppDetatilDisplayData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> {
766    let traceRow = TraceRow.skeleton<XpowerAppDetailStruct>(traceId);
767    traceRow.rowId = 'AppDetailDisplay';
768    traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_APP_DETAIL_DISPLAY;
769    traceRow.rowParentId = folder.rowId;
770    traceRow.style.height = '200px';
771    traceRow.setAttribute('height', '200px');
772    let element = traceRow.shadowRoot?.querySelector('.root') as HTMLDivElement;
773    element!.style.height = '200px';
774    traceRow.name = 'Display';
775    traceRow.rowHidden = !folder.expansion;
776    traceRow.folderTextLeft = 40;
777    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
778    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
779    traceRow.setAttribute('children', '');
780
781    let list = ['180hz', '120hz', '90hz', '60hz', '45hz', '30hz', '24hz', '15hz', '10hz', '5hz', '1hz'];
782    traceRow.rowSettingCheckBoxList = list;
783    traceRow.addRowSettingCheckBox();
784    traceRow.rowSetting = 'enable';
785    traceRow.rowSettingPopoverDirection = 'bottomLeft';
786    traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => {
787      this.trace.refreshCanvas(false);
788    };
789
790    traceRow.supplierFrame = (): Promise<XpowerAppDetailStruct[]> =>
791      xpowerAppDetailDataSender(traceRow).then((res): XpowerAppDetailStruct[] => {
792        return res;
793      });
794    traceRow.getCacheData = (args: unknown): Promise<XpowerAppDetailStruct[]> | undefined => {
795      let result: Promise<XpowerAppDetailStruct[]> | undefined;
796      result = xpowerAppDetailDataSender(traceRow, args);
797      return result;
798    };
799    traceRow.focusHandler = (ev): void => {
800      this.trace?.displayTip(
801        traceRow,
802        XpowerAppDetailStruct.hoverXpowerStruct,
803        XpowerAppDetailStruct.hoverXpowerStruct?.hoverHtml!
804      );
805    };
806    this.xpowerAppDetailThreadHandler(traceRow);
807    folder.addChildTraceRow(traceRow);
808  }
809
810  private xpowerWifiThreadHandler(traceRow: TraceRow<XpowerWifiStruct>, name: string): void {
811    traceRow.onThreadHandler = (useCache): void => {
812      let context: CanvasRenderingContext2D;
813      if (traceRow.currentContext) {
814        context = traceRow.currentContext;
815      } else {
816        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
817      }
818      traceRow.canvasSave(context);
819      (renders.xpowerWifi as XpowerWifiRender).renderMainThread(
820        {
821          context: context,
822          useCache: useCache,
823          name: name,
824        },
825        traceRow
826      );
827      traceRow.canvasRestore(context, this.trace);
828    };
829  }
830
831  async initXpowerWifiData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> {
832    let name = ['WIFIPackets', 'WIFIBytes'];
833    for (let it of name) {
834      let traceRow = TraceRow.skeleton<XpowerWifiStruct>(traceId);
835      traceRow.rowId = it;
836      traceRow.rowType = it === name[0] ? TraceRow.ROW_TYPE_XPOWER_WIFI_PACKETS : TraceRow.ROW_TYPE_XPOWER_WIFI_BYTES;
837      traceRow.rowParentId = folder.rowId;
838      traceRow.style.height = '100px';
839      traceRow.setAttribute('height', '100px');
840      let element = traceRow.shadowRoot?.querySelector('.root') as HTMLDivElement;
841      element!.style.height = '100px';
842      traceRow.name = it;
843      traceRow.rowHidden = !folder.expansion;
844      traceRow.folderTextLeft = 40;
845      traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
846      traceRow.selectChangeHandler = this.trace.selectChangeHandler;
847      traceRow.setAttribute('children', '');
848      traceRow.rowSettingCheckBoxList = ['tx', 'rx'];
849      traceRow.addRowSettingCheckBox();
850      traceRow.rowSetting = 'enable';
851      traceRow.rowSettingPopoverDirection = 'bottomLeft';
852      traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => {
853        this.trace.refreshCanvas(false);
854      };
855      traceRow.supplierFrame = (): Promise<XpowerWifiStruct[]> =>
856        xpowerWifiDataSender(traceRow, it).then((res): XpowerWifiStruct[] => {
857          return res;
858        });
859      traceRow.getCacheData = (args: unknown): Promise<XpowerWifiStruct[]> | undefined => {
860        let result: Promise<XpowerWifiStruct[]> | undefined;
861        result = xpowerWifiDataSender(traceRow, it, args);
862        return result;
863      };
864      traceRow.focusHandler = (ev): void => {
865        it === 'WIFIPackets'
866          ? this.trace?.displayTip(
867              traceRow,
868              XpowerWifiStruct.hoverPacketsStruct,
869              XpowerWifiStruct.hoverPacketsStruct?.hoverHtmlPackets!
870            )
871          : this.trace?.displayTip(
872              traceRow,
873              XpowerWifiStruct.hoverBytesStruct,
874              XpowerWifiStruct.hoverBytesStruct?.hoverHtmlBytes!
875            );
876      };
877      this.xpowerWifiThreadHandler(traceRow, it);
878      folder.addChildTraceRow(traceRow);
879    }
880  }
881
882  private xpowerGpuFreqThreadHandler(traceRow: TraceRow<XpowerGpuFreqStruct>): void {
883    traceRow.onThreadHandler = (useCache): void => {
884      let context: CanvasRenderingContext2D;
885      if (traceRow.currentContext) {
886        context = traceRow.currentContext;
887      } else {
888        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
889      }
890      traceRow.canvasSave(context);
891      (renders.xpowerGpuFreq as XpowerGpuFreqRender).renderMainThread(
892        {
893          context: context,
894          useCache: useCache,
895        },
896        traceRow
897      );
898      traceRow.canvasRestore(context, this.trace);
899    };
900  }
901
902  async initGpuFreqData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> {
903    let traceRow = TraceRow.skeleton<XpowerGpuFreqStruct>(traceId);
904    traceRow.rowId = 'gpu-frequency';
905    traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_GPU_FREQUENCY;
906    traceRow.rowParentId = folder.rowId;
907    traceRow.style.height = `${ROW_HEIGHT}px`;
908    traceRow.name = 'GPU Freq';
909    traceRow.rowHidden = !folder.expansion;
910    traceRow.folderTextLeft = 40;
911    traceRow.setAttribute('children', '');
912    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
913    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
914    let freqList = await queryFreq();
915    // @ts-ignore
916    const values = freqList.map((item) => item.frequency.toString());
917    let freqSet = new Set(values);
918    traceRow.rowSettingCheckBoxList = [...freqSet];
919    traceRow.addRowSettingCheckBox();
920    traceRow.rowSetting = 'enable';
921    traceRow.rowSettingPopoverDirection = 'bottomLeft';
922    traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => {
923      this.trace.refreshCanvas(false);
924    };
925    this.xpowerGpuFreqSupplierFrame(traceRow);
926    traceRow.getCacheData = (args: unknown): Promise<XpowerGpuFreqStruct[]> | undefined => {
927      let result: Promise<XpowerGpuFreqStruct[]> | undefined;
928      result = xpowerGpuFreqDataSender(traceRow, args);
929      return result;
930    };
931    traceRow.focusHandler = (ev): void => {
932      let html =
933        (XpowerGpuFreqStruct.hoverXpowerStruct &&
934          XpowerGpuFreqStruct.hoverMap.get(XpowerGpuFreqStruct.hoverXpowerStruct.startNS)) ||
935        '';
936      this.trace?.displayTip(traceRow, XpowerGpuFreqStruct.hoverXpowerStruct, html);
937    };
938    this.xpowerGpuFreqThreadHandler(traceRow);
939    folder.addChildTraceRow(traceRow);
940  }
941}
942
943// 鼠标悬浮时转换xpower泳道名
944export function convertTitle(title: string): string {
945  switch (title) {
946    case 'Battery.Capacity':
947      return '电池容量(单位mAh)';
948    case 'Battery.Charge':
949      return '充电状态(充电1,非充电0)';
950    case 'Battery.GasGauge':
951      return '电池剩余电量(单位mAh)';
952    case 'Battery.Level':
953      return '电池百分比';
954    case 'Battery.RealCurrent':
955      return '实时电流(单位mAh,充电时为正数,耗电时为负数)';
956    case 'Battery.Screen':
957      return '屏幕状态(亮屏1,灭屏0)';
958    case 'ThermalReport.ShellTemp':
959      return '外壳温度(单位℃)';
960    case 'ThermalReport.ThermalLevel':
961      return '温度等级';
962    default:
963      return title;
964  }
965}
966
967// 鼠标悬浮ThermalReport.ThermalLevel泳道时转换悬浮框内容
968export function convertHoverValue(value: string): string {
969  switch (value) {
970    case '0':
971      return 'COOL';
972    case '1':
973      return 'WARM';
974    case '2':
975      return 'HOT';
976    case '3':
977      return 'OVERHEATED';
978    default:
979      return value;
980  }
981}
982