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 { TraceRow } from '../trace/base/TraceRow';
17import { renders } from '../../database/ui-worker/ProcedureWorker';
18import { JankRender, JankStruct } from '../../database/ui-worker/ProcedureWorkerJank';
19import { SpSystemTrace } from '../SpSystemTrace';
20import { JanksStruct } from '../../bean/JanksStruct';
21import { ns2xByTimeShaft, type PairPoint } from '../../database/ui-worker/ProcedureWorkerCommon';
22import { FrameDynamicRender, FrameDynamicStruct } from '../../database/ui-worker/ProcedureWorkerFrameDynamic';
23import { FrameAnimationRender, FrameAnimationStruct } from '../../database/ui-worker/ProcedureWorkerFrameAnimation';
24import { type BaseStruct } from '../../bean/BaseStruct';
25import { FrameSpacingRender, FrameSpacingStruct } from '../../database/ui-worker/ProcedureWorkerFrameSpacing';
26import { FlagsConfig, type Params } from '../SpFlags';
27import { type AnimationRanges, type DeviceStruct } from '../../bean/FrameComponentBean';
28import { type EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU';
29import { TreeItemData } from '../../../base-ui/tree/LitTree';
30import { QueryEnum } from '../../database/data-trafic/utils/QueryEnum';
31import {
32  frameAnimationSender,
33  frameDynamicSender,
34  frameSpacingSender,
35} from '../../database/data-trafic/FrameDynamicEffectSender';
36import { frameJanksSender } from '../../database/data-trafic/FrameJanksSender';
37import {
38  queryAnimationIdAndNameData,
39  queryAnimationTimeRangeData,
40  queryDynamicIdAndNameData,
41  queryFrameApp,
42  queryFrameTimeData,
43  queryPhysicalData,
44  querySourceTypen,
45} from '../../database/sql/SqlLite.sql';
46import { queryAllProcessNames } from '../../database/sql/ProcessThread.sql';
47
48export class SpFrameTimeChart {
49  private trace: SpSystemTrace;
50  private flagConfig: Params | undefined;
51  private pidToProcessNameMap: Map<number, string> = new Map();
52  private idToProcessNameMap: Map<number, string> = new Map();
53
54  constructor(trace: SpSystemTrace) {
55    this.trace = trace;
56  }
57
58  async init(): Promise<void> {
59    let frameTimeData = await queryFrameTimeData();
60    this.pidToProcessNameMap.clear();
61    this.idToProcessNameMap.clear();
62    if (frameTimeData.length > 0) {
63      let processNamesArray = await queryAllProcessNames();
64      processNamesArray.forEach((it) => {
65        //@ts-ignore
66        this.pidToProcessNameMap.set(it.pid, it.name); //@ts-ignore
67        this.idToProcessNameMap.set(it.id, it.name);
68      });
69      let frameTimeLineRow: TraceRow<JanksStruct> = await this.initFrameTimeLine();
70      await this.initExpectedChart(frameTimeLineRow);
71      await this.initActualChart(frameTimeLineRow);
72    }
73  }
74
75  async initFrameTimeLine(): Promise<TraceRow<JanksStruct>> {
76    let frameTimeLineRow: TraceRow<JanksStruct> = TraceRow.skeleton<JanksStruct>();
77    frameTimeLineRow.rowId = 'frameTime';
78    frameTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK;
79    frameTimeLineRow.rowParentId = '';
80    frameTimeLineRow.style.width = '100%';
81    frameTimeLineRow.style.height = '40px';
82    frameTimeLineRow.folder = true;
83    frameTimeLineRow.name = 'FrameTimeline';
84    frameTimeLineRow.setAttribute('children', '');
85    frameTimeLineRow.supplier = (): Promise<JanksStruct[]> =>
86      new Promise((resolve) => {
87        resolve([]);
88      });
89    frameTimeLineRow.addTemplateTypes('AppStartup');
90    frameTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
91    frameTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler;
92    frameTimeLineRow.onThreadHandler = (useCache: boolean): void => {
93      let context: CanvasRenderingContext2D;
94      if (frameTimeLineRow.currentContext) {
95        context = frameTimeLineRow.currentContext;
96      } else {
97        context = frameTimeLineRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
98      }
99      frameTimeLineRow!.canvasSave(context);
100      (renders.jank as JankRender).renderMainThread(
101        {
102          context: context,
103          useCache: useCache,
104          type: 'expected_frame_timeline_slice',
105        },
106        frameTimeLineRow!
107      );
108      frameTimeLineRow!.canvasRestore(context, this.trace);
109    };
110    this.trace.rowsEL?.appendChild(frameTimeLineRow);
111    return frameTimeLineRow;
112  }
113
114  private expectedChartSupplierFrame(expectedTimeLineRow: TraceRow<JanksStruct>): void {
115    expectedTimeLineRow.supplierFrame = async (): Promise<JanksStruct[]> => {
116      const res = await frameJanksSender(QueryEnum.FrameExpectedData, expectedTimeLineRow);
117      let maxDepth: number = 1;
118      let unitHeight: number = 20;
119      res.forEach((item) => {
120        if (item.depth! >= maxDepth) {
121          maxDepth = item.depth! + 1;
122        }
123        item.frameType = 'frameTime';
124        item.cmdline = this.pidToProcessNameMap.get(item.pid!);
125        item.rs_name = this.idToProcessNameMap.get(Number(item.rs_name)!);
126      });
127      if (expectedTimeLineRow && !expectedTimeLineRow.isComplete && res.length > 0) {
128        let maxHeight: number = maxDepth * unitHeight;
129        expectedTimeLineRow.style.height = `${maxHeight}px`;
130        expectedTimeLineRow.setAttribute('height', `${maxHeight}`);
131      }
132      return res;
133    };
134  }
135
136  async initExpectedChart(frameTimeLineRow: TraceRow<JanksStruct>): Promise<void> {
137    let expectedTimeLineRow = TraceRow.skeleton<JanksStruct>();
138    expectedTimeLineRow.rowId = 'expected frameTime';
139    expectedTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK;
140    expectedTimeLineRow.rowHidden = !frameTimeLineRow.expansion;
141    expectedTimeLineRow.rowParentId = 'frameTime';
142    expectedTimeLineRow.style.width = '100%';
143    expectedTimeLineRow.name = 'Expected Timeline';
144    expectedTimeLineRow.addTemplateTypes('FrameTimeline');
145    expectedTimeLineRow.setAttribute('children', '');
146    this.expectedChartSupplierFrame(expectedTimeLineRow);
147    expectedTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
148    expectedTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler;
149    expectedTimeLineRow.onThreadHandler = (useCache: boolean): void => {
150      let context: CanvasRenderingContext2D;
151      if (expectedTimeLineRow.currentContext) {
152        context = expectedTimeLineRow.currentContext;
153      } else {
154        context = expectedTimeLineRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
155      }
156      expectedTimeLineRow!.canvasSave(context);
157      (renders.jank as JankRender).renderMainThread(
158        {
159          context: context,
160          useCache: useCache,
161          type: 'expected_frame_timeline_slice',
162        },
163        expectedTimeLineRow!
164      );
165      expectedTimeLineRow!.canvasRestore(context, this.trace);
166    };
167    frameTimeLineRow.addChildTraceRow(expectedTimeLineRow);
168  }
169
170  private actualChartSupplierFrame(row: TraceRow<JanksStruct>): void {
171    row.supplierFrame = async (): Promise<JanksStruct[]> => {
172      const res = await frameJanksSender(QueryEnum.FrameActualData, row);
173      let maxDepth: number = 1;
174      let unitHeight: number = 20;
175      res.forEach((item) => {
176        if (item.depth! >= maxDepth) {
177          maxDepth = item.depth! + 1;
178        }
179        item.frameType = 'frameTime';
180        item.cmdline = this.pidToProcessNameMap.get(item.pid!);
181        item.rs_name = this.idToProcessNameMap.get(Number(item.rs_name)!);
182        item.type = '0';
183      });
184      if (row && !row.isComplete && res.length > 0) {
185        let maxHeight: number = maxDepth * unitHeight;
186        row.style.height = `${maxHeight}px`;
187        row.setAttribute('height', `${maxHeight}`);
188      }
189      return res;
190    };
191  }
192
193  async initActualChart(frameTimeLineRow: TraceRow<JanksStruct>): Promise<void> {
194    let actualTimeLineRow = TraceRow.skeleton<JanksStruct>();
195    actualTimeLineRow.rowId = 'actual frameTime';
196    actualTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK;
197    actualTimeLineRow.rowHidden = !frameTimeLineRow.expansion;
198    actualTimeLineRow.rowParentId = 'frameTime';
199    actualTimeLineRow.style.width = '100%';
200    actualTimeLineRow.name = 'Actual Timeline';
201    actualTimeLineRow.addTemplateTypes('FrameTimeline');
202    actualTimeLineRow.setAttribute('children', '');
203    this.actualChartSupplierFrame(actualTimeLineRow);
204    actualTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
205    actualTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler;
206    actualTimeLineRow.onThreadHandler = (useCache: boolean): void => {
207      let context: CanvasRenderingContext2D;
208      if (actualTimeLineRow.currentContext) {
209        context = actualTimeLineRow.currentContext;
210      } else {
211        context = actualTimeLineRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
212      }
213      actualTimeLineRow!.canvasSave(context);
214      (renders.jank as JankRender).renderMainThread(
215        {
216          context: context,
217          useCache: useCache,
218          type: 'expected_frame_timeline_slice',
219        },
220        actualTimeLineRow!
221      );
222      actualTimeLineRow!.canvasRestore(context, this.trace);
223    };
224    frameTimeLineRow.addChildTraceRow(actualTimeLineRow);
225    let offsetYTimeOut: number = 0;
226    frameTimeLineRow.addEventListener('expansion-change', (customEventInit: CustomEventInit) => {
227      JankStruct.delJankLineFlag = false;
228      if (offsetYTimeOut) {
229        clearTimeout(offsetYTimeOut);
230      }
231      if (customEventInit.detail?.expansion) {
232        offsetYTimeOut = this.frameExpandTimeOut(customEventInit, actualTimeLineRow);
233      } else {
234        offsetYTimeOut = this.frameNoExpandTimeOut(customEventInit, frameTimeLineRow);
235      }
236    });
237  }
238
239  async initAnimatedScenesChart(
240    processRow: TraceRow<BaseStruct>,
241    process: { pid: number | null; processName: string | null },
242    firstRow: TraceRow<BaseStruct>,
243    secondRow: TraceRow<BaseStruct>
244  ): Promise<void> {
245    let sourceTypeName = await querySourceTypen();
246    this.flagConfig = FlagsConfig.getFlagsConfig('AnimationAnalysis');
247    let appNameMap: Map<number, string> = new Map();
248    //@ts-ignore
249    if (this.flagConfig?.AnimationAnalysis === 'Enabled' && sourceTypeName[0].value !== 'txt-based-trace') {
250      if (process.processName?.startsWith('render_service')) {
251        let targetRowList = processRow.childrenList.filter(
252          (childRow) => childRow.rowType === 'thread' && childRow.name.startsWith('render_service')
253        );
254        let nameArr: { name: string }[] = await queryFrameApp();
255        if (nameArr && nameArr.length > 0) {
256          let currentName = nameArr[0].name;
257          let frameChart = await this.initFrameChart(processRow, nameArr);
258          if (firstRow !== null) {
259            processRow.addChildTraceRowBefore(frameChart, firstRow);
260          } else if (secondRow !== null) {
261            processRow.addChildTraceRowBefore(frameChart, secondRow);
262          } else {
263            // @ts-ignore
264            processRow.addChildTraceRowBefore(frameChart, targetRowList[0]);
265          }
266          let appNameList = await queryDynamicIdAndNameData();
267          appNameList.forEach((item) => {
268            appNameMap.set(item.id, item.appName);
269          });
270          let animationRanges = await this.initAnimationChart(processRow);
271          await this.initDynamicCurveChart(appNameMap, frameChart, currentName, animationRanges);
272          await this.initFrameSpacing(appNameMap, frameChart, currentName, animationRanges);
273        }
274      }
275    }
276  }
277
278  private async initFrameChart(
279    processRow: TraceRow<BaseStruct>,
280    nameArr: { name: string }[]
281  ): Promise<TraceRow<BaseStruct>> {
282    let frameChart: TraceRow<BaseStruct> = TraceRow.skeleton<BaseStruct>();
283    let labelName = frameChart.shadowRoot?.querySelector('.name') as HTMLLabelElement;
284    labelName.style.marginRight = '77px';
285    this.addSystemConfigButton(frameChart, nameArr, 'model-name', true);
286    frameChart.rowId = 'frame';
287    frameChart.rowType = TraceRow.ROW_TYPE_FRAME;
288    frameChart.rowHidden = !processRow.expansion;
289    frameChart.rowParentId = processRow.rowId;
290    frameChart.style.width = '100%';
291    frameChart.style.height = '40px';
292    frameChart.folder = true;
293    frameChart.name = nameArr[0].name;
294    frameChart.setAttribute('children', '');
295    frameChart.supplier = (): Promise<BaseStruct[]> =>
296      new Promise((resolve) => {
297        resolve([]);
298      });
299    frameChart.favoriteChangeHandler = this.trace.favoriteChangeHandler;
300    frameChart.selectChangeHandler = this.trace.selectChangeHandler;
301    frameChart.onThreadHandler = (useCache: boolean): void => {
302      let context: CanvasRenderingContext2D = frameChart!.collect
303        ? this.trace.canvasFavoritePanelCtx!
304        : this.trace.canvasPanelCtx!;
305      frameChart!.canvasSave(context);
306      (renders.empty as EmptyRender).renderMainThread(
307        {
308          context: context,
309          useCache: useCache,
310          type: 'frame',
311        },
312        frameChart!
313      );
314      frameChart!.canvasRestore(context, this.trace);
315    };
316    this.trace.rowsEL?.appendChild(frameChart);
317    return frameChart;
318  }
319
320  private animationChartSupplierFrame(
321    row: TraceRow<FrameAnimationStruct>,
322    animationIdNameMap: Map<number, string>,
323    animationIdInfoMap: Map<number, string>
324  ): void {
325    const unitIndex: number = 1;
326    const unitHeight: number = 20;
327    row.supplierFrame = async (): Promise<FrameAnimationStruct[]> => {
328      const result = await frameAnimationSender(row);
329      let maxDepth = 0;
330      result.forEach((item) => {
331        if (`${item.status}` === '1') {
332          item.status = 'Completion delay';
333        } else if (`${item.status}` === '0') {
334          item.status = 'Response delay';
335        }
336        if (item.depth > maxDepth) {
337          maxDepth = item.depth;
338        }
339        if (animationIdNameMap.has(item.animationId!)) {
340          item.name = animationIdNameMap.get(item.animationId!);
341          item.frameInfo = item.status === 'Completion delay' ? animationIdInfoMap.get(item.animationId!) : '0';
342        }
343      });
344      let maxHeight: number = (maxDepth + unitIndex) * unitHeight;
345      row.style.height = `${maxHeight}px`;
346      row.setAttribute('height', `${maxHeight}`);
347      return result;
348    };
349  }
350  private animationThreadHandler(row: TraceRow<FrameAnimationStruct>): void {
351    row.onThreadHandler = (useCache): void => {
352      let context: CanvasRenderingContext2D = row!.collect
353        ? this.trace.canvasFavoritePanelCtx!
354        : this.trace.canvasPanelCtx!;
355      row!.canvasSave(context);
356      (renders.frameAnimation as FrameAnimationRender).renderMainThread(
357        {
358          context: context,
359          useCache: useCache,
360          type: 'frameAnimation',
361        },
362        row!
363      );
364      row!.canvasRestore(context, this.trace);
365    };
366  }
367
368  async initAnimationChart(processRow: TraceRow<BaseStruct>): Promise<AnimationRanges[]> {
369    let animationRanges: AnimationRanges[] = [];
370    let frameAnimationRow = TraceRow.skeleton<FrameAnimationStruct>();
371
372    frameAnimationRow.rowId = 'Animation';
373    frameAnimationRow.rowType = TraceRow.ROW_TYPE_FRAME_ANIMATION;
374    frameAnimationRow.rowHidden = !processRow.expansion;
375    frameAnimationRow.rowParentId = processRow.rowId;
376    frameAnimationRow.style.width = '100%';
377    frameAnimationRow.name = 'Animation';
378    frameAnimationRow.addTemplateTypes('AnimationEffect');
379    frameAnimationRow.setAttribute('children', '');
380    let timeRangeData = await queryAnimationTimeRangeData();
381    timeRangeData.forEach((rangeTime) => {
382      if (rangeTime.status === 'Completion delay') {
383        animationRanges.push({
384          start: rangeTime.startTs,
385          end: rangeTime.endTs,
386        });
387      }
388    });
389    let animationIdNameMap: Map<number, string> = new Map<number, string>();
390    let animationIdInfoMap: Map<number, string> = new Map<number, string>();
391    let animationNameData = await queryAnimationIdAndNameData();
392    animationNameData.forEach((item) => {
393      animationIdNameMap.set(item.id, item.name);
394      animationIdInfoMap.set(item.id, item.info);
395    });
396    this.animationChartSupplierFrame(frameAnimationRow, animationIdNameMap, animationIdInfoMap);
397    frameAnimationRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
398    frameAnimationRow.selectChangeHandler = this.trace.selectChangeHandler;
399    this.animationThreadHandler(frameAnimationRow);
400    processRow.addChildTraceRowSpecifyLocation(frameAnimationRow, 0);
401    return animationRanges;
402  }
403
404  private dynamicCurveChartThreadHandler(
405    dynamicCurveRow: TraceRow<FrameDynamicStruct>,
406    animationRanges: AnimationRanges[]
407  ): void {
408    dynamicCurveRow.onThreadHandler = (useCache: boolean): void => {
409      let context: CanvasRenderingContext2D = dynamicCurveRow!.collect
410        ? this.trace.canvasFavoritePanelCtx!
411        : this.trace.canvasPanelCtx!;
412      dynamicCurveRow!.canvasSave(context);
413      (renders.frameDynamicCurve as FrameDynamicRender).renderMainThread(
414        {
415          context: context,
416          useCache: useCache,
417          type: 'dynamicEffectCurve',
418          animationRanges: animationRanges,
419        },
420        dynamicCurveRow!
421      );
422      dynamicCurveRow!.canvasRestore(context, this.trace);
423    };
424  }
425
426  async initDynamicCurveChart(
427    appNameMap: Map<number, string>,
428    frameChart: TraceRow<BaseStruct>,
429    name: string,
430    animationRanges: AnimationRanges[]
431  ): Promise<void> {
432    let systemConfigList: {
433      name: string;
434    }[] = [{ name: 'x' }, { name: 'y' }, { name: 'width' }, { name: 'height' }, { name: 'alpha' }];
435    let dynamicCurveRow: TraceRow<FrameDynamicStruct> = TraceRow.skeleton<FrameDynamicStruct>();
436    this.addSystemConfigButton(dynamicCurveRow, systemConfigList, 'model-type');
437    dynamicCurveRow.setAttribute('model-type', systemConfigList[0].name);
438    dynamicCurveRow.rowId = 'animation-Effect-Curve';
439    dynamicCurveRow.rowType = TraceRow.ROW_TYPE_FRAME_DYNAMIC;
440    dynamicCurveRow.rowHidden = !frameChart.expansion;
441    dynamicCurveRow.rowParentId = frameChart.rowId;
442    dynamicCurveRow.style.width = '100%';
443    dynamicCurveRow.style.height = '40px';
444    dynamicCurveRow.style.height = '100px';
445    let labelName = dynamicCurveRow.shadowRoot?.querySelector('.name') as HTMLLabelElement;
446    labelName.style.marginRight = '77px';
447    dynamicCurveRow.name = 'Animation Effect Curve';
448    dynamicCurveRow.addTemplateTypes('AnimationEffect');
449    dynamicCurveRow.setAttribute('height', '100px');
450    dynamicCurveRow.setAttribute('children', '');
451    dynamicCurveRow.setAttribute('model-type', systemConfigList[0].name);
452    dynamicCurveRow.setAttribute('model-name', name);
453    dynamicCurveRow.supplierFrame = async (): Promise<FrameDynamicStruct[]> => {
454      const result = await frameDynamicSender(dynamicCurveRow);
455      result.forEach((dataItem) => {
456        if (appNameMap.has(dataItem.id!)) {
457          dataItem.appName = appNameMap.get(dataItem.id!);
458        }
459      });
460      return result;
461    };
462    dynamicCurveRow.selectChangeHandler = this.trace.selectChangeHandler;
463    this.dynamicCurveChartThreadHandler(dynamicCurveRow, animationRanges);
464    frameChart.addChildTraceRow(dynamicCurveRow);
465  }
466
467  private FrameSpacingThreadHandler(
468    frameSpacingRow: TraceRow<FrameSpacingStruct>,
469    animationRanges: AnimationRanges[],
470    rate: number
471  ): void {
472    frameSpacingRow.onThreadHandler = (useCache: boolean): void => {
473      let context = frameSpacingRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
474      frameSpacingRow!.canvasSave(context);
475      (renders.frameSpacing as FrameSpacingRender).renderMainThread(
476        {
477          context: context,
478          useCache: useCache,
479          type: 'frame_spacing_slice',
480          frameRate: rate,
481          animationRanges: animationRanges,
482        },
483        frameSpacingRow!
484      );
485      frameSpacingRow!.canvasRestore(context, this.trace);
486    };
487  }
488
489  async initFrameSpacing(
490    appNameMap: Map<number, string>,
491    frameChart: TraceRow<BaseStruct>,
492    name: string,
493    animationRanges: AnimationRanges[]
494  ): Promise<void> {
495    let deviceStructArray = await queryPhysicalData();
496    let deviceStruct: DeviceStruct = deviceStructArray[0];
497    let frameSpacingRow = TraceRow.skeleton<FrameSpacingStruct>();
498    frameSpacingRow.rowId = 'frame spacing';
499    frameSpacingRow.rowType = TraceRow.ROW_TYPE_FRAME_SPACING;
500    frameSpacingRow.rowHidden = !frameChart.expansion;
501    frameSpacingRow.rowParentId = frameChart.rowId;
502    frameSpacingRow.style.width = '100%';
503    frameSpacingRow.style.height = '140px';
504    frameSpacingRow.name = 'Frame spacing';
505    frameSpacingRow.addTemplateTypes('AnimationEffect');
506    frameSpacingRow.setAttribute('height', '140');
507    frameSpacingRow.setAttribute('children', '');
508    frameSpacingRow.setAttribute('model-name', name);
509    let physicalConfigWidth = Number(this.flagConfig!.physicalWidth);
510    let physicalConfigHeight = Number(this.flagConfig!.physicalHeight);
511    let physicalWidth = physicalConfigWidth !== 0 ? physicalConfigWidth : deviceStruct.physicalWidth;
512    let physicalHeight = physicalConfigHeight !== 0 ? physicalConfigHeight : deviceStruct.physicalHeight;
513    frameSpacingRow.supplierFrame = async (): Promise<FrameSpacingStruct[]> => {
514      const result = await frameSpacingSender(physicalWidth, physicalHeight, frameSpacingRow);
515      result.forEach((dataItem) => {
516        if (appNameMap.has(dataItem.id!)) {
517          dataItem.nameId = appNameMap.get(dataItem.id!);
518        }
519        dataItem.physicalWidth = physicalWidth;
520        dataItem.physicalHeight = physicalHeight;
521      });
522      return result;
523    };
524    frameSpacingRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
525    frameSpacingRow.selectChangeHandler = this.trace.selectChangeHandler;
526    this.FrameSpacingThreadHandler(frameSpacingRow, animationRanges, deviceStruct.physicalFrameRate);
527    frameChart.addChildTraceRow(frameSpacingRow);
528  }
529
530  addSystemConfigButton(
531    systemTraceRow: TraceRow<BaseStruct>,
532    systemConfigList: { name: string }[],
533    attributeKey: string,
534    allowChangeName: boolean = false
535  ): void {
536    let componentList: Array<TreeItemData> = [];
537    for (let index = 0; index < systemConfigList.length; index++) {
538      let componentName = systemConfigList[index].name;
539      componentList.push({
540        key: `${componentName}`,
541        title: `${componentName}`,
542        checked: index === 0,
543      });
544    }
545    systemTraceRow.addRowSettingPop();
546    systemTraceRow.rowSetting = 'enable';
547    systemTraceRow.rowSettingPopoverDirection = 'bottomLeft';
548    systemTraceRow.rowSettingList = componentList;
549    systemTraceRow.onRowSettingChangeHandler = (value: string[]): void => {
550      if (allowChangeName) {
551        systemTraceRow.name = value[0];
552      }
553      systemTraceRow.setAttribute(attributeKey, `${value[0]}`);
554      systemTraceRow.childrenList.forEach((row): void => {
555        row.setAttribute(attributeKey, `${value[0]}`);
556      });
557      this.trace.refreshCanvas(false);
558    };
559  }
560
561  private frameNoExpandTimeOut(event: CustomEventInit<unknown>, frameTimeLineRow: TraceRow<JanksStruct>): number {
562    if (JankStruct!.selectJankStruct) {
563      JankStruct.selectJankStructList?.push(<JankStruct>JankStruct!.selectJankStruct);
564    }
565    let topPadding: number = 195;
566    let halfNumber: number = 2;
567    let offsetYTime: number = 300;
568    let refreshTime: number = 360;
569    let offsetYTimeOut: number = window.setTimeout(() => {
570      this.trace.linkNodes.forEach((linkNode: PairPoint[]) => {
571        if (linkNode[0].rowEL.collect) {
572          linkNode[0].rowEL.translateY = linkNode[0].rowEL.getBoundingClientRect().top - topPadding;
573        } else {
574          linkNode[0].rowEL.translateY = linkNode[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
575        }
576        linkNode[0].y = linkNode[0].rowEL!.translateY! + linkNode[0].offsetY;
577        if (linkNode[1].rowEL.collect) {
578          linkNode[1].rowEL.translateY = linkNode[1].rowEL.getBoundingClientRect().top - topPadding;
579        } else {
580          linkNode[1].rowEL.translateY = linkNode[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
581        }
582        linkNode[1].y = linkNode[1].rowEL!.translateY! + linkNode[1].offsetY; //@ts-ignore
583        if (linkNode[0].rowEL.rowParentId === event.detail?.rowId) {
584          if (!linkNode[0].rowEL.collect) {
585            linkNode[0].x = ns2xByTimeShaft(linkNode[0].ns, this.trace.timerShaftEL!);
586            linkNode[0].y = frameTimeLineRow!.translateY! + linkNode[0].offsetY / halfNumber;
587            linkNode[0].offsetY = linkNode[0].offsetY / halfNumber; //@ts-ignore
588            linkNode[0].rowEL = frameTimeLineRow;
589          } //@ts-ignore
590        } else if (linkNode[1].rowEL.rowParentId === event.detail?.rowId) {
591          if (!linkNode[1].rowEL.collect) {
592            linkNode[1].x = ns2xByTimeShaft(linkNode[1].ns, this.trace.timerShaftEL!);
593            linkNode[1].y = frameTimeLineRow!.translateY! + linkNode[1].offsetY / halfNumber;
594            linkNode[1].offsetY = linkNode[1].offsetY / halfNumber; //@ts-ignore
595            linkNode[1].rowEL = frameTimeLineRow!;
596          }
597        }
598      });
599    }, offsetYTime);
600    let refreshTimeOut: number = window.setTimeout(() => {
601      this.trace.refreshCanvas(true);
602      clearTimeout(refreshTimeOut);
603    }, refreshTime);
604    return offsetYTimeOut;
605  }
606
607  private frameExpandTimeOut(
608    event: CustomEventInit<{ expansion: boolean; rowType: string; rowId: string; rowParentId: string }>,
609    actualTimeLineRow: TraceRow<JanksStruct>
610  ): number {
611    let topPadding: number = 195;
612    let halfNumber: number = 2;
613    let offsetYTime: number = 300;
614    let refreshTime: number = 360;
615    let offsetYTimeOut: number = window.setTimeout(() => {
616      this.trace.linkNodes.forEach((linkFrameNode: PairPoint[]) => {
617        JankStruct.selectJankStructList?.forEach((dat: JankStruct) => {
618          if (event.detail?.rowId === dat.pid) {
619            JankStruct.selectJankStruct = dat;
620            JankStruct.hoverJankStruct = dat;
621          }
622        });
623        if (linkFrameNode[0].rowEL.collect) {
624          linkFrameNode[0].rowEL.translateY = linkFrameNode[0].rowEL.getBoundingClientRect().top - topPadding;
625        } else {
626          linkFrameNode[0].rowEL.translateY = linkFrameNode[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
627        }
628        linkFrameNode[0].y = linkFrameNode[0].rowEL!.translateY! + linkFrameNode[0].offsetY;
629        if (linkFrameNode[1].rowEL.collect) {
630          linkFrameNode[1].rowEL.translateY = linkFrameNode[1].rowEL.getBoundingClientRect().top - topPadding;
631        } else {
632          linkFrameNode[1].rowEL.translateY = linkFrameNode[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
633        }
634        linkFrameNode[1].y = linkFrameNode[1].rowEL!.translateY! + linkFrameNode[1].offsetY;
635        if (linkFrameNode[0].rowEL.rowId === event.detail?.rowId) {
636          linkFrameNode[0].x = ns2xByTimeShaft(linkFrameNode[0].ns, this.trace.timerShaftEL!);
637          linkFrameNode[0].y = actualTimeLineRow!.translateY! + linkFrameNode[0].offsetY * halfNumber;
638          linkFrameNode[0].offsetY = linkFrameNode[0].offsetY * halfNumber;
639          //@ts-ignore
640          linkFrameNode[0].rowEL = actualTimeLineRow;
641        } else if (linkFrameNode[1].rowEL.rowId === event.detail?.rowId) {
642          linkFrameNode[1].x = ns2xByTimeShaft(linkFrameNode[1].ns, this.trace.timerShaftEL!);
643          linkFrameNode[1].y = actualTimeLineRow!.translateY! + linkFrameNode[1].offsetY * halfNumber;
644          linkFrameNode[1].offsetY = linkFrameNode[1].offsetY * halfNumber;
645          //@ts-ignore
646          linkFrameNode[1].rowEL = actualTimeLineRow!;
647        }
648      });
649    }, offsetYTime);
650    let refreshTimeOut: number = window.setTimeout(() => {
651      this.trace.refreshCanvas(true);
652      clearTimeout(refreshTimeOut);
653    }, refreshTime);
654    return offsetYTimeOut;
655  }
656}
657