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