• 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 { BaseElement, element } from '../../../../base-ui/BaseElement';
17import '../../../../base-ui/checkbox/LitCheckBox';
18import { LitCheckBox } from '../../../../base-ui/checkbox/LitCheckBox';
19import { TraceRow } from './TraceRow';
20import { SpSystemTrace } from '../../SpSystemTrace';
21import { LitSearch } from '../search/Search';
22import { TraceSheet } from './TraceSheet';
23import { CpuStruct } from '../../../database/ui-worker/cpu/ProcedureWorkerCPU';
24import { type BaseStruct } from '../../../bean/BaseStruct';
25import { LitIcon } from '../../../../base-ui/icon/LitIcon';
26import { TraceRowConfigHtml } from './TraceRowConfig.html';
27
28@element('trace-row-config')
29export class TraceRowConfig extends BaseElement {
30  static allTraceRowList: Array<TraceRow<BaseStruct>> = [];
31  private selectTypeList: Array<string> | undefined = [];
32  private subsystemSelectList: Array<SceneNode> | undefined = [];
33  private spSystemTrace: SpSystemTrace | null | undefined;
34  private sceneTable: HTMLDivElement | null | undefined;
35  private chartTable: HTMLDivElement | null | undefined;
36  private inputElement: HTMLInputElement | null | undefined;
37  private configTitle: HTMLDivElement | null | undefined;
38  private resetButton: HTMLButtonElement | null | undefined;
39  private traceRowList: NodeListOf<TraceRow<BaseStruct>> | undefined;
40  private exportFileIcon: LitIcon | null | undefined;
41  private switchButton: LitIcon | null | undefined;
42  private openFileIcon: LitIcon | null | undefined;
43  private openTempFile: HTMLInputElement | null | undefined;
44  private treeNodes: SubsystemNode[] = [];
45  private expandedNodeList: Set<number> = new Set();
46  private tempString: string | null = null;
47  private subSystemSearch: string | undefined;
48  private backTableHTML: string | undefined;
49  private otherRowNames: Array<SceneNode> = [];
50  private configList: string = '';
51  private defaultConfigList: string = '';
52  private sceneList = [
53    'FrameTimeline',
54    'AnimationEffect',
55    'AppStartup',
56    'TaskPool',
57    'HiSysEvent',
58    'EnergyEvent',
59    'Memory',
60    'ProcessMemory',
61    'ArkTs',
62    'NativeMemory',
63    'HiPerf',
64    'HiEBpf',
65  ];
66
67  static get observedAttributes(): string[] {
68    return ['mode'];
69  }
70
71  init(): void {
72    this.selectTypeList = [];
73    this.subsystemSelectList = [];
74    this.otherRowNames = [];
75    this.inputElement!.value = '';
76    this.exportFileIcon!.style.display = 'none';
77    this.openFileIcon!.style.display = 'none';
78    this.resetButton!.style.display = 'none';
79    this.configTitle!.innerHTML = 'Timeline Details';
80    this.spSystemTrace = this.parentElement!.querySelector<SpSystemTrace>('sp-system-trace');
81    this.traceRowList =
82      this.spSystemTrace!.shadowRoot?.querySelector('div[class=rows-pane]')!.querySelectorAll<TraceRow<BaseStruct>>(
83        "trace-row[row-parent-id='']"
84      );
85    TraceRowConfig.allTraceRowList.push(...this.traceRowList!);
86    this.refreshAllConfig(true, true);
87    // 鼠标移入该页面,隐藏泳道图tip
88    this.onmouseenter = (): void => {
89      this.spSystemTrace!.tipEL!.style.display = 'none';
90      this.spSystemTrace!.hoverStructNull();
91      this.spSystemTrace!.refreshCanvas(true);
92    };
93  }
94
95  private refreshAllConfig(
96    isRefreshTopTable: boolean = false,
97    isRefreshBottomTable: boolean = false,
98    isSubSysConfig: boolean = false
99  ): void {
100    let allowSceneList: Array<string> = [];
101    this.selectTypeList = [];
102    this.subsystemSelectList = [];
103    this.otherRowNames = [];
104    this.switchButton!.title = 'Show subSystem template';
105    let topPanel = new DocumentFragment();
106    let bottomPanel = new DocumentFragment();
107    this.traceRowList!.forEach((traceRow: TraceRow<BaseStruct>) => {
108      traceRow.setAttribute('scene', '');
109      this.otherRowNames.push({
110        nodeName: traceRow.name,
111        scene: [...traceRow.templateType],
112      });
113      this.subsystemSelectList!.push({
114        nodeName: traceRow.name,
115        scene: [...traceRow.templateType],
116      });
117      if (isRefreshTopTable) {
118        this.sceneTable!.innerHTML = '';
119        if (traceRow.templateType.size > 0) {
120          traceRow.templateType.forEach((type) => {
121            if (this.sceneList.indexOf(type) >= 0 && allowSceneList.indexOf(type) < 0) {
122              allowSceneList.push(type);
123              this.initConfigSceneTable(type, topPanel);
124            }
125          });
126        }
127      }
128      if (isRefreshBottomTable) {
129        if (isSubSysConfig) {
130          this.backTableHTML = '';
131          this.chartTable!.innerHTML = '';
132        } else {
133          this.removeAttribute('temp_config');
134          this.backTableHTML = this.chartTable!.innerHTML;
135          this.chartTable!.innerHTML = '';
136          this.initConfigChartTable(traceRow, bottomPanel);
137        }
138      }
139    });
140    this.sceneTable?.appendChild(topPanel);
141    this.chartTable?.appendChild(bottomPanel);
142  }
143
144  initConfigSceneTable(item: string, topPanel: DocumentFragment): void {
145    let spliceIndex = 1;
146    let div = document.createElement('div');
147    div.className = 'scene-option-div';
148    div.textContent = item;
149    let optionCheckBox: LitCheckBox = new LitCheckBox();
150    optionCheckBox.checked = false;
151    optionCheckBox.style.justifySelf = 'center';
152    optionCheckBox.style.height = '100%';
153    optionCheckBox.title = item;
154    optionCheckBox.addEventListener('change', () => {
155      this.subsystemSelectList = [];
156      this.clearLines(optionCheckBox.title);
157      if (optionCheckBox.checked) {
158        this.selectTypeList!.push(item);
159      } else {
160        if (this.selectTypeList!.length > 0) {
161          let indexNum = this.selectTypeList!.indexOf(item);
162          this.selectTypeList!.splice(indexNum, spliceIndex);
163        }
164      }
165      this.resetChartOption();
166      this.resetChartTable();
167    });
168    let htmlDivElement = document.createElement('div');
169    htmlDivElement.style.display = 'grid';
170    htmlDivElement.style.gridTemplateColumns = '1fr 1fr';
171    htmlDivElement.appendChild(div);
172    htmlDivElement.appendChild(optionCheckBox);
173    topPanel.appendChild(htmlDivElement);
174  }
175
176  clearLines(type: string): void {
177    if (type === 'FrameTimeline' || type === 'AppStartup') {
178      this.spSystemTrace?.removeLinkLinesByBusinessType('janks');
179    } else if (type === 'Task Pool') {
180      this.spSystemTrace?.removeLinkLinesByBusinessType('task');
181    } else if (type === 'func') {
182      this.spSystemTrace?.removeLinkLinesByBusinessType('distributed');
183    }
184  }
185
186  initConfigChartTable(row: TraceRow<BaseStruct>, bottomPanel: DocumentFragment): void {
187    let templateType = '';
188    if (row.templateType.size > 0) {
189      templateType = [...row.templateType].reduce((pre, cur) => `${pre}:${cur}`);
190    }
191    let div = document.createElement('div');
192    div.className = 'chart-option-div chart-item';
193    div.textContent = row.name;
194    div.title = row.name;
195    div.setAttribute('search_text', row.name);
196    let optionCheckBox: LitCheckBox = new LitCheckBox();
197    optionCheckBox.checked = true;
198    optionCheckBox.className = 'chart-config-check chart-item';
199    optionCheckBox.style.height = '100%';
200    optionCheckBox.style.justifySelf = 'center';
201    optionCheckBox.title = templateType;
202    optionCheckBox.setAttribute('search_text', row.name);
203    optionCheckBox.addEventListener('change', () => {
204      if (row.folder) {
205        TraceRowConfig.allTraceRowList.forEach((chartRow): void => {
206          let upParentRow = chartRow;
207          while (upParentRow.hasParentRowEl) {
208            if (!upParentRow.parentRowEl) {
209              break;
210            } // @ts-ignore
211            upParentRow = upParentRow.parentRowEl;
212          }
213          if (upParentRow === row) {
214            if (optionCheckBox.checked) {
215              chartRow.rowHidden = false;
216              chartRow.setAttribute('scene', '');
217            } else {
218              row.expansion = false;
219              chartRow.removeAttribute('scene');
220              chartRow.rowHidden = true;
221            }
222          }
223        });
224      }
225      if (optionCheckBox.checked) {
226        row.rowHidden = false;
227        row.setAttribute('scene', '');
228      } else {
229        row.removeAttribute('scene');
230        row.rowHidden = true;
231      }
232      this.refreshSystemPanel();
233    });
234    bottomPanel.append(...[div, optionCheckBox]);
235  }
236
237  resetChartOption(): void {
238    this.shadowRoot!.querySelectorAll<LitCheckBox>('.chart-item').forEach((litCheckBox: LitCheckBox) => {
239      let isShowCheck: boolean = false;
240      if (this.selectTypeList!.length === 0) {
241        isShowCheck = true;
242      } else {
243        if (litCheckBox.title !== '') {
244          let divTemplateTypeList = litCheckBox.title.split(':');
245          for (let index = 0; index < divTemplateTypeList.length; index++) {
246            let type = divTemplateTypeList[index];
247            if (this.selectTypeList!.indexOf(type) >= 0) {
248              isShowCheck = true;
249              break;
250            }
251          }
252        }
253      }
254      litCheckBox.checked = isShowCheck;
255    });
256    if (this.hasAttribute('temp_config')) {
257      this.refreshNodes(this.treeNodes);
258      this.refreshSelectList(this.treeNodes);
259      this.refreshTable();
260    }
261  }
262
263  refreshSelectList(nodes: SubsystemNode[]): void {
264    nodes.forEach((item) => {
265      if (item.depth === 3) {
266        if (item.isCheck) {
267          this.subsystemSelectList!.push({
268            nodeName: item.nodeName,
269            scene: item.scene,
270          });
271        }
272      }
273      if (item.children.length > 0) {
274        this.refreshSelectList(item.children);
275      }
276    });
277  }
278
279  resetChartTable(): void {
280    if (this.traceRowList && this.traceRowList.length > 0) {
281      this.traceRowList.forEach((traceRow: TraceRow<BaseStruct>) => {
282        let isShowRow: boolean = false;
283        if (this.selectTypeList!.length === 0) {
284          traceRow.rowHidden = false;
285          traceRow.setAttribute('scene', ''); // @ts-ignore
286          this.refreshChildRow(traceRow.childrenList, true);
287        } else {
288          let templateTypeList = [...traceRow.templateType];
289          isShowRow = templateTypeList.some((type) => this.selectTypeList!.includes(type));
290          traceRow.expansion = false;
291          if (isShowRow) {
292            if (traceRow.templateType.size > 0) {
293              traceRow.rowHidden = false;
294              traceRow.setAttribute('scene', '');
295              if (traceRow.childrenList && traceRow.childrenList.length > 0) {
296                // @ts-ignore
297                this.refreshChildRow(traceRow.childrenList, isShowRow);
298              }
299            }
300          } else {
301            traceRow.removeAttribute('scene');
302            traceRow.rowHidden = true; // @ts-ignore
303            this.refreshChildRow(traceRow.childrenList);
304          }
305        }
306      });
307      this.handleCollectRow();
308    }
309    this.refreshSystemPanel();
310  }
311
312  private handleCollectRow(): void {
313    this.spSystemTrace?.collectRows.forEach((favoriteRow) => {
314      let isShowRow: boolean = false;
315      if (this.selectTypeList!.length === 0) {
316        favoriteRow.rowHidden = false;
317        favoriteRow.setAttribute('scene', '');
318      } else {
319        if (favoriteRow.parentRowEl) {
320          favoriteRow.parentRowEl.expansion = false;
321          let favoriteList = [...favoriteRow.parentRowEl!.templateType];
322          isShowRow = favoriteList.some((type) => this.selectTypeList!.includes(type));
323        } else {
324          let typeList = [...favoriteRow.templateType];
325          isShowRow = typeList.some((type) => this.selectTypeList!.includes(type));
326        }
327        if (isShowRow) {
328          favoriteRow.rowHidden = false;
329          favoriteRow.setAttribute('scene', '');
330        } else {
331          favoriteRow.removeAttribute('scene');
332          favoriteRow.rowHidden = true;
333          if (this.spSystemTrace?.favoriteChartListEL?.scrollTop && this.spSystemTrace?.favoriteChartListEL?.scrollTop > 0) {
334            this.spSystemTrace.favoriteChartListEL.scrollTop = 0;
335          }
336        }
337      }
338    });
339  }
340
341  refreshNodes(nodes: SubsystemNode[]): void {
342    if (this.selectTypeList?.length !== 0) {
343      for (let index = 0; index < nodes.length; index++) {
344        let item = nodes[index];
345        let exists = false;
346        item.scene?.forEach((sceneItem) => {
347          if (this.selectTypeList!.some((elem) => elem === sceneItem)) {
348            exists = true;
349            return;
350          }
351        });
352        if (exists) {
353          item.isCheck = true;
354        } else {
355          item.isCheck = false;
356        }
357        this.refreshNodes(item.children);
358      }
359    } else {
360      for (let index = 0; index < nodes.length; index++) {
361        let item = nodes[index];
362        item.isCheck = true;
363        this.refreshNodes(item.children);
364      }
365    }
366  }
367
368  refreshChildRow(childRows: Array<TraceRow<BaseStruct>>, isShowScene: boolean = false): void {
369    childRows.forEach((row) => {
370      if (isShowScene) {
371        row.setAttribute('scene', '');
372        if (row.childrenList && row.childrenList.length > 0) {
373          // @ts-ignore
374          this.refreshChildRow(row.childrenList, isShowScene);
375        }
376        row.expansion = false;
377      } else {
378        row.removeAttribute('scene');
379        row.rowHidden = true;
380        if (row.childrenList && row.childrenList.length > 0) {
381          // @ts-ignore
382          this.refreshChildRow(row.childrenList);
383        }
384      }
385    });
386  }
387
388  refreshSystemPanel(): void {
389    this.clearSearchAndFlag();
390    this.spSystemTrace!.rowsPaneEL!.scroll({
391      top: 0 - this.spSystemTrace!.canvasPanel!.offsetHeight,
392      left: 0,
393      behavior: 'smooth',
394    });
395    this.spSystemTrace!.refreshFavoriteCanvas();
396    this.spSystemTrace!.refreshCanvas(true);
397  }
398
399  clearSearchAndFlag(): void {
400    let traceSheet = this.spSystemTrace!.shadowRoot?.querySelector('.trace-sheet') as TraceSheet;
401    if (traceSheet) {
402      traceSheet!.setMode('hidden');
403    }
404    let search = document.querySelector('sp-application')!.shadowRoot?.querySelector('#lit-search') as LitSearch;
405    if (search) {
406      search.clear();
407    }
408    let highlightRow = this.spSystemTrace!.shadowRoot?.querySelector<TraceRow<BaseStruct>>('trace-row[highlight]');
409    if (highlightRow) {
410      highlightRow.highlight = false;
411    }
412    this.spSystemTrace!.timerShaftEL?.removeTriangle('inverted');
413    CpuStruct.wakeupBean = undefined;
414    this.spSystemTrace!.hoverFlag = undefined;
415    this.spSystemTrace!.selectFlag = undefined;
416  }
417
418  initElements(): void {
419    this.sceneTable = this.shadowRoot!.querySelector<HTMLDivElement>('#scene-select');
420    this.chartTable = this.shadowRoot!.querySelector<HTMLDivElement>('#chart-select');
421    this.inputElement = this.shadowRoot!.querySelector('input');
422    this.openTempFile = this.shadowRoot?.querySelector<HTMLInputElement>('#open-temp-file');
423    this.exportFileIcon = this.shadowRoot?.querySelector<LitIcon>('#export-file-icon');
424    this.switchButton = this.shadowRoot?.querySelector<LitIcon>('#switch-button');
425    this.openFileIcon = this.shadowRoot?.querySelector<LitIcon>('#open-file-icon');
426    this.configTitle = this.shadowRoot?.querySelector<HTMLDivElement>('#config_title');
427    this.resetButton = this.shadowRoot?.querySelector<HTMLButtonElement>('#resetTemplate');
428    this.initSwitchClickListener();
429    this.openFileIcon!.addEventListener('click', () => {
430      this.openTempFile!.value = '';
431      this.openTempFile?.click();
432    });
433    this.resetButton!.addEventListener('click', () => {
434      this.loadTempConfig(this.defaultConfigList);
435      this.resetChartTable();
436    });
437    this.inputElement?.addEventListener('keypress',(e) => {
438      e.stopPropagation();
439    })
440  }
441
442  private initSwitchClickListener(): void {
443    let jsonUrl = `https://${window.location.host.split(':')[0]}:${window.location.port
444      }${window.location.pathname}trace/config/custom_temp_config.json`;
445    this.switchButton!.addEventListener('click', () => {
446      // @ts-ignore
447      this.inputElement?.value = '';
448      if (this.switchButton!.title === 'Show charts template') {
449        this.switchButton!.title = 'Show subSystem template';
450        this.refreshAllConfig(true, true);
451        this.resetChartTable();
452        this.openFileIcon!.style.display = 'none';
453        this.exportFileIcon!.style.display = 'none';
454        this.configTitle!.innerHTML = 'Timeline Details';
455        this.resetButton!.style.display = 'none';
456      } else {
457        this.switchButton!.title = 'Show charts template';
458        this.openFileIcon!.style.display = 'block';
459        this.exportFileIcon!.style.display = 'block';
460        this.resetButton!.style.display = 'block';
461        this.configTitle!.innerHTML = 'SubSystem Template';
462        if (this.configList) {
463          this.loadTempConfig(this.configList);
464        } else {
465          if (this.defaultConfigList === '') {
466            fetch(jsonUrl)
467              .then((res) => {
468                if (res.ok) {
469                  res.text().then((text) => {
470                    this.defaultConfigList = text;
471                    this.loadTempConfig(this.defaultConfigList);
472                  });
473                }
474              })
475            ['catch']((err) => {
476              console.log(err);
477            });
478          } else {
479            this.loadTempConfig(this.defaultConfigList);
480          }
481        }
482        this.resetChartTable();
483      }
484    });
485  }
486
487  private filterSearch(): void {
488    this.shadowRoot!.querySelectorAll<HTMLElement>('.temp-chart-item').forEach((subSystemOption: HTMLElement) => {
489      this.subSystemSearch = subSystemOption.getAttribute('search_text') || '';
490      if (this.subSystemSearch!.toLowerCase().indexOf(this.inputElement!.value.toLowerCase()) < 0) {
491        subSystemOption.style.display = 'none';
492      } else {
493        subSystemOption.style.display = 'grid';
494      }
495    });
496  }
497
498  connectedCallback(): void {
499    this.inputElement?.addEventListener('keyup', (e) => {
500      e.stopPropagation();
501      this.shadowRoot!.querySelectorAll<HTMLElement>('.chart-item').forEach((elementOption: HTMLElement) => {
502        let searchText = elementOption.getAttribute('search_text') || '';
503        if (searchText!.toLowerCase().indexOf(this.inputElement!.value.toLowerCase()) < 0) {
504          elementOption.style.display = 'none';
505        } else {
506          elementOption.style.display = 'block';
507        }
508      });
509      this.filterSearch();
510    });
511    this.openTempFile!.addEventListener('change', (event) => {
512      let that = this;
513      let fileList = (event.target as HTMLInputElement).files;
514      if (fileList && fileList.length > 0) {
515        let file = fileList[0];
516        if (file) {
517          let reader = new FileReader();
518          reader.onload = (): void => {
519            that.loadTempConfig(reader.result as string);
520          };
521          reader.readAsText(file);
522        }
523      }
524    });
525    this.exportFileIcon!.addEventListener('click', () => {
526      this.exportConfig();
527    });
528  }
529
530  exportConfig(): void {
531    let a = document.createElement('a');
532    let encoder = new TextEncoder();
533    let tempBuffer = encoder.encode(this.tempString!);
534    a.href = URL.createObjectURL(new Blob([tempBuffer]));
535    a.download = 'custom_temp_config';
536    a.click();
537    window.URL.revokeObjectURL(a.href);
538  }
539
540  loadTempConfig(text: string): void {
541    this.selectTypeList = [];
542    this.inputElement!.value = '';
543    this.backTableHTML = this.chartTable?.innerHTML;
544    let configJson;
545    let isTrulyJson = false;
546    try {
547      configJson = JSON.parse(text);
548      let subsystemsKey: string = 'subsystems';
549      if (configJson[subsystemsKey]) {
550        isTrulyJson = true;
551        this.tempString = text;
552        this.openFileIcon!.style.display = 'block';
553      }
554    } catch (e) {
555      console.log(e);
556    }
557    if (!isTrulyJson) {
558      return;
559    }
560    let id = 0;
561    try {
562      this.treeNodes = this.buildSubSystemTreeData(id, configJson);
563    } catch (e) {
564      this.loadTempConfig(this.configList);
565      return;
566    }
567    this.configList = text;
568    this.buildTempOtherList(id);
569    this.setAttribute('temp_config', '');
570    this.expandedNodeList.clear();
571    this.refreshTable();
572  }
573
574  // 构建节点关系
575  private buildSubSystemTreeData(id: number, configJson: unknown): SubsystemNode[] {
576    let subsystemsKey: string = 'subsystems'; // @ts-ignore
577    let keys = Object.keys(configJson);
578    if (keys.indexOf(subsystemsKey) < 0) {
579      return [];
580    }
581    let subSystems: SubsystemNode[] = []; // @ts-ignore
582    let subsystemsData = configJson[subsystemsKey];
583    this.initOtherRowNames();
584    let subsystemList = [];
585    for (let subIndex = 0; subIndex < subsystemsData.length; subIndex++) {
586      let currentSystemData = subsystemsData[subIndex];
587      if (
588        !currentSystemData.subsystem || currentSystemData.subsystem === '' ||
589        subsystemList.indexOf(currentSystemData.subsystem) > -1 ||
590        Array.isArray(currentSystemData.subsystem)
591      ) {
592        continue;
593      }
594      let currentSubName = currentSystemData.subsystem;
595      subsystemList.push(currentSystemData.subsystem);
596      id++;
597      let subsystemStruct: SubsystemNode = {
598        id: id,
599        nodeName: currentSubName,
600        children: [],
601        depth: 1,
602        isCheck: true,
603        scene: [],
604      };
605      if (subSystems.indexOf(subsystemStruct) < 0) {
606        let currentCompDates = currentSystemData.components;
607        if (!currentCompDates || !Array.isArray(currentCompDates)) {
608          continue;
609        }
610        for (let compIndex = 0; compIndex < currentCompDates.length; compIndex++) {
611          let currentCompDate = currentCompDates[compIndex];
612          if (!currentCompDate.component || currentCompDate.component === '' || !currentCompDate.charts) {
613            continue;
614          }
615          id = this.setSubsystemComp(currentCompDate, id, subsystemStruct);
616        }
617        subSystems.push(subsystemStruct);
618      }
619    }
620    return subSystems;
621  }
622
623  private initOtherRowNames(): void {
624    if (this.traceRowList) {
625      this.otherRowNames = [];
626      for (let index = 0; index < this.traceRowList.length; index++) {
627        let item = this.traceRowList[index];
628        this.otherRowNames.push({
629          nodeName: item.name,
630          scene: [...item.templateType],
631        });
632      }
633    }
634  }
635
636  private setSubsystemComp(currentCompDate: unknown, id: number, subsystemStruct: SubsystemNode): number {
637    // @ts-ignore
638    let currentCompName = currentCompDate.component; // @ts-ignore
639    let currentChartDates = currentCompDate.charts; // @ts-ignore
640    id++;
641    let componentStruct: SubsystemNode = {
642      id: id,
643      parent: subsystemStruct,
644      nodeName: currentCompName,
645      children: [],
646      depth: 2,
647      isCheck: true,
648      scene: [],
649    };
650    for (let chartIndex = 0; chartIndex < currentChartDates.length; chartIndex++) {
651      let currentChartDate = currentChartDates[chartIndex];
652      if (
653        (!currentChartDate.chartName && !currentChartDate.chartId) ||
654        Array.isArray(currentChartDate.chartName)
655      ) {
656        continue;
657      }
658      let currentChartName = `${currentChartDate.chartName}`;
659      let currentChartId = currentChartDate.chartId;
660      let findChartNames: Array<string> | undefined = [];
661      let scene: string[] = [];
662      this.setSubsystemChart(currentChartName, currentChartId, scene, findChartNames);
663      findChartNames.forEach((currentChartName) => {
664        id++;
665        let chartStruct: SubsystemNode = {
666          id: id,
667          parent: componentStruct,
668          nodeName: currentChartName,
669          children: [],
670          depth: 3,
671          isCheck: true,
672          scene: scene,
673        };
674        if (componentStruct.children.indexOf(chartStruct) < 0) {
675          let rowNumber = this.otherRowNames.findIndex((row) => row.nodeName === chartStruct.nodeName);
676          if (rowNumber >= 0) {
677            this.otherRowNames.splice(rowNumber, 1);
678          }
679          componentStruct.children.push(chartStruct);
680        }
681      });
682    }
683    if (subsystemStruct.children.indexOf(componentStruct) < 0) {
684      subsystemStruct.children.push(componentStruct);
685    }
686    return id;
687  }
688
689  private setSubsystemChart(
690    currentChartName: string,
691    currentChartId: string,
692    scene: Array<string>,
693    findChartNames: Array<string>
694  ): void {
695    if (this.traceRowList) {
696      for (let index = 0; index < this.traceRowList.length; index++) {
697        let item = this.traceRowList[index];
698        let chartId = '';
699        let name = item.name;
700        let pattern = / (\d+)$/;
701        let match = item.name.match(pattern);
702        if (match) {
703          chartId = match[0].trim();
704          name = item.name.split(match[0])[0];
705          if (name !== 'Cpu') {
706            if (
707              (currentChartName !== undefined &&
708                currentChartName !== '' &&
709                name.toLowerCase().endsWith(currentChartName.toLowerCase())) ||
710              currentChartId === chartId
711            ) {
712              scene.push(...item.templateType);
713              findChartNames.push(item.name);
714            }
715          } else {
716            if (
717              currentChartName !== undefined &&
718              currentChartName !== '' &&
719              name.toLowerCase().endsWith(currentChartName.toLowerCase())
720            ) {
721              scene.push(...item.templateType);
722              findChartNames.push(item.name);
723            }
724          }
725        } else {
726          if (
727            currentChartName !== undefined &&
728            currentChartName !== '' &&
729            name.toLowerCase().endsWith(currentChartName.toLowerCase())
730          ) {
731            scene.push(...item.templateType);
732            findChartNames.push(item.name);
733          }
734        }
735      }
736    }
737  }
738
739  refreshTable(): void {
740    this.chartTable!.innerHTML = '';
741    for (let index = 0; index < this.treeNodes.length; index++) {
742      this.buildSubsystem(this.treeNodes[index]);
743    }
744    this.filterSearch();
745  }
746
747  buildSubsystem(subsystemNode: SubsystemNode): void {
748    let subsystemDiv = document.createElement('div');
749    subsystemDiv.className = 'layout temp-chart-item';
750    subsystemDiv.title = subsystemNode.nodeName!;
751    subsystemDiv.setAttribute('search_text', subsystemNode.nodeName!);
752    if (subsystemNode.scene) {
753      subsystemDiv.title = subsystemNode.scene.toString();
754    }
755    if (subsystemNode.depth !== 3) {
756      let container = document.createElement('div');
757      container.style.display = 'flex';
758      container.style.marginLeft = `${subsystemNode.depth * 25}px`;
759      container.style.alignItems = 'center';
760      let expandIcon = document.createElement('lit-icon') as LitIcon;
761      expandIcon.name = 'caret-down';
762      expandIcon.className = 'expand-icon';
763      if (this.expandedNodeList.has(subsystemNode.id)) {
764        expandIcon.setAttribute('expansion', '');
765      } else {
766        expandIcon.removeAttribute('expansion');
767      }
768      expandIcon.addEventListener('click', () => {
769        this.changeNode(subsystemNode.id);
770        this.refreshTable();
771      });
772      let componentDiv = document.createElement('div');
773      componentDiv.className = 'subsystem-div';
774      componentDiv.textContent = subsystemNode.nodeName!;
775      container.appendChild(expandIcon);
776      container.appendChild(componentDiv);
777      subsystemDiv.appendChild(container);
778    } else {
779      let chartDiv = document.createElement('div');
780      chartDiv.className = 'chart-option';
781      chartDiv.textContent = subsystemNode.nodeName!;
782      subsystemDiv.appendChild(chartDiv);
783    }
784    let configCheckBox: LitCheckBox = new LitCheckBox();
785    configCheckBox.className = 'scene-check-box temp-chart-item';
786    configCheckBox.setAttribute('search_text', subsystemNode.nodeName!);
787    this.buildCheckBox(configCheckBox, subsystemNode);
788    subsystemDiv.appendChild(configCheckBox);
789    this.chartTable?.appendChild(subsystemDiv);
790    if (subsystemNode.children && this.expandedNodeList.has(subsystemNode.id)) {
791      subsystemNode.children.forEach((item) => {
792        this.buildSubsystem(item);
793      });
794    }
795  }
796
797  private buildCheckBox(configCheckBox: LitCheckBox, subsystemNode: SubsystemNode): void {
798    if (subsystemNode.scene) {
799      configCheckBox.title = subsystemNode.scene.toString();
800    }
801    configCheckBox.checked = subsystemNode.isCheck!;
802    configCheckBox.addEventListener('change', () => {
803      this.spSystemTrace?.removeLinkLinesByBusinessType('janks');
804      this.spSystemTrace?.removeLinkLinesByBusinessType('task');
805      this.setChildIsSelect(subsystemNode, configCheckBox);
806      this.setParentSelect(subsystemNode, configCheckBox.checked);
807      this.refreshTable();
808      this.displayRow(subsystemNode, configCheckBox);
809      // 收藏后的泳道的展示或者隐藏
810      this.spSystemTrace?.collectRows.forEach((subsystemFavorite) => {
811        let isShowRow: boolean = false;
812        let favoriteName = '';
813        if (this.subsystemSelectList!.length === 0) {
814          subsystemFavorite.removeAttribute('scene');
815          subsystemFavorite.rowHidden = true;
816        } else {
817          if (subsystemFavorite.parentRowEl) {
818            subsystemFavorite.parentRowEl.expansion = false;
819            favoriteName = subsystemFavorite.parentRowEl!.name;
820            // 三级泳道判断
821            if (subsystemFavorite.parentRowEl.parentRowEl) {
822              subsystemFavorite.parentRowEl.parentRowEl.expansion = false;
823              favoriteName = subsystemFavorite.parentRowEl.parentRowEl.name;
824            }
825            for (let i = 0; i < this.subsystemSelectList!.length; i++) {
826              if (this.subsystemSelectList![i].nodeName === favoriteName) {
827                isShowRow = true;
828                break;
829              }
830            }
831          } else {
832            favoriteName = subsystemFavorite.name;
833            for (let i = 0; i < this.subsystemSelectList!.length; i++) {
834              if (this.subsystemSelectList![i].nodeName === favoriteName) {
835                isShowRow = true;
836                break;
837              }
838            }
839          }
840          if (isShowRow) {
841            subsystemFavorite.rowHidden = false;
842            subsystemFavorite.setAttribute('scene', '');
843          } else {
844            subsystemFavorite.removeAttribute('scene');
845            subsystemFavorite.rowHidden = true;
846          }
847        }
848      });
849      this.spSystemTrace!.favoriteChartListEL!.scroll({
850        top: 0,
851        left: 0,
852        behavior: 'smooth',
853      });
854      this.refreshSystemPanel();
855    });
856  }
857
858  private buildTempOtherList(id: number): void {
859    let otherRootNode: SubsystemNode = {
860      children: [],
861      depth: 1,
862      id: id,
863      nodeName: 'other',
864      isCheck: true,
865      scene: [],
866    };
867    for (let index = 0; index < this.otherRowNames!.length; index++) {
868      otherRootNode.children.push({
869        children: [],
870        depth: 3,
871        id: id++,
872        nodeName: this.otherRowNames![index].nodeName,
873        isCheck: true,
874        parent: otherRootNode,
875        scene: this.otherRowNames![index].scene,
876      });
877    }
878    this.treeNodes.push(otherRootNode);
879  }
880
881  private setChildIsSelect(node: SubsystemNode, configCheckBox: LitCheckBox): void {
882    node.isCheck = configCheckBox.checked;
883    if (node.children.length > 0) {
884      node.children.forEach((childItem) => {
885        this.displayRow(childItem, configCheckBox);
886        this.setChildIsSelect(childItem, configCheckBox);
887      });
888    }
889  }
890
891  private displayRow(node: SubsystemNode, configCheckBox: LitCheckBox): void {
892    if (node.depth === 3) {
893      let chartNumber = this.subsystemSelectList?.findIndex((item) => item.nodeName === node.nodeName!);
894      if (configCheckBox.checked) {
895        if (chartNumber === -1) {
896          this.subsystemSelectList?.push({
897            nodeName: node.nodeName!,
898            scene: configCheckBox.title.split(','),
899          });
900        }
901      } else {
902        if (chartNumber !== undefined && chartNumber !== null) {
903          this.subsystemSelectList?.splice(chartNumber, 1);
904        }
905      }
906      this.traceRowList?.forEach((item) => {
907        if (item.name === node.nodeName) {
908          if (configCheckBox.checked) {
909            item.setAttribute('scene', '');
910            item.removeAttribute('row-hidden');
911            item.childrenList.forEach(v => {
912              if (v.hasAttribute('row-hidden')) {
913                v.setAttribute('scene', '');
914                v.removeAttribute('row-hidden');
915              }
916            });
917          } else {
918            item.expansion = false;
919            item.removeAttribute('scene');
920            item.setAttribute('row-hidden', '');
921          }
922        }
923      });
924    }
925  }
926
927  private setParentSelect(node: SubsystemNode, isSelect: boolean): void {
928    if (node.parent) {
929      if (isSelect) {
930        node.parent.isCheck = isSelect;
931      } else {
932        let isParentCheck = false;
933        for (let index = 0; index < node.parent!.children.length; index++) {
934          let childItem = node.parent!.children[index];
935          if (childItem.isCheck) {
936            isParentCheck = true;
937            break;
938          }
939        }
940        node.parent.isCheck = isParentCheck;
941      }
942      if (node.parent.parent) {
943        this.setParentSelect(node.parent, isSelect);
944      }
945    }
946  }
947
948  private changeNode(currentNode: number): void {
949    if (this.expandedNodeList.has(currentNode)) {
950      this.expandedNodeList['delete'](currentNode);
951    } else {
952      this.expandedNodeList.add(currentNode);
953    }
954    this.refreshTable();
955  }
956
957  initHtml(): string {
958    return TraceRowConfigHtml;
959  }
960
961  attributeChangedCallback(name: string, oldValue: string, newValue: string): void {
962    if (name === 'mode' && newValue === '') {
963      this.init();
964    }
965  }
966}
967
968export interface SubsystemNode {
969  id: number;
970  parent?: SubsystemNode;
971  nodeName: string | undefined | null;
972  children: SubsystemNode[];
973  depth: number;
974  isCheck?: boolean;
975  scene?: string[];
976}
977
978export interface SceneNode {
979  nodeName: string | undefined | null;
980  scene?: string[];
981}
982