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 { SelectionParam } from '../../../../bean/BoxSelection'; 18import { LitIcon } from '../../../../../base-ui/icon/LitIcon'; 19import { HiSysEventStruct } from '../../../../database/ui-worker/ProcedureWorkerHiSysEvent'; 20import { LitTable } from '../../../../../base-ui/table/lit-table'; 21import { ColorUtils } from '../../base/ColorUtils'; 22import { TabPaneHiSysEventSummaryHtml } from './TabPaneHiSysEventSummary.html'; 23import { NUM_30, NUM_40 } from '../../../../bean/NumBean'; 24import { queryHiSysEventTabData } from '../../../../database/sql/Perf.sql'; 25 26@element('tab-hi-sysevent-summary') 27export class TabPaneHiSysEventSummary extends BaseElement { 28 private summarySource: Array<HiSysEventStruct> = []; 29 private eventSummaryTable: HTMLDivElement | undefined | null; 30 private summaryTable: LitTable | undefined | null; 31 private eventTreeNodes: HiSysEventTreeNode[] = []; 32 private expansionDiv: HTMLDivElement | undefined | null; 33 private expansionUpIcon: LitIcon | undefined | null; 34 private expansionDownIcon: LitIcon | undefined | null; 35 private expandedNodeList: Set<number> = new Set(); 36 private selectTreeDepth: number = 0; 37 private currentSelection: SelectionParam | undefined; 38 39 set data(systemEventParam: SelectionParam) { 40 if (systemEventParam === this.currentSelection) { 41 return; 42 } 43 this.summarySource = []; 44 this.expandedNodeList.clear(); 45 this.expansionUpIcon!.name = 'up'; 46 this.expansionDownIcon!.name = 'down'; 47 this.eventSummaryTable!.innerHTML = ''; 48 this.summaryTable!.recycleDataSource = []; 49 queryHiSysEventTabData(systemEventParam.leftNs, systemEventParam.rightNs).then((res) => { 50 this.currentSelection = systemEventParam; 51 systemEventParam.sysAllEventsData = res; 52 this.summarySource = systemEventParam.sysAllEventsData; 53 this.summaryTable!.recycleDataSource = res; 54 this.refreshRowNodeTable(); 55 }); 56 if (this.summarySource?.length !== 0 && systemEventParam) { 57 this.refreshRowNodeTable(); 58 } 59 } 60 61 initElements(): void { 62 this.eventSummaryTable = this.shadowRoot?.querySelector<HTMLDivElement>('#tab-summary'); 63 this.summaryTable = this.shadowRoot?.querySelector<LitTable>('#tb-event-summary'); 64 this.expansionDiv = this.shadowRoot?.querySelector<HTMLDivElement>('.expansion-div'); 65 this.expansionUpIcon = this.shadowRoot?.querySelector<LitIcon>('.expansion-up-icon'); 66 this.expansionDownIcon = this.shadowRoot?.querySelector<LitIcon>('.expansion-down-icon'); 67 let summaryTreeLevel: string[] = ['Level', '/Domain', '/EventName']; 68 this.shadowRoot?.querySelectorAll<HTMLLabelElement>('.head-label').forEach((summaryTreeHead) => { 69 summaryTreeHead.addEventListener('click', () => { 70 this.selectTreeDepth = summaryTreeLevel.indexOf(summaryTreeHead.textContent!); 71 this.expandedNodeList.clear(); 72 this.refreshSelectDepth(this.eventTreeNodes); 73 this.refreshRowNodeTable(true); 74 }); 75 }); 76 this.eventSummaryTable?.addEventListener('scroll', () => { 77 let treeTableEl = this.shadowRoot?.querySelector<HTMLDivElement>('.event-tree-table'); 78 if (treeTableEl) { 79 treeTableEl.scrollTop = this.eventSummaryTable?.scrollTop || 0; 80 } 81 }); 82 } 83 84 initHtml(): string { 85 return TabPaneHiSysEventSummaryHtml; 86 } 87 88 connectedCallback(): void { 89 super.connectedCallback(); 90 new ResizeObserver(() => { 91 this.parentElement!.style.overflow = 'hidden'; 92 this.refreshRowNodeTable(); 93 }).observe(this.parentElement!); 94 this.expansionDiv?.addEventListener('click', this.expansionClickEvent); 95 } 96 97 expansionClickEvent = (): void => { 98 this.expandedNodeList.clear(); 99 if (this.expansionUpIcon?.name === 'down') { 100 this.selectTreeDepth = 0; 101 this.expansionUpIcon!.name = 'up'; 102 this.expansionDownIcon!.name = 'down'; 103 } else { 104 this.selectTreeDepth = 2; 105 this.expansionUpIcon!.name = 'down'; 106 this.expansionDownIcon!.name = 'up'; 107 } 108 this.refreshSelectDepth(this.eventTreeNodes); 109 this.refreshRowNodeTable(true); 110 }; 111 112 disconnectedCallback(): void { 113 super.disconnectedCallback(); 114 this.expansionDiv?.removeEventListener('click', this.expansionClickEvent); 115 } 116 117 private refreshSelectDepth(eventTreeNodes: HiSysEventTreeNode[]): void { 118 eventTreeNodes.forEach((item) => { 119 if (item.depth < this.selectTreeDepth) { 120 this.expandedNodeList.add(item.id); 121 if (item.children.length > 0) { 122 this.refreshSelectDepth(item.children); 123 } 124 } 125 }); 126 } 127 128 private createRowNodeTableEL( 129 rowNodeList: HiSysEventTreeNode[], 130 tableTreeEl: HTMLDivElement, 131 tableCountEl: HTMLDivElement, 132 rowColor: string = '' 133 ): void { 134 let unitPadding: number = 20; 135 let leftPadding: number = 5; 136 rowNodeList.forEach((rowNode) => { 137 let tableRowEl: HTMLElement = document.createElement('tr'); 138 tableRowEl.className = 'tree-row-tr'; 139 tableRowEl.title = rowNode.summaryName!; 140 let tdEl: HTMLElement = document.createElement('td'); 141 tdEl.style.paddingLeft = `${rowNode.depth * unitPadding + leftPadding}px`; 142 tableRowEl.appendChild(tdEl); 143 this.addToggleIconEl(rowNode, tableRowEl); 144 let rowNodeTextEL: HTMLElement = document.createElement('td'); 145 rowNodeTextEL.textContent = rowNode.summaryName!; 146 rowNodeTextEL.className = 'row-name-td'; 147 tableRowEl.appendChild(rowNodeTextEL); 148 tableTreeEl.appendChild(tableRowEl); 149 let tableCountRowEl: HTMLElement = document.createElement('tr'); 150 let countEle: HTMLElement = document.createElement('td'); 151 countEle.textContent = rowNode.count.toString(); 152 countEle.className = 'count-column-td'; 153 if (rowNode.depth === 0) { 154 rowNodeTextEL.style.color = ColorUtils.getHisysEventColor(rowNode.summaryName!); 155 countEle.style.color = ColorUtils.getHisysEventColor(rowNode.summaryName!); 156 } else { 157 rowNodeTextEL.style.color = rowColor; 158 countEle.style.color = rowColor; 159 } 160 tableCountRowEl.appendChild(countEle); 161 tableCountEl.appendChild(tableCountRowEl); 162 if (rowNode.children && this.expandedNodeList.has(rowNode.id)) { 163 this.createRowNodeTableEL(rowNode.children, tableTreeEl, tableCountEl, countEle.style.color); 164 } 165 }); 166 } 167 168 private addToggleIconEl(rowNode: HiSysEventTreeNode, tableRowEl: HTMLElement): void { 169 let toggleIconEl: HTMLElement = document.createElement('td'); 170 let expandIcon = document.createElement('lit-icon'); 171 expandIcon.classList.add('tree-icon'); 172 if (rowNode.children && rowNode.children.length > 0) { 173 toggleIconEl.appendChild(expandIcon); 174 // @ts-ignore 175 expandIcon.name = this.expandedNodeList.has(rowNode.id) ? 'minus-square' : 'plus-square'; 176 toggleIconEl.classList.add('expand-icon'); 177 toggleIconEl.addEventListener('click', () => { 178 let scrollTop = this.eventSummaryTable?.scrollTop ?? 0; 179 this.changeNode(rowNode.id); 180 this.eventSummaryTable!.scrollTop = scrollTop; 181 }); 182 } 183 tableRowEl.appendChild(toggleIconEl); 184 } 185 186 private refreshRowNodeTable(useCacheRefresh: boolean = false): void { 187 this.eventSummaryTable!.innerHTML = ''; 188 if (this.eventSummaryTable && this.parentElement) { 189 this.eventSummaryTable.style.height = `${this.parentElement.clientHeight - NUM_30}px`; 190 } 191 if (!useCacheRefresh) { 192 this.eventTreeNodes = this.buildTreeTblNodes(this.summarySource); 193 if (this.eventTreeNodes.length > 0) { 194 this.summaryTable!.recycleDataSource = this.eventTreeNodes; 195 } else { 196 this.summaryTable!.recycleDataSource = []; 197 } 198 } 199 let tableFragmentEl: DocumentFragment = document.createDocumentFragment(); 200 let tableTreeEl: HTMLDivElement = document.createElement('div'); 201 tableTreeEl.className = 'event-tree-table'; 202 let tableCountEl: HTMLDivElement = document.createElement('div'); 203 if (this.parentElement) { 204 tableTreeEl.style.height = `${this.parentElement!.clientHeight - NUM_40}px`; 205 } 206 this.createRowNodeTableEL(this.eventTreeNodes, tableTreeEl, tableCountEl, ''); 207 const emptyTr = document.createElement('tr'); 208 emptyTr.className = 'tree-row-tr'; 209 tableTreeEl?.appendChild(emptyTr); 210 const emptyCountTr = document.createElement('tr'); 211 emptyCountTr.className = 'tree-row-tr'; 212 tableCountEl?.appendChild(emptyCountTr); 213 tableFragmentEl.appendChild(tableTreeEl); 214 tableFragmentEl.appendChild(tableCountEl); 215 this.eventSummaryTable!.appendChild(tableFragmentEl); 216 } 217 218 private changeNode(currentNode: number): void { 219 if (this.expandedNodeList.has(currentNode)) { 220 this.expandedNodeList.delete(currentNode); 221 } else { 222 this.expandedNodeList.add(currentNode); 223 } 224 this.refreshRowNodeTable(); 225 } 226 227 private buildTreeTblNodes(eventTreeNodes: HiSysEventStruct[]): HiSysEventTreeNode[] { 228 let id = 0; 229 let root: HiSysEventTreeNode = { id: id, depth: 0, children: [], summaryName: '', count: 0 }; 230 eventTreeNodes.forEach((item) => { 231 id++; 232 let levelNode = root.children.find((node) => node.summaryName === item.level); 233 if (levelNode) { 234 levelNode.count++; 235 } else { 236 id++; 237 levelNode = { id: id, depth: 0, children: [], summaryName: item.level, count: 1 }; 238 root.children.push(levelNode); 239 } 240 let domainNode = levelNode.children.find((node) => node.summaryName === item.domain); 241 if (domainNode) { 242 domainNode.count++; 243 } else { 244 id++; 245 domainNode = { id: id, depth: 1, children: [], summaryName: item.domain, count: 1 }; 246 levelNode.children.push(domainNode); 247 } 248 let eventNameNode = domainNode.children.find((node) => node.summaryName === item.eventName); 249 if (eventNameNode) { 250 eventNameNode.count++; 251 } else { 252 id++; 253 eventNameNode = { id: id, depth: 2, children: [], summaryName: item.eventName, count: 1 }; 254 domainNode.children.push(eventNameNode); 255 } 256 root.count++; 257 }); 258 return root.children.sort((leftData, rightData) => { 259 return leftData.summaryName!.length - rightData.summaryName!.length; 260 }); 261 } 262} 263 264export interface HiSysEventTreeNode { 265 id: number; 266 depth: number; 267 children: HiSysEventTreeNode[]; 268 summaryName: string | undefined; 269 count: number; 270} 271