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