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