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