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