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 { LitTable } from '../../../../../base-ui/table/lit-table'; 18import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection'; 19import { LitTableColumn } from '../../../../../base-ui/table/lit-table-column'; 20import { Utils } from '../../base/Utils'; 21import { SpSystemTrace } from '../../../SpSystemTrace'; 22import { TabUtil } from './TabUtil'; 23import { resizeObserver } from '../SheetUtils'; 24import { getTabSdkSliceData } from '../../../../database/sql/Sdk.sql'; 25import { queryTotalTime } from '../../../../database/sql/SqlLite.sql'; 26import { SdkSliceSummary } from '../../../../bean/SdkSummary'; 27 28@element('tabpane-sdk-slice') 29export class TabPaneSdkSlice extends BaseElement { 30 private tblSdkSlice: LitTable | null | undefined; 31 private sdkSliceRange: HTMLLabelElement | null | undefined; 32 private keyList: Array<string> | undefined; 33 private statDataArray: unknown = []; 34 private columnMap: unknown = {}; 35 private sqlMap: Map<number, string> = new Map<number, string>(); 36 37 set data(valSdkSlice: SelectionParam | unknown) { 38 let millisecond = 1000_000; 39 this.sdkSliceRange!.textContent = //@ts-ignore 40 'Selected range: ' + ((valSdkSlice.rightNs - valSdkSlice.leftNs) / millisecond).toFixed(5) + ' ms'; 41 this.queryDataByDB(valSdkSlice); 42 } 43 44 initElements(): void { 45 this.tblSdkSlice = this.shadowRoot?.querySelector<LitTable>('#tb-sdk-slice'); 46 this.sdkSliceRange = this.shadowRoot?.querySelector('#sdk-slice-time-range'); 47 this.tblSdkSlice!.addEventListener('column-click', (evt) => { 48 // @ts-ignore 49 this.sortByColumn(evt.detail); 50 }); 51 } 52 53 connectedCallback(): void { 54 super.connectedCallback(); 55 resizeObserver(this.parentElement!, this.tblSdkSlice!); 56 } 57 58 queryDataByDB(sdkSliceVal: SelectionParam | unknown): void { 59 queryTotalTime().then((res) => { 60 let startTime = res[0].recordStartNS; 61 let totalTime = res[0].total; 62 let componentId: number = -1; 63 let slices: Array<string> = []; //@ts-ignore 64 for (let index = 0; index < sdkSliceVal.sdkSliceIds.length; index++) { 65 //@ts-ignore 66 let values = sdkSliceVal.sdkSliceIds[index].split('-'); 67 let value = values[0]; 68 componentId = Number(values[1]); 69 slices.push(value); 70 } // @ts-ignore 71 this.parseJson(SpSystemTrace.SDK_CONFIG_MAP); 72 let sql = this.sqlMap.get(componentId); 73 if (sql === undefined) { 74 return; 75 } //@ts-ignore 76 getTabSdkSliceData(sql, startTime, sdkSliceVal.leftNs, sdkSliceVal.rightNs, slices, componentId).then( 77 (sliceItem) => { 78 this.keyList = []; 79 this.tblSdkSlice!.innerHTML = ''; 80 this.statDataArray = []; 81 if (sliceItem.length !== null && sliceItem.length > 0) { 82 this.initSdkSliceData(sliceItem, totalTime, sdkSliceVal); 83 } else { 84 this.tblSdkSlice!.recycleDataSource = []; 85 } 86 this.initDataElement(); 87 88 setTimeout(() => { 89 //@ts-ignore 90 this.tblSdkSlice!.recycleDataSource = this.statDataArray; 91 new ResizeObserver(() => { 92 if (this.parentElement?.clientHeight !== 0) { 93 this.tblSdkSlice!.style.height = '100%'; 94 this.tblSdkSlice!.reMeauseHeight(); 95 } 96 }).observe(this.parentElement!); 97 }, 200); 98 } 99 ); 100 }); 101 } 102 103 initSdkSliceData(sliceItem: SdkSliceSummary[], totalTime: number, sdkSliceVal: SelectionParam | unknown): void { 104 for (let sliceItemIndex = 0; sliceItemIndex < sliceItem.length; sliceItemIndex++) { 105 const dataResult = sliceItem[sliceItemIndex]; 106 let keys = Object.keys(dataResult); 107 // @ts-ignore 108 let values = Object.values(dataResult); 109 let sliceJsonText = '{'; 110 for (let sliceKeyIndex = 0; sliceKeyIndex < keys.length; sliceKeyIndex++) { 111 let sliceKey = keys[sliceKeyIndex]; 112 if (this.keyList!.indexOf(sliceKey) <= -1) { 113 this.keyList!.push(sliceKey); 114 } 115 let sliceValue = values[sliceKeyIndex]; //@ts-ignore 116 if (this.columnMap[sliceKey] === 'TimeStamp') { 117 sliceValue = Utils.getTimeString(Number(sliceValue)); //@ts-ignore 118 } else if (this.columnMap[sliceKey] === 'ClockTime') { 119 sliceValue = Utils.getTimeStampHMS(Number(sliceValue)); //@ts-ignore 120 } else if (this.columnMap[sliceKey] === 'RangTime') { 121 sliceValue = Utils.getDurString(Number(sliceValue)); //@ts-ignore 122 } else if (this.columnMap[sliceKey] === 'PercentType') { 123 sliceValue = sliceValue + '%'; //@ts-ignore 124 } else if (this.columnMap[sliceKey] === 'CurrencyType') { 125 sliceValue = sliceValue.toString().replace(/\B(?=(\d{3})+$)/g, ','); //@ts-ignore 126 } else if (this.columnMap[sliceKey] === 'FIXED') { 127 sliceValue = sliceValue.toFixed(2); 128 } 129 if (typeof sliceValue === 'string') { 130 sliceValue = sliceValue.replace(/</gi, '<').replace(/>/gi, '>'); 131 } 132 sliceJsonText += '"' + sliceKey + '"' + ': ' + '"' + sliceValue + '"'; 133 if (sliceKeyIndex !== keys.length - 1) { 134 sliceJsonText += ','; 135 } else { 136 sliceJsonText += '}'; 137 } 138 } 139 let sliceParseData = JSON.parse(sliceJsonText); 140 if ( 141 sliceParseData.start_ts !== null && 142 sliceParseData.end_ts !== null && 143 sliceParseData.start_ts > sliceParseData.end_ts && 144 sliceParseData.end_ts === 0 145 ) { 146 sliceParseData.end_ts = totalTime; 147 } 148 if ( 149 //@ts-ignore 150 this.isDateIntersection(sdkSliceVal.leftNs, sdkSliceVal.rightNs, sliceParseData.start_ts, sliceParseData.end_ts) 151 ) { 152 //@ts-ignore 153 this.statDataArray.push(sliceParseData); 154 } 155 } //@ts-ignore 156 this.tblSdkSlice!.recycleDataSource = this.statDataArray; 157 } 158 159 private isDateIntersection( 160 selectStartTime: number, 161 selectEndTime: number, 162 startTime: number, 163 endTime: number 164 ): boolean { 165 if (selectStartTime > startTime && selectStartTime < endTime) { 166 return true; 167 } 168 if (selectEndTime > startTime && selectEndTime < endTime) { 169 return true; 170 } 171 if (selectStartTime < startTime && selectEndTime > endTime) { 172 return true; 173 } 174 return false; 175 } 176 177 parseJson(map: Map<number, string>): string { 178 for (let [key, value] of map) { 179 let sliceConfigObj: unknown = value; 180 if (sliceConfigObj !== undefined) { 181 //@ts-ignore 182 let { jsonConfig } = sliceConfigObj; 183 let { tableConfig } = JSON.parse(jsonConfig); 184 if (tableConfig) { 185 for (let showType of tableConfig.showType) { 186 let innerTableName = this.getInnerTableName(showType); 187 let type = TabUtil.getTableType(showType); 188 if (type === 'slice') { 189 let sliceSelectSql = 'select '; 190 for (let { column, displayName, showType: columnShowType } of showType.columns) { 191 //@ts-ignore 192 this.columnMap[column] = displayName; 193 if (columnShowType.includes(3)) { 194 switch (column) { 195 case 'slice_id': 196 sliceSelectSql += 'a.slice_id,b.slice_name,'; 197 break; 198 case 'start_ts': 199 case 'end_ts': 200 sliceSelectSql += `(a.${column} - $startTime) as ${column},`; 201 break; 202 default: 203 sliceSelectSql += `a.${column},`; 204 break; 205 } 206 } 207 } 208 let sliceSql = `${sliceSelectSql.slice(0, -1)} from ${showType.tableName} as a,${innerTableName} as b 209 where a.slice_id in ($slices) 210 and a.slice_id = b.slice_id 211 and ((a.start_ts - $startTime) >= $leftNs and (a.end_ts - $startTime) <= $rightNs 212 or (a.start_ts - $startTime) <= $leftNs and $leftNs <= (a.end_ts - $startTime) 213 or (a.start_ts - $startTime) <= $rightNs and $rightNs <= (a.end_ts - $startTime))`; 214 this.sqlMap.set(key, sliceSql); 215 } 216 } 217 } 218 } 219 } 220 return ''; 221 } 222 223 initDataElement(): void { 224 if (this.keyList) { 225 this.keyList.forEach((sdkSliceItemKey) => { 226 let sdkSliceEl = document.createElement('lit-table-column') as LitTableColumn; 227 sdkSliceEl.setAttribute('title', sdkSliceItemKey); 228 sdkSliceEl.setAttribute('data-index', sdkSliceItemKey); 229 sdkSliceEl.setAttribute('key', sdkSliceItemKey); 230 sdkSliceEl.setAttribute('align', 'flex-start'); 231 if (sdkSliceItemKey === 'slice_id') { 232 sdkSliceEl.setAttribute('width', '0.5fr'); 233 } else { 234 sdkSliceEl.setAttribute('width', '1fr'); 235 } 236 sdkSliceEl.setAttribute('order', ''); 237 this.tblSdkSlice!.appendChild(sdkSliceEl); 238 }); 239 } 240 } 241 242 initHtml(): string { 243 return ` 244<style> 245.sdk-slice-table{ 246 height: 20px; 247 margin-bottom: 5px; 248} 249:host{ 250 padding: 10px 10px; 251 flex-direction: column; 252 display: flex; 253} 254</style> 255<div class="sdk-slice-content" class="sdk-slice-table" style="display: flex;align-items: center;flex-direction: row;"> 256 <stack-bar id="sdk-slice-stack-bar" style="flex: 1"></stack-bar> 257 <label id="sdk-slice-time-range" style="width: auto;text-align: end;font-size: 10pt;">Selected range:0.0 ms</label> 258 </div> 259<lit-table id="tb-sdk-slice" class="sdk-slice-tbl" style="height: auto"> 260</lit-table> 261 `; 262 } 263 264 sortByColumn(sliceDetail: unknown): void { 265 // @ts-ignore 266 function compare(property, sliceSort, type) { 267 return function (aSdkSlice: SelectionData, bSdkSlice: SelectionData) { 268 if (aSdkSlice.process === ' ' || bSdkSlice.process === ' ') { 269 return 0; 270 } 271 if (type === 'number') { 272 return sliceSort === 2 273 ? // @ts-ignore 274 parseFloat(bSdkSlice[property]) - parseFloat(aSdkSlice[property]) 275 : // @ts-ignore 276 parseFloat(aSdkSlice[property]) - parseFloat(bSdkSlice[property]); 277 } 278 // @ts-ignore 279 if (bSdkSlice[property] > aSdkSlice[property]) { 280 return sliceSort === 2 ? 1 : -1; 281 } else { 282 // @ts-ignore 283 if (bSdkSlice[property] === aSdkSlice[property]) { 284 return 0; 285 } else { 286 return sliceSort === 2 ? -1 : 1; 287 } 288 } 289 }; 290 } 291 //@ts-ignore 292 if (sliceDetail.key.indexOf('name') !== -1) { 293 //@ts-ignore 294 this.statDataArray.sort(compare(sliceDetail.key, sliceDetail.sort, 'string')); 295 } else { 296 //@ts-ignore 297 this.statDataArray.sort(compare(sliceDetail.key, sliceDetail.sort, 'number')); 298 } //@ts-ignore 299 this.tblSdkSlice!.recycleDataSource = this.statDataArray; 300 } 301 302 private getInnerTableName(showType: unknown): string { 303 //@ts-ignore 304 let inner = showType.inner; 305 if (inner !== null) { 306 return inner.tableName; 307 } 308 return ''; 309 } 310 311 filterSliceItem(sliceItem: Array<SdkSliceSummary>, totalTime: number, sdkSliceVal: unknown): void { 312 this.tblSdkSlice!.innerHTML = ''; 313 this.statDataArray = []; 314 if (sliceItem.length > 0) { 315 sliceItem.forEach((dataResult) => { 316 let keys = Object.keys(dataResult); 317 let values = Object.values(dataResult); 318 let sliceJsonText = '{'; 319 keys.forEach((sliceKey, sliceKeyIndex) => { 320 this.keyList = []; 321 if (this.keyList.indexOf(sliceKey) <= -1) { 322 this.keyList.push(sliceKey); 323 } 324 let sliceValue = values[sliceKeyIndex]; 325 sliceValue = this.getFormattedSliceValue(sliceKey, sliceValue); 326 sliceJsonText += `"${sliceKey}": "${sliceValue}"`; 327 if (sliceKeyIndex !== keys.length - 1) { 328 sliceJsonText += ','; 329 } else { 330 sliceJsonText += '}'; 331 } 332 }); 333 let sliceParseData = JSON.parse(sliceJsonText); 334 if ( 335 sliceParseData.start_ts !== null && 336 sliceParseData.end_ts !== null && 337 sliceParseData.start_ts > sliceParseData.end_ts && 338 sliceParseData.end_ts === 0 339 ) { 340 sliceParseData.end_ts = totalTime; 341 } 342 if ( 343 this.isDateIntersection( 344 //@ts-ignore 345 sdkSliceVal.leftNs, //@ts-ignore 346 sdkSliceVal.rightNs, 347 sliceParseData.start_ts, 348 sliceParseData.end_ts 349 ) 350 ) { 351 //@ts-ignore 352 this.statDataArray.push(sliceParseData); 353 } 354 }); //@ts-ignore 355 this.tblSdkSlice!.recycleDataSource = this.statDataArray; 356 } else { 357 this.tblSdkSlice!.recycleDataSource = []; 358 } 359 } 360 361 getFormattedSliceValue(sliceKey: string, sliceValue: unknown): unknown { 362 let result: string = ''; 363 //@ts-ignore 364 switch (this.columnMap[sliceKey]) { 365 case 'TimeStamp': 366 result = Utils.getTimeString(Number(sliceValue)); 367 break; 368 case 'ClockTime': 369 result = Utils.getTimeStampHMS(Number(sliceValue)); 370 break; 371 case 'RangTime': 372 result = Utils.getDurString(Number(sliceValue)); 373 break; 374 case 'PercentType': 375 result = `${sliceValue}%`; 376 break; 377 case 'CurrencyType': //@ts-ignore 378 result = sliceValue.toString().replace(/\B(?=(\d{3})+$)/g, ','); 379 break; 380 case 'FIXED': //@ts-ignore 381 result = sliceValue.toFixed(2); 382 break; 383 default: 384 if (typeof sliceValue === 'string') { 385 result = sliceValue.replace(/</gi, '<').replace(/>/gi, '>'); 386 break; 387 } 388 } 389 return result; 390 } 391} 392