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 { type LitChartPie } from '../../../../../base-ui/chart/pie/LitChartPie'; 18import { type LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar'; 19import { type LitTable } from '../../../../../base-ui/table/lit-table'; 20import { SelectionParam } from '../../../../bean/BoxSelection'; 21import { type JsCpuProfilerChartFrame, JsCpuProfilerStatisticsStruct } from '../../../../bean/JsStruct'; 22import { procedurePool } from '../../../../database/Procedure'; 23import { type SampleType } from '../../../../database/logic-worker/ProcedureLogicWorkerJsCpuProfiler'; 24import { ns2s } from '../../../../database/ui-worker/ProcedureWorkerCommon'; 25import { resizeObserver } from '../SheetUtils'; 26import { TabPaneJsCpuStatisticsHtml } from './TabPaneJsCpuStatistics.html'; 27 28@element('tabpane-js-cpu-statistics') 29export class TabPaneJsCpuStatistics extends BaseElement { 30 private progress: LitProgressBar | null | undefined; 31 private statisticsTable: LitTable | null | undefined; 32 private statisticsSource: Array<JsCpuProfilerStatisticsStruct> = []; 33 private tabTitle: HTMLDivElement | undefined | null; 34 private sortKey = 'timeStr'; 35 private sortType = 2; 36 private statisticsPie: LitChartPie | null | undefined; 37 private currentSelection: SelectionParam | undefined; 38 39 set data(data: SelectionParam | Array<JsCpuProfilerChartFrame>) { 40 if (data instanceof SelectionParam) { 41 if (data === this.currentSelection) { 42 return; 43 } 44 this.currentSelection = data; 45 } 46 47 this.init(); 48 this.clearData(); 49 this.progress!.loading = true; 50 51 this.getDataByWorker(data, (results: Map<SampleType, number>) => { 52 this.progress!.loading = false; 53 this.statisticsSource = results.size > 0 ? this.setStatisticsData(results) : []; 54 this.queryPieChartDataByType(this.statisticsSource || []); 55 }); 56 } 57 58 private init(): void { 59 const thTable = this.tabTitle!.querySelector('.th'); 60 const jsCpuStatTblNodes = thTable!.querySelectorAll('div'); 61 if (this.tabTitle!.hasAttribute('sort')) { 62 this.tabTitle!.removeAttribute('sort'); 63 jsCpuStatTblNodes.forEach((item) => { 64 item.querySelectorAll('svg').forEach((svg) => { 65 svg.style.display = 'none'; 66 }); 67 }); 68 } 69 this.sortKey = 'timeStr'; 70 this.sortType = 2; 71 } 72 73 private getDataByWorker(data: SelectionParam | Array<JsCpuProfilerChartFrame>, handler: Function): void { 74 let params; 75 if (data instanceof SelectionParam) { 76 params = { 77 data: data.jsCpuProfilerData, 78 leftNs: data.leftNs, 79 rightNs: data.rightNs, 80 }; 81 } else { 82 params = { 83 data: data, 84 }; 85 } 86 procedurePool.submitWithName( 87 'logic0', 88 'jsCpuProfiler-statistics', 89 params, 90 undefined, 91 (results: Map<SampleType, number>) => { 92 handler(results); 93 } 94 ); 95 } 96 97 private queryPieChartDataByType(res: Array<JsCpuProfilerStatisticsStruct>): void { 98 this.statisticsPie!.config = { 99 appendPadding: 0, 100 data: res, 101 angleField: 'time', 102 colorField: 'type', 103 radius: 1, 104 label: { 105 type: 'outer', 106 }, 107 tip: (obj): string => { 108 return `<div> 109 <div>type: ${ 110 // @ts-ignore 111 obj.obj.type 112 }</div> 113 <div>total: ${ 114 // @ts-ignore 115 ns2s(obj.obj.time) 116 } (${ 117 // @ts-ignore 118 obj.obj.percentage 119 }%)</div> 120 </div> `; 121 }, 122 hoverHandler: (data): void => { 123 if (data) { 124 this.statisticsTable!.setCurrentHover(data); 125 } else { 126 this.statisticsTable!.mouseOut(); 127 } 128 }, 129 interactions: [ 130 { 131 type: 'element-active', 132 }, 133 ], 134 }; 135 this.statisticsTable!.addEventListener('row-hover', (statisticsRowHover: unknown) => { 136 // @ts-ignore 137 if (statisticsRowHover.detail.data) { 138 // @ts-ignore 139 let data = statisticsRowHover.detail.data; // @ts-ignore 140 data.isHover = true; // @ts-ignore 141 if ((statisticsRowHover.detail as unknown).callBack) { 142 // @ts-ignore 143 (statisticsRowHover.detail as unknown).callBack(true); 144 } 145 } 146 this.statisticsPie?.showHover(); 147 this.statisticsPie?.hideTip(); 148 }); 149 150 if (this.statisticsSource.length > 0) { 151 this.sortByColumn({ key: this.sortKey, sort: this.sortType }); 152 let total = this.totalData(this.statisticsSource); 153 this.statisticsSource.unshift(total); 154 this.statisticsTable!.recycleDataSource = this.statisticsSource; 155 this.statisticsSource.shift(); 156 } 157 this.statisticsTable?.reMeauseHeight(); 158 } 159 160 private totalData(source: Array<JsCpuProfilerStatisticsStruct>): JsCpuProfilerStatisticsStruct { 161 // 计算总的time作为表格的第一行显示 162 let totalTime = 0; 163 for (let item of source) { 164 totalTime += item.time; 165 } 166 return this.toStatisticsStruct('', totalTime, totalTime); 167 } 168 169 private clearData(): void { 170 this.statisticsPie!.dataSource = []; 171 this.statisticsTable!.recycleDataSource = []; 172 } 173 174 private setStatisticsData(results: Map<SampleType, number>): Array<JsCpuProfilerStatisticsStruct> { 175 this.statisticsTable!.recycleDataSource = []; 176 this.statisticsSource = []; 177 let statisticsData: JsCpuProfilerStatisticsStruct; 178 const totalTime = [...results.values()].reduce((prev, curr) => prev + curr); 179 for (let [key, value] of results.entries()) { 180 statisticsData = this.toStatisticsStruct(key, value, totalTime); 181 this.statisticsSource.push(statisticsData); 182 } 183 return this.statisticsSource.sort((a, b) => b.time - a.time); 184 } 185 186 private toStatisticsStruct( 187 type: string | SampleType, 188 time: number, 189 percentage: number 190 ): JsCpuProfilerStatisticsStruct { 191 return new JsCpuProfilerStatisticsStruct(type, time, ns2s(time), ((time / percentage || 0) * 100).toFixed(1)); 192 } 193 194 private sortByColumn(detail: unknown): void { 195 // @ts-ignore 196 function compare(key, sort, type) { 197 return function (a: unknown, b: unknown) { 198 // 不管哪一列的排序方式是0(默认排序),都按照time列从大到小排序 199 if (sort === 0) { 200 sort = 2; 201 key = 'time'; 202 type = 'number'; 203 } 204 if (type === 'number') { 205 // @ts-ignore 206 return sort === 2 ? parseFloat(b[key]) - parseFloat(a[key]) : parseFloat(a[key]) - parseFloat(b[key]); 207 } else { 208 if (sort === 2) { 209 // @ts-ignore 210 return b[key].toString().localeCompare(a[key].toString()); 211 } else { 212 // @ts-ignore 213 return a[key].toString().localeCompare(b[key].toString()); 214 } 215 } 216 }; 217 } // @ts-ignore 218 if (detail.key === 'timeStr' || detail.key === 'percentage') { 219 // @ts-ignore 220 this.statisticsSource.sort(compare('time', detail.sort, 'number')); // @ts-ignore 221 } else if (detail.key === 'type') { 222 // @ts-ignore 223 this.statisticsSource.sort(compare(detail.key, detail.sort, 'string')); 224 } 225 if (this.statisticsSource.length > 0) { 226 let total = this.totalData(this.statisticsSource); 227 this.statisticsSource.unshift(total); 228 this.statisticsTable!.recycleDataSource = this.statisticsSource; 229 this.statisticsSource.shift(); 230 } 231 } 232 233 public connectedCallback(): void { 234 super.connectedCallback(); 235 resizeObserver(this.parentElement!, this.statisticsTable!); 236 } 237 238 public initElements(): void { 239 this.progress = this.shadowRoot!.querySelector<LitProgressBar>('#loading'); 240 this.statisticsTable = this.shadowRoot?.querySelector('#statistics-table') as LitTable; 241 this.statisticsPie = this.shadowRoot?.querySelector('#chart-pie') as LitChartPie; 242 this.tabTitle = this.statisticsTable!.shadowRoot?.querySelector('.thead') as HTMLDivElement; 243 244 this.statisticsTable!.addEventListener('column-click', (evt: unknown) => { 245 // @ts-ignore 246 this.sortKey = evt.detail.key; // @ts-ignore 247 this.sortType = evt.detail.sort; 248 // @ts-ignore 249 this.sortByColumn(evt.detail); 250 }); 251 } 252 253 public initHtml(): string { 254 return TabPaneJsCpuStatisticsHtml; 255 } 256} 257