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 { element } from '../../../../base-ui/BaseElement'; 17import { TimeRange } from '../timer-shaft/RangeRuler'; 18import '../../../../base-ui/icon/LitIcon'; 19import { Rect } from '../timer-shaft/Rect'; 20import { BaseStruct } from '../../../bean/BaseStruct'; 21import { ns2x } from '../TimerShaftElement'; 22import { TraceRowObject } from './TraceRowObject'; 23import { LitCheckBox } from '../../../../base-ui/checkbox/LitCheckBox'; 24import { LitIcon } from '../../../../base-ui/icon/LitIcon'; 25import '../../../../base-ui/popover/LitPopoverV'; 26import '../../../../base-ui/tree/LitTree'; 27import { LitPopover } from '../../../../base-ui/popover/LitPopoverV'; 28import { info } from '../../../../log/Log'; 29import { ColorUtils } from './ColorUtils'; 30import { drawSelectionRange, isFrameContainPoint } from '../../../database/ui-worker/ProcedureWorkerCommon'; 31import { TraceRowConfig } from './TraceRowConfig'; 32import { type TreeItemData, LitTree } from '../../../../base-ui/tree/LitTree'; 33import { SpSystemTrace } from '../../SpSystemTrace'; 34import { TraceRowHtml } from './TraceRow.html'; 35 36export class RangeSelectStruct { 37 startX: number | undefined; 38 endX: number | undefined; 39 startNS: number | undefined; 40 endNS: number | undefined; 41} 42 43let collectList: Array<TraceRow<BaseStruct>> = []; 44let rowDragId: string | undefined | null; 45let dragDirection: string = ''; 46 47@element('trace-row') 48export class TraceRow<T extends BaseStruct> extends HTMLElement { 49 sharedArrayBuffers: unknown; 50 intersectionRatio: number = 0; 51 static ROW_TYPE_SPSEGNENTATION = 'spsegmentation'; 52 static ROW_TYPE_CPU_COMPUTILITY = 'cpu-computility'; 53 static ROW_TYPE_GPU_COMPUTILITY = 'gpu-computility'; 54 static ROW_TYPE_BINDER_COUNT = 'binder-count'; 55 static ROW_TYPE_SCHED_SWITCH = 'sched-switch'; 56 static ROW_TYPE_CPU = 'cpu-data'; 57 static ROW_TYPE_CPU_STATE = 'cpu-state'; 58 static ROW_TYPE_CPU_FREQ = 'cpu-freq'; 59 static ROW_TYPE_CPU_FREQ_LIMIT = 'cpu-limit-freq'; 60 static ROW_TYPE_CPU_FREQ_ALL = 'cpu-frequency'; 61 static ROW_TYPE_CPU_STATE_ALL = 'cpu-State'; 62 static ROW_TYPE_CPU_FREQ_LIMITALL = 'cpu-frequency-limit'; 63 static ROW_TYPE_FPS = 'fps'; 64 static ROW_TYPE_NATIVE_MEMORY = 'native-memory'; 65 static ROW_TYPE_HIPERF = 'hiperf'; 66 static ROW_TYPE_DELIVER_INPUT_EVENT = 'DeliverInputEvent'; 67 static ROW_TYPE_HIPERF_CPU = 'hiperf-cpu'; 68 static ROW_TYPE_PERF_CALLCHART = 'hiperf-callchart'; 69 static ROW_TYPE_HIPERF_PROCESS = 'hiperf-process'; 70 static ROW_TYPE_HIPERF_THREAD = 'hiperf-thread'; 71 static ROW_TYPE_HIPERF_REPORT = 'hiperf-report'; 72 static ROW_TYPE_HIPERF_EVENT = 'hiperf-event'; 73 static ROW_TYPE_PROCESS = 'process'; 74 static ROW_TYPE_APP_STARTUP = 'app-startup'; 75 static ROW_TYPE_STATIC_INIT = 'static-init'; 76 static ROW_TYPE_THREAD = 'thread'; 77 static ROW_TYPE_MEM = 'mem'; 78 static ROW_TYPE_VIRTUAL_MEMORY_GROUP = 'virtual-memory-group'; 79 static ROW_TYPE_VIRTUAL_MEMORY = 'virtual-memory-cell'; 80 static ROW_TYPE_FILE_SYSTEM_GROUP = 'file-system-group'; 81 static ROW_TYPE_FILE_SYSTEM = 'file-system-cell'; 82 static ROW_TYPE_HEAP = 'heap'; 83 static ROW_TYPE_ARK_TS = 'ark-ts'; 84 static ROW_TYPE_HEAP_SNAPSHOT = 'heap-snapshot'; 85 static ROW_TYPE_HEAP_TIMELINE = 'heap-timeline'; 86 static ROW_TYPE_FUNC = 'func'; 87 static ROW_TYPE_MONITOR = 'ability-monitor'; 88 static ROW_TYPE_CPU_ABILITY = 'cpu-ability'; 89 static ROW_TYPE_MEMORY_ABILITY = 'memory-ability'; 90 static ROW_TYPE_DISK_ABILITY = 'disk-ability'; 91 static ROW_TYPE_NETWORK_ABILITY = 'network-ability'; 92 static ROW_TYPE_DMA_ABILITY = 'dma-ability'; 93 static ROW_TYPE_GPU_MEMORY_ABILITY = 'gpu-memory-ability'; 94 static ROW_TYPE_SDK = 'sdk'; 95 static ROW_TYPE_SDK_COUNTER = 'sdk-counter'; 96 static ROW_TYPE_SDK_SLICE = 'sdk-slice'; 97 static ROW_TYPE_ENERGY = 'energy'; 98 static ROW_TYPE_ANOMALY_ENERGY = 'anomaly-energy'; 99 static ROW_TYPE_SYSTEM_ENERGY = 'system-energy'; 100 static ROW_TYPE_POWER_ENERGY = 'power-energy'; 101 static ROW_TYPE_STATE_ENERGY = 'state-energy'; 102 static ROW_TYPE_SYS_MEMORY_GPU = 'sys-memory-gpu'; 103 static ROW_TYPE_SYS_MEMORY_GPU_GL = 'sys-memory-gpu-gl'; 104 static ROW_TYPE_SYS_MEMORY_GPU_GRAPH = 'sys-memory-gpu-graph'; 105 static ROW_TYPE_SYS_MEMORY_GPU_TOTAL = 'sys-memory-gpu-total'; 106 static ROW_TYPE_SYS_MEMORY_GPU_WINDOW = 'sys-memory-gpu-window'; 107 static ROW_TYPE_VM_TRACKER_SMAPS = 'smaps'; 108 static ROW_TYPE_VM_TRACKER = 'VmTracker'; 109 static ROW_TYPE_DMA_VMTRACKER = 'dma-vmTracker'; 110 static ROW_TYPE_GPU_MEMORY_VMTRACKER = 'gpu-memory-vmTracker'; 111 static ROW_TYPE_GPU_RESOURCE_VMTRACKER = 'sys-memory-gpu-resource'; 112 static ROW_TYPE_VMTRACKER_SHM = 'VmTracker-shm'; 113 static ROW_TYPE_CLOCK_GROUP = 'clock-group'; 114 static ROW_TYPE_COLLECT_GROUP = 'collect-group'; 115 static ROW_TYPE_CLOCK = 'clock'; 116 static ROW_TYPE_IRQ_GROUP = 'irq-group'; 117 static ROW_TYPE_IRQ = 'irq'; 118 static ROW_TYPE_JANK = 'janks'; 119 static ROW_TYPE_FRAME = 'frame'; 120 static ROW_TYPE_FRAME_ANIMATION = 'frame-animation'; 121 static ROW_TYPE_FRAME_DYNAMIC = 'frame-dynamic'; 122 static ROW_TYPE_FRAME_SPACING = 'frame-spacing'; 123 static ROW_TYPE_JS_CPU_PROFILER = 'js-cpu-profiler-cell'; 124 static ROW_TYPE_PURGEABLE_TOTAL_ABILITY = 'purgeable-total-ability'; 125 static ROW_TYPE_PURGEABLE_PIN_ABILITY = 'purgeable-pin-ability'; 126 static ROW_TYPE_PURGEABLE_TOTAL_VM = 'purgeable-total-vm'; 127 static ROW_TYPE_PURGEABLE_PIN_VM = 'purgeable-pin-vm'; 128 static ROW_TYPE_LOGS = 'logs'; 129 static ROW_TYPE_SAMPLE = 'bpftrace'; 130 static ROW_TYPE_ALL_APPSTARTUPS = 'all-appstartups'; 131 static ROW_TYPE_PERF_TOOL_GROUP = 'perf-tool-group'; 132 static ROW_TYPE_PERF_TOOL = 'perf-tool'; 133 static FRAME_WIDTH: number = 0; 134 static range: TimeRange | undefined | null; 135 static rangeSelectObject: RangeSelectStruct | undefined; 136 static ROW_TYPE_HI_SYSEVENT = 'hi-sysevent'; // @ts-ignore 137 public obj: TraceRowObject<unknown> | undefined | null; 138 isHover: boolean = false; 139 hoverX: number = 0; 140 hoverY: number = 0; 141 index: number = 0; 142 public must: boolean = false; 143 public isTransferCanvas = false; 144 onComplete: Function | undefined; 145 isComplete: boolean = false; 146 public dataList: Array<T> = []; 147 public dataList2: Array<T> = []; 148 public dataListCache: Array<T> = []; 149 public fixedList: Array<T> = []; 150 public sliceCache: number[] = [-1, -1]; 151 public describeEl: HTMLElement | null | undefined; 152 public canvas: Array<HTMLCanvasElement> = []; 153 public canvasVessel: HTMLDivElement | null | undefined; 154 public tipEL: HTMLDivElement | null | undefined; 155 public checkBoxEL: LitCheckBox | null | undefined; 156 public collectEL: LitIcon | null | undefined; 157 public onThreadHandler: ((useCache: boolean, buf: ArrayBuffer | undefined | null) => void) | undefined | null; 158 public onRowSettingChangeHandler: ((keys: Array<string>, nodes: Array<unknown>) => void) | undefined | null; 159 public onRowCheckFileChangeHandler: (() => void) | undefined | null; 160 public supplier: (() => Promise<Array<T>>) | undefined | null; // @ts-ignore 161 public favoriteChangeHandler: ((fav: TraceRow<unknown>) => void) | undefined | null; // @ts-ignore 162 public selectChangeHandler: ((traceRow: TraceRow<unknown>) => void) | undefined | null; 163 dpr = window.devicePixelRatio || 1; 164 // @ts-ignore 165 offscreen: Array<OffscreenCanvas | undefined> = []; 166 canvasWidth = 0; 167 canvasHeight = 0; 168 private _collectGroup: string | undefined; 169 public _frame: Rect | undefined; 170 public isLoading: boolean = false; 171 public tampName: string = ''; 172 public readonly args: unknown; 173 public templateType: Set<string> = new Set<string>(); 174 private rootEL: HTMLDivElement | null | undefined; 175 private nameEL: HTMLLabelElement | null | undefined; 176 private rowSettingTree: LitTree | null | undefined; 177 private rowSettingPop: LitPopover | null | undefined; 178 private fileEL: unknown; 179 private rowCheckFilePop: LitPopover | null | undefined; 180 private _rangeSelect: boolean = false; 181 private _drawType: number = 0; 182 private _enableCollapseChart: boolean = false; 183 online: boolean = false; 184 static isUserInteraction: boolean; 185 asyncFuncName: string | undefined | null; 186 asyncFuncNamePID: number | undefined | null; 187 translateY: number = 0; //single canvas offsetY; 188 // @ts-ignore 189 childrenList: Array<TraceRow<unknown>> = []; // @ts-ignore 190 parentRowEl: TraceRow<unknown> | undefined; 191 _rowSettingList: Array<TreeItemData> | null | undefined; 192 public supplierFrame: (() => Promise<Array<T>>) | undefined | null; //实时查询 193 public getCacheData: ((arg: unknown) => Promise<Array<unknown>> | undefined) | undefined; //实时查询 194 public loadingFrame: boolean = false; //实时查询,正在查询中 195 public needRefresh: boolean = true; 196 _frameRateList: Array<number> | undefined; //存储平均帧率数据 197 _avgRateTxt: string | undefined | null; //存储帧率显示文字 198 public folderIcon: LitIcon | null | undefined; 199 private sampleUploadEl: HTMLDivElement | null | undefined; 200 private jsonFileEl: HTMLInputElement | null | undefined; 201 202 focusHandler?: (ev: MouseEvent) => void | undefined; 203 findHoverStruct?: () => void | undefined; 204 public funcMaxHeight: number = 0; 205 currentContext: CanvasRenderingContext2D | undefined | null; 206 static ROW_TYPE_LTPO: string | null | undefined; 207 static ROW_TYPE_HITCH_TIME: string | null | undefined; 208 209 constructor( 210 args: { 211 canvasNumber: number; 212 alpha: boolean; 213 contextId: string; 214 isOffScreen: boolean; 215 skeleton?: boolean; 216 } = { 217 canvasNumber: 1, 218 alpha: false, 219 contextId: '2d', 220 isOffScreen: true, 221 skeleton: false, 222 } 223 ) { 224 super(); 225 this.args = args; 226 this.attachShadow({ mode: 'open' }).innerHTML = this.initHtml(); 227 this.initElements(); 228 } 229 230 static skeleton<T extends BaseStruct>(): TraceRow<T> { 231 let tr = new TraceRow<T>({ 232 alpha: false, 233 canvasNumber: 0, 234 contextId: '', 235 isOffScreen: false, 236 skeleton: true, 237 }); 238 tr.isTransferCanvas = true; 239 return tr; 240 } 241 242 static get observedAttributes(): string[] { 243 return [ 244 'folder', 245 'sticky', 246 'name', 247 'expansion', 248 'children', 249 'height', 250 'row-type', 251 'row-id', 252 'row-parent-id', 253 'sleeping', 254 'check-type', 255 'collect-type', 256 'collect-group', 257 'disabled-check', 258 'row-discard', 259 'func-expand', 260 'row-setting', 261 'row-setting-list', 262 'row-setting-popover-direction', 263 ]; 264 } 265 266 get uploadEl(): HTMLDivElement | null | undefined { 267 return this.sampleUploadEl; 268 } 269 270 get frameRateList(): Array<number> | undefined { 271 return this._frameRateList; 272 } 273 274 set frameRateList(value: Array<number> | undefined) { 275 this._frameRateList = value; 276 } 277 278 get avgRateTxt(): string | undefined | null { 279 return this._avgRateTxt; 280 } 281 282 set avgRateTxt(value: string | undefined | null) { 283 this._avgRateTxt = value; 284 } 285 286 get funcExpand(): boolean { 287 return this.getAttribute('func-expand') === 'true'; 288 } 289 290 set funcExpand(b: boolean) { 291 this.setAttribute('func-expand', b ? 'true' : 'false'); 292 } 293 294 get sticky(): boolean { 295 return this.hasAttribute('sticky'); 296 } 297 298 set sticky(fixed: boolean) { 299 if (fixed) { 300 this.setAttribute('sticky', ''); 301 } else { 302 this.removeAttribute('sticky'); 303 } 304 } 305 306 get hasParentRowEl(): boolean { 307 return this.parentRowEl !== undefined; 308 } 309 310 get rowDiscard(): boolean { 311 return this.hasAttribute('row-discard'); 312 } 313 314 set rowDiscard(value: boolean) { 315 if (value) { 316 this.setAttribute('row-discard', ''); 317 this.style.display = 'none'; 318 } else { 319 this.removeAttribute('row-discard'); 320 this.style.display = 'block'; 321 } 322 } 323 324 get collectGroup(): string | undefined { 325 return this._collectGroup; 326 } 327 328 set collectGroup(value: string | undefined) { 329 this._collectGroup = value; 330 this.setAttribute('collect-group', value || ''); 331 } 332 333 set rowSetting(value: string) { 334 this.setAttribute('row-setting', value); 335 } 336 337 get rowSetting(): string { 338 return this.getAttribute('row-setting') || 'disable'; 339 } 340 341 set rowSettingPopoverDirection(value: string) { 342 if (this.rowSettingPop) { 343 this.rowSettingPop.placement = value; 344 } 345 if (this.rowSettingPop) { 346 this.rowSettingPop.placement = value; 347 } 348 } 349 350 get rowSettingPopoverDirection(): string { 351 return this.rowSettingPop?.placement || 'bottomLeft'; 352 } 353 354 set rowSettingList(value: Array<TreeItemData> | null | undefined) { 355 this._rowSettingList = value; 356 if (this.rowSettingTree) { 357 this.rowSettingTree.treeData = value || []; 358 } 359 if (this.rowSettingTree) { 360 this.rowSettingTree.treeData = value || []; 361 } 362 } 363 364 set rowSettingMultiple(value: boolean) { 365 if (this.rowSettingTree) { 366 this.rowSettingTree.multiple = value; 367 } 368 } 369 370 get rowSettingList(): TreeItemData[] | null | undefined { 371 return this._rowSettingList; 372 } 373 374 get collect(): boolean { 375 return this.hasAttribute('collect-type'); 376 } 377 378 set collect(value: boolean) { 379 if (value) { 380 this.setAttribute('collect-type', ''); 381 } else { 382 this.removeAttribute('collect-type'); 383 } 384 } 385 386 get rangeSelect(): boolean { 387 return this._rangeSelect; 388 } 389 390 set rangeSelect(value: boolean) { 391 this._rangeSelect = value; 392 } 393 394 sleeping: boolean = false; 395 396 get rowType(): string | undefined | null { 397 return this.getAttribute('row-type'); 398 } 399 400 set rowType(val) { 401 this.setAttribute('row-type', val || ''); 402 } 403 404 get rowId(): string | undefined | null { 405 return this.getAttribute('row-id'); 406 } 407 408 set rowId(val) { 409 this.setAttribute('row-id', val || ''); 410 } 411 412 get rowParentId(): string | undefined | null { 413 return this.getAttribute('row-parent-id'); 414 } 415 416 set rowParentId(val) { 417 this.setAttribute('row-parent-id', val || ''); 418 } 419 420 get namePrefix(): string | undefined | null { 421 return this.getAttribute('name-prefix'); 422 } 423 424 set namePrefix(val) { 425 this.setAttribute('name-prefix', val || ''); 426 } 427 428 set rowHidden(val: boolean) { 429 let height = 0; 430 if (val) { 431 this.setAttribute('row-hidden', ''); 432 height = 0; 433 } else { 434 this.removeAttribute('row-hidden'); 435 height = this.clientHeight; 436 } 437 if (this.collect) { 438 window.publish(window.SmartEvent.UI.RowHeightChange, { 439 expand: this.funcExpand, 440 value: height, 441 }); 442 } 443 } 444 445 get name(): string { 446 return this.getAttribute('name') || ''; 447 } 448 449 set name(value: string) { 450 this.setAttribute('name', value); 451 } 452 453 get folder(): boolean { 454 return this.hasAttribute('folder'); 455 } 456 457 set folder(value: boolean) { 458 if (value) { 459 this.setAttribute('folder', ''); 460 this.folderIcon = document.createElement('lit-icon') as LitIcon; 461 this.folderIcon.classList.add('icon'); 462 this.folderIcon.setAttribute('name', 'caret-down'); 463 this.folderIcon.setAttribute('size', '19'); 464 this.folderIcon.style.display = 'flex'; 465 this.describeEl?.insertBefore(this.folderIcon, this.describeEl.children[0]); 466 } else { 467 this.removeAttribute('folder'); 468 } 469 } 470 471 get expansion(): boolean { 472 return this.hasAttribute('expansion'); 473 } 474 475 fragment: DocumentFragment = document.createDocumentFragment(); 476 477 set expansion(value) { 478 if (value === this.expansion) { 479 return; 480 } 481 if (value) { 482 this.updateChildRowStatus(); 483 } else { 484 this.childRowToFragment(false); 485 } 486 if (value) { 487 this.setAttribute('expansion', ''); 488 } else { 489 this.removeAttribute('expansion'); 490 } 491 this.dispatchEvent( 492 new CustomEvent('expansion-change', { 493 detail: { 494 expansion: this.expansion, 495 rowType: this.rowType, 496 rowId: this.rowId, 497 rowParentId: this.rowParentId, 498 }, 499 }) 500 ); 501 } 502 503 childRowToFragment(expansion: boolean): void { 504 for (const childrenRow of this.childrenList) { 505 if (!childrenRow.collect) { 506 this.fragment.append(childrenRow); 507 } 508 if (!expansion) { 509 if (childrenRow.childrenList && childrenRow.folder && childrenRow.expansion) { 510 childrenRow.expansion = false; 511 } 512 } 513 } 514 } 515 516 updateChildRowStatus(): void { 517 this.fragment = document.createDocumentFragment(); 518 this.childRowToFragment(true); 519 this.insertAfter(this.fragment, this); 520 } 521 522 clearMemory(): void { 523 this.dataList2 = []; 524 this.dataList = []; 525 this.dataListCache = []; 526 this.fixedList = []; 527 if (this.rootEL) { 528 this.rootEL.innerHTML = ''; 529 } 530 if (this.folder) { 531 this.childrenList.forEach((child) => { 532 if (child.clearMemory !== undefined) { 533 child.clearMemory(); 534 } 535 }); 536 this.childrenList = []; 537 } 538 } 539 540 addTemplateTypes(...type: string[]): void { 541 type.forEach((item) => { 542 this.templateType.add(item); 543 }); 544 if (this.hasParentRowEl) { 545 this.toParentAddTemplateType(this); 546 } 547 } 548 // @ts-ignore 549 toParentAddTemplateType = (currentRowEl: TraceRow<unknown>): void => { 550 let parentRow = currentRowEl.parentRowEl; 551 if (parentRow !== undefined) { 552 currentRowEl.templateType.forEach((item) => { 553 parentRow!.templateType.add(item); 554 }); 555 if (parentRow.parentRowEl !== undefined) { 556 this.toParentAddTemplateType(parentRow); 557 } 558 } 559 }; 560 561 //@ts-ignore 562 getHoverStruct(strict: boolean = true, offset: boolean = false, maxKey?: string): T | undefined { 563 if (this.isHover) { 564 if (maxKey) { 565 let arr = this.dataListCache 566 .filter((re) => re.frame && isFrameContainPoint(re.frame, this.hoverX, this.hoverY, strict, offset)) // @ts-ignore 567 .sort((targetA, targetB) => (targetB as unknown)[maxKey] - (targetA as unknown)[maxKey]); 568 return arr[0]; 569 } else { 570 return this.dataListCache.find( 571 (re) => re.frame && isFrameContainPoint(re.frame, this.hoverX, this.hoverY, strict, offset) 572 ); 573 } 574 } 575 } 576 // @ts-ignore 577 addChildTraceRow(child: TraceRow<unknown>): void { 578 // @ts-ignore 579 TraceRowConfig.allTraceRowList.push(child); 580 child.parentRowEl = this; 581 this.toParentAddTemplateType(child); 582 child.setAttribute('scene', ''); 583 this.childrenList.push(child); 584 child.rowHidden = false; 585 this.fragment.appendChild(child); 586 } 587 // @ts-ignore 588 addChildTraceRowAfter(child: TraceRow<unknown>, targetRow: TraceRow<unknown>): void { 589 // @ts-ignore 590 TraceRowConfig.allTraceRowList.push(child); 591 child.parentRowEl = this; 592 this.toParentAddTemplateType(child); 593 let index = this.childrenList.indexOf(targetRow); 594 child.setAttribute('scene', ''); 595 if (index !== -1) { 596 this.childrenList.splice(index + 1, 0, child); 597 child.rowHidden = false; 598 this.fragment.insertBefore(child, this.fragment.childNodes.item(index + 1)); 599 } else { 600 this.childrenList.push(child); 601 child.rowHidden = false; 602 this.fragment.append(child); 603 } 604 } 605 606 addChildTraceRowBefore(child: TraceRow<BaseStruct>, targetRow: TraceRow<BaseStruct>): void { 607 TraceRowConfig.allTraceRowList.push(child); 608 child.parentRowEl = this; 609 this.toParentAddTemplateType(child); 610 let index = this.childrenList.indexOf(targetRow); 611 child.setAttribute('scene', ''); 612 if (index !== -1) { 613 this.childrenList.splice(index, 0, child); 614 this.fragment.insertBefore(child, this.fragment.childNodes.item(index)); 615 } else { 616 this.childrenList.push(child); 617 child.rowHidden = false; 618 this.fragment.appendChild(child); 619 } 620 } 621 622 addRowSampleUpload(): void { 623 this.sampleUploadEl = document.createElement('div'); 624 this.sampleUploadEl!.className = 'upload'; 625 this.sampleUploadEl!.innerHTML = ` 626 <input id="file" class="file" accept="application/json" type="file" style="display:none;pointer-events:none"/> 627 <label for="file" style="cursor:pointer"> 628 <lit-icon class="folder" name="copy-csv" size="19"></lit-icon> 629 </label> 630 `; 631 this.jsonFileEl = this.sampleUploadEl!.querySelector('.file') as HTMLInputElement; 632 this.sampleUploadEl!.addEventListener('change', () => { 633 let files = this.jsonFileEl!.files; 634 if (files && files.length > 0) { 635 this.sampleUploadEl!.dispatchEvent( 636 new CustomEvent('sample-file-change', { 637 detail: files[0], 638 }) 639 ); 640 if (this.jsonFileEl) { 641 this.jsonFileEl.value = ''; 642 } 643 } 644 }); 645 this.sampleUploadEl!.addEventListener('click', (e): void => { 646 e.stopPropagation(); 647 }); 648 this.describeEl?.appendChild(this.sampleUploadEl!); 649 } 650 // @ts-ignore 651 addChildTraceRowSpecifyLocation(child: TraceRow<unknown>, index: number): void { 652 // @ts-ignore 653 TraceRowConfig.allTraceRowList.push(child); 654 child.parentRowEl = this; 655 child.setAttribute('scene', ''); 656 this.childrenList.splice(index, 0, child); 657 child.rowHidden = false; 658 this.fragment.insertBefore(child, this.fragment.childNodes.item(index)); 659 } 660 661 insertAfter(newEl: DocumentFragment, targetEl: HTMLElement): void { 662 let parentEl = targetEl.parentNode; 663 if (parentEl) { 664 if (parentEl!.lastChild === targetEl) { 665 parentEl!.appendChild(newEl); 666 } else { 667 parentEl!.insertBefore(newEl, targetEl.nextSibling); 668 } 669 } 670 } 671 672 sortRenderServiceData( 673 child: TraceRow<BaseStruct>, 674 targetRow: TraceRow<BaseStruct>, 675 threadRowArr: Array<TraceRow<BaseStruct>>, 676 flag: boolean 677 ): void { 678 if (child.rowType === 'thread') { 679 threadRowArr.push(child); 680 } else { 681 let index: number = threadRowArr.indexOf(targetRow); 682 if (index !== -1) { 683 threadRowArr.splice(index + 1, 0, child); 684 } else { 685 threadRowArr.push(child); 686 } 687 } 688 if (flag) { 689 let order: string[] = [ 690 'VSyncGenerator', 691 'VSync-rs', 692 'VSync-app', 693 'render_service', 694 'RSUniRenderThre', 695 'Acquire Fence', 696 'RSHardwareThrea', 697 'Present Fence', 698 ]; 699 let filterOrderArr: Array<TraceRow<BaseStruct>> = []; 700 let filterNotOrderArr: Array<TraceRow<BaseStruct>> = []; 701 for (let i = 0; i < threadRowArr.length; i++) { 702 // @ts-ignore 703 const element: TraceRow<unknown> = threadRowArr[i]; 704 let renderFlag: boolean = 705 element.name.startsWith('render_service') && element.rowId === element.rowParentId ? true : false; 706 if (renderFlag) { 707 // @ts-ignore 708 filterOrderArr.push(element); 709 } else if (order.includes(element.namePrefix!) && !element.name.startsWith('render_service')) { 710 // @ts-ignore 711 filterOrderArr.push(element); 712 } else if (!order.includes(element.namePrefix!) || !renderFlag) { 713 // @ts-ignore 714 filterNotOrderArr.push(element); 715 } 716 } 717 filterOrderArr.sort((star, next) => { 718 return order.indexOf(star.namePrefix!) - order.indexOf(next.namePrefix!); 719 }); 720 let combinedArr = [...filterOrderArr, ...filterNotOrderArr]; 721 combinedArr.forEach((item): void => { 722 this.addChildTraceRow(item); 723 }); 724 } 725 } 726 727 set tip(value: string) { 728 if (this.tipEL) { 729 this.tipEL.innerHTML = value; 730 } 731 } 732 733 get frame(): Rect { 734 if (this._frame) { 735 this._frame.width = TraceRow.FRAME_WIDTH; 736 this._frame.height = this.clientHeight; 737 return this._frame; 738 } else { 739 this._frame = new Rect(0, 0, TraceRow.FRAME_WIDTH, this.clientHeight || 40); 740 return this._frame; 741 } 742 } 743 744 set frame(f: Rect) { 745 this._frame = f; 746 } 747 748 get checkType(): string { 749 return this.getAttribute('check-type') || ''; 750 } 751 752 set checkType(value: string) { 753 if (!value || value.length === 0) { 754 this.removeAttribute('check-type'); 755 return; 756 } 757 if (this.getAttribute('check-type') === value) { 758 return; 759 } 760 if (this.folder) { 761 this.childrenList.forEach((it) => (it.checkType = value)); 762 } 763 this.setAttribute('check-type', value); 764 if (this.hasAttribute('disabled-check')) { 765 this.checkBoxEL!.style.display = 'none'; 766 return; 767 } 768 switch (value) { 769 case '-1': 770 this.checkBoxEL!.style.display = 'none'; 771 this.rangeSelect = false; 772 break; 773 case '0': 774 this.checkBoxEL!.style.display = 'flex'; 775 this.checkBoxEL!.checked = false; 776 this.checkBoxEL!.indeterminate = false; 777 this.rangeSelect = false; 778 break; 779 case '1': 780 this.checkBoxEL!.style.display = 'flex'; 781 this.checkBoxEL!.checked = false; 782 this.checkBoxEL!.indeterminate = true; 783 this.rangeSelect = false; 784 break; 785 case '2': 786 this.rangeSelect = true; 787 this.checkBoxEL!.style.display = 'flex'; 788 this.checkBoxEL!.checked = true; 789 this.checkBoxEL!.indeterminate = false; 790 break; 791 } 792 } 793 794 get drawType(): number { 795 return this._drawType; 796 } 797 798 set drawType(value: number) { 799 this._drawType = value; // @ts-ignore 800 let radioList: NodeListOf<unknown> = this.shadowRoot!.querySelectorAll('input[type=radio][name=status]'); 801 if (radioList!.length > 0) { 802 // @ts-ignore 803 radioList[Number(value)].checked = true; 804 } 805 } 806 807 get highlight(): boolean { 808 return this.hasAttribute('expansion'); 809 } 810 811 set highlight(value: boolean) { 812 if (value) { 813 this.setAttribute('highlight', ''); 814 } else { 815 this.removeAttribute('highlight'); 816 } 817 } 818 819 set folderPaddingLeft(value: number) { 820 if (this.folderIcon) { 821 this.folderIcon.style.marginLeft = `${value}px`; 822 } 823 } 824 825 set folderTextLeft(value: number) { 826 this.nameEL!.style.marginLeft = `${value}px`; 827 } 828 829 initElements(): void { 830 this.rootEL = this.shadowRoot?.querySelector('.root'); 831 this.checkBoxEL = this.shadowRoot?.querySelector<LitCheckBox>('.lit-check-box'); 832 this.collectEL = this.shadowRoot?.querySelector<LitIcon>('.collect'); 833 this.describeEl = this.shadowRoot?.querySelector('.describe'); 834 this.nameEL = this.shadowRoot?.querySelector('.name'); 835 this.canvasVessel = this.shadowRoot?.querySelector('.panel-vessel'); 836 this.tipEL = this.shadowRoot?.querySelector('.tip'); // @ts-ignore 837 let canvasNumber = this.args.canvasNumber; // @ts-ignore 838 if (!this.args.skeleton) { 839 for (let i = 0; i < canvasNumber; i++) { 840 let canvas = document.createElement('canvas'); 841 canvas.className = 'panel'; 842 this.canvas.push(canvas); 843 if (this.canvasVessel) { 844 this.canvasVessel.appendChild(canvas); 845 } 846 } 847 } 848 this.checkBoxEvent(); 849 this.describeEl?.addEventListener('click', () => { 850 if (this.folder) { 851 this.expansion = !this.expansion; 852 this.sticky = this.expansion; 853 } 854 }); 855 this.funcExpand = true; 856 if (this.rowSettingTree) { 857 this.rowSettingTree.onChange = (e: unknown): void => { 858 // @ts-ignore 859 this.rowSettingPop!.visible = false; 860 if (this.rowSettingTree?.multiple) { 861 // @ts-ignore 862 this.rowSettingPop!.visible = true; 863 } else { 864 // @ts-ignore 865 this.rowSettingPop!.visible = false; 866 } //@ts-ignore 867 this.onRowSettingChangeHandler?.(this.rowSettingTree!.getCheckdKeys(), this.rowSettingTree!.getCheckdNodes()); 868 }; 869 } 870 this.checkType = '-1'; 871 } 872 873 private checkBoxEvent(): void { 874 this.checkBoxEL!.onchange = (ev: unknown): void => { 875 info('checkBoxEL onchange '); // @ts-ignore 876 if (!ev.target.checked) { 877 info('checkBoxEL target not checked'); 878 this.rangeSelect = false; 879 this.checkType = '0'; 880 } else { 881 this.rangeSelect = true; 882 this.checkType = '2'; 883 } // @ts-ignore 884 this.setCheckBox(ev.target.checked); // @ts-ignore 885 ev.stopPropagation(); 886 }; 887 // 防止事件冒泡触发两次describeEl的点击事件 888 this.checkBoxEL!.onclick = (ev: unknown): void => { 889 // @ts-ignore 890 ev.stopPropagation(); 891 }; 892 } 893 894 addRowCheckFilePop(): void { 895 this.rowCheckFilePop = document.createElement('litpopover') as LitPopover; 896 this.rowCheckFilePop.innerHTML = `<div slot="content" id="jsonFile" style="display: block;height: auto;max-height:200px;overflow-y:auto"> 897 </div> 898 <lit-icon name="copy-csv" size="19" id="myfolder"></lit-icon> 899 <input type="file" id="jsoninput" style="width:0px;height:0px"placeholder=''/>`; 900 this.rowCheckFilePop.id = 'rowCheckFile'; 901 this.rowCheckFilePop.className = 'popover checkFile'; 902 this.rowCheckFilePop.setAttribute('trigger', 'click'); 903 this.rowCheckFilePop?.addEventListener('mouseenter', (e): void => { 904 window.publish(window.SmartEvent.UI.HoverNull, undefined); 905 }); 906 this.fileEL = this.rowCheckFilePop.querySelector('#jsoninput'); 907 this.rowCheckFilePop.addEventListener('click', (e): void => { 908 // @ts-ignore 909 this.fileEL.click(); 910 }); // @ts-ignore 911 this.fileEL.addEventListener('click', (event: Event) => { 912 event.stopPropagation(); 913 }); 914 915 window.addEventListener('storage', (e): void => { 916 if (e.storageArea === sessionStorage) { 917 if (e.key === 'freqInfoData') { 918 this.onRowCheckFileChangeHandler?.(); 919 } 920 } 921 }); // @ts-ignore 922 this.fileEL.addEventListener( 923 'change', 924 (e: unknown): void => { 925 // @ts-ignore 926 let file = e.target.files[0]; 927 if (file && file.type === 'application/json') { 928 let file_reader = new FileReader(); 929 file_reader.readAsText(file, 'UTF-8'); 930 file_reader.onload = (): void => { 931 let fc = file_reader.result; 932 window.sessionStorage.setItem('freqInfoData', JSON.stringify(fc)); 933 this.onRowCheckFileChangeHandler?.(); 934 alert('json文件上传成功!'); // @ts-ignore 935 this.fileEL.value = ''; 936 }; 937 } else { 938 return; 939 } 940 }, 941 false 942 ); 943 this.describeEl?.appendChild(this.rowCheckFilePop); 944 } 945 946 addRowSettingPop(): void { 947 this.rowSettingPop = document.createElement('lit-popover') as LitPopover; 948 this.rowSettingPop.innerHTML = `<div slot="content" id="settingList" style="display: block;height: auto;max-height:200px;overflow-y:auto"> 949 <lit-tree id="rowSettingTree" checkable="true"></lit-tree> 950 </div> 951 <lit-icon name="setting" size="19" id="setting"></lit-icon>`; 952 this.rowSettingPop.id = 'rowSetting'; 953 this.rowSettingPop.className = 'popover setting'; 954 this.rowSettingPop.setAttribute('placement', 'bottomLeft'); 955 this.rowSettingPop.setAttribute('trigger', 'click'); 956 this.rowSettingPop.setAttribute('haveRadio', 'true'); 957 this.rowSettingTree = this.rowSettingPop.querySelector('#rowSettingTree') as LitTree; 958 this.rowSettingTree.onChange = (): void => { 959 let isVisible = false; 960 // @ts-ignore 961 this.rowSettingPop!.visible = isVisible; 962 if (this.rowSettingTree?.multiple) { 963 isVisible = true; 964 } 965 // @ts-ignore 966 this.rowSettingPop!.visible = isVisible; //@ts-ignore 967 this.onRowSettingChangeHandler?.(this.rowSettingTree!.getCheckdKeys(), this.rowSettingTree!.getCheckdNodes()); 968 }; 969 this.rowSettingPop?.addEventListener('mouseenter', (): void => { 970 window.publish(window.SmartEvent.UI.HoverNull, undefined); 971 }); 972 this.describeEl?.appendChild(this.rowSettingPop); 973 } 974 975 getRowSettingKeys(): Array<string> { 976 if (this.rowSetting === 'enable') { 977 //@ts-ignore 978 return this.rowSettingTree!.getCheckdKeys(); 979 } 980 return []; 981 } 982 983 expandFunc(): void { 984 if (this._enableCollapseChart && !this.funcExpand) { 985 this.style.height = `${this.funcMaxHeight}px`; 986 this.funcExpand = true; 987 if (this.collect) { 988 window.publish(window.SmartEvent.UI.RowHeightChange, { 989 expand: this.funcExpand, 990 value: this.funcMaxHeight - 20, 991 }); 992 } 993 } 994 } 995 996 enableCollapseChart(): void { 997 this._enableCollapseChart = true; 998 this.nameEL!.onclick = (): void => { 999 if (this.funcMaxHeight > 20 || this.clientHeight > 20) { 1000 if (this.funcExpand) { 1001 this.funcMaxHeight = this.clientHeight; 1002 this.style.height = '20px'; 1003 this.funcExpand = false; 1004 } else { 1005 this.style.height = `${this.funcMaxHeight}px`; 1006 this.funcExpand = true; 1007 } 1008 TraceRow.range!.refresh = true; 1009 this.needRefresh = true; 1010 this.draw(false); 1011 if (this.collect) { 1012 window.publish(window.SmartEvent.UI.RowHeightChange, { 1013 expand: this.funcExpand, 1014 value: this.funcMaxHeight - 20, 1015 }); 1016 } 1017 } 1018 }; 1019 } 1020 1021 initCanvas(list: Array<HTMLCanvasElement>): void { 1022 let timerShaftEL = document! 1023 .querySelector('body > sp-application')! 1024 .shadowRoot!.querySelector('#sp-system-trace')! 1025 .shadowRoot!.querySelector('div > timer-shaft-element'); 1026 let timerShaftCanvas = timerShaftEL!.shadowRoot!.querySelector<HTMLCanvasElement>('canvas'); 1027 let tempHeight: number = 0; 1028 if (this.rowType === TraceRow.ROW_TYPE_FUNC) { 1029 tempHeight = 20; 1030 } else if (this.rowType === TraceRow.ROW_TYPE_THREAD) { 1031 tempHeight = 30; 1032 } else if (this.rowType === TraceRow.ROW_TYPE_SYSTEM_ENERGY) { 1033 tempHeight = 80; 1034 } else if (this.rowType === TraceRow.ROW_TYPE_POWER_ENERGY) { 1035 tempHeight = 200; 1036 } else if (this.rowType === TraceRow.ROW_TYPE_ANOMALY_ENERGY) { 1037 tempHeight = 55; 1038 } else { 1039 tempHeight = 40; 1040 } 1041 this.dpr = window.devicePixelRatio || 1; 1042 list.forEach((canvas): void => { 1043 this.rootEL!.style.height = `${this.getAttribute('height') || '40'}px`; 1044 canvas.style.width = timerShaftCanvas!.style.width; 1045 canvas.style.height = `${tempHeight}px`; 1046 this.canvasWidth = timerShaftCanvas!.width; 1047 this.canvasHeight = Math.ceil(tempHeight * this.dpr); 1048 canvas.width = this.canvasWidth; 1049 canvas.height = this.canvasHeight; 1050 // @ts-ignore 1051 this.offscreen.push(canvas!.transferControlToOffscreen()); 1052 }); 1053 } 1054 1055 updateWidth(width: number): void { 1056 this.dpr = window.devicePixelRatio || 1; 1057 let tempHeight: number = 0; 1058 if (this.rowType === TraceRow.ROW_TYPE_FUNC) { 1059 tempHeight = 20; 1060 } else if (this.rowType === TraceRow.ROW_TYPE_THREAD) { 1061 tempHeight = 30; 1062 } else if (this.rowType === TraceRow.ROW_TYPE_SYSTEM_ENERGY) { 1063 tempHeight = 90; 1064 } else if (this.rowType === TraceRow.ROW_TYPE_POWER_ENERGY) { 1065 tempHeight = 200; 1066 } else if (this.rowType === TraceRow.ROW_TYPE_ANOMALY_ENERGY) { 1067 tempHeight = 55; 1068 } else { 1069 tempHeight = 40; 1070 } 1071 if (this.canvas.length > 1) { 1072 tempHeight = 20; 1073 } 1074 this.canvas.forEach((it): void => { 1075 this.canvasWidth = Math.ceil((width - (this.describeEl?.clientWidth || 248)) * this.dpr); 1076 this.canvasHeight = Math.ceil(tempHeight * this.dpr); 1077 it!.style.width = `${width - (this.describeEl?.clientWidth || 248)}px`; // @ts-ignore 1078 if (this.args.isOffScreen) { 1079 this.draw(true); 1080 } 1081 }); 1082 } 1083 1084 drawLine(item: HTMLDivElement, direction: string /*string[top|bottom]*/): void { 1085 if (!item) { 1086 return; 1087 } 1088 switch (direction) { 1089 case 'top': 1090 item.classList.remove('line-bottom'); 1091 item.classList.add('line-top'); 1092 break; 1093 case 'bottom': 1094 item.classList.remove('line-top'); 1095 item.classList.add('line-bottom'); 1096 break; 1097 case '': 1098 item.classList.remove('line-top'); 1099 item.classList.remove('line-bottom'); 1100 break; 1101 } 1102 } 1103 1104 connectedCallback(): void { 1105 this.describeEl!.ondragstart = (ev: DragEvent): void => this.rowDragstart(ev); 1106 this.describeEl!.ondragleave = (ev: unknown): void => { 1107 // @ts-ignore 1108 this.drawLine(ev.currentTarget, ''); 1109 return undefined; 1110 }; 1111 this.describeElEvent(); 1112 this.collectEL!.onclick = (e): void => { 1113 this.collect = !this.collect; 1114 if (this.collect) { 1115 this.describeEl!.draggable = false; 1116 } else { 1117 this.describeEl!.draggable = false; 1118 } 1119 document.dispatchEvent( 1120 new CustomEvent('collect', { 1121 detail: { 1122 type: e.type, 1123 row: this, 1124 }, 1125 }) 1126 ); 1127 this.favoriteChangeHandler?.(this); 1128 }; // @ts-ignore 1129 if (!this.args.skeleton) { 1130 this.initCanvas(this.canvas); 1131 } 1132 } 1133 1134 private describeElEvent(): void { 1135 this.describeEl!.ondragend = (ev: unknown): void => { 1136 rowDragId = null; // @ts-ignore 1137 ev.target.classList.remove('drag'); // @ts-ignore 1138 this.drawLine(ev.currentTarget, ''); 1139 return undefined; 1140 }; 1141 this.describeEl!.ondragover = (ev: unknown): undefined => { 1142 if (!this.collect || rowDragId === this.rowId) { 1143 return; 1144 } // @ts-ignore 1145 let rect = ev.currentTarget.getBoundingClientRect(); // @ts-ignore 1146 if (ev.clientY >= rect.top && ev.clientY < rect.top + rect.height / 2) { 1147 //上面 1148 dragDirection = 'top'; // @ts-ignore 1149 this.drawLine(ev.currentTarget, 'top'); // @ts-ignore 1150 } else if (ev.clientY <= rect.bottom && ev.clientY > rect.top + rect.height / 2) { 1151 //下面 1152 dragDirection = 'bottom'; // @ts-ignore 1153 this.drawLine(ev.currentTarget, 'bottom'); 1154 } 1155 return undefined; 1156 }; 1157 this.describeEl!.ondrop = (ev: unknown): void => { 1158 if (!this.collect) { 1159 return; 1160 } // @ts-ignore 1161 this.drawLine(ev.currentTarget, ''); 1162 let spacer = this.parentElement!.previousElementSibling! as HTMLDivElement; 1163 let startDragNode = collectList.findIndex((it): boolean => it.rowId === rowDragId); 1164 let endDragNode = collectList.findIndex((it): boolean => it === this); 1165 if (startDragNode === -1 || endDragNode === -1) { 1166 return; 1167 } 1168 if (startDragNode < endDragNode && dragDirection === 'top') { 1169 endDragNode--; 1170 } else if (startDragNode > endDragNode && dragDirection === 'bottom') { 1171 endDragNode++; 1172 } 1173 collectList.splice(endDragNode, 0, ...collectList.splice(startDragNode, 1)); 1174 collectList.forEach((it, i): void => { 1175 if (i === 0) { 1176 // @ts-ignore 1177 it.style.top = `${spacer.offsetTop + 48}px`; 1178 } else { 1179 it.style.top = `${collectList[i - 1].offsetTop + collectList[i - 1].offsetHeight}px`; 1180 } 1181 }); 1182 }; 1183 } 1184 1185 rowDragstart(ev: unknown): void { 1186 rowDragId = this.rowId; // @ts-ignore 1187 ev.target.classList.add('drag'); 1188 } 1189 1190 setCheckBox(isCheck: boolean): void { 1191 if (this.folder) { 1192 // favorite row check change; 1193 window.publish(window.SmartEvent.UI.CheckALL, { 1194 rowId: this.rowId, 1195 isCheck: isCheck, 1196 }); 1197 this.childrenList!.forEach((ck): void => { 1198 ck.setAttribute('check-type', isCheck ? '2' : '0'); 1199 let allCheck: LitCheckBox | null | undefined = ck?.shadowRoot?.querySelector('.lit-check-box'); 1200 if (allCheck) { 1201 allCheck!.checked = isCheck; 1202 } 1203 }); 1204 } 1205 this.selectChangeHandler?.(this); 1206 } 1207 1208 onMouseHover(x: number, y: number, tip: boolean = true): T | undefined | null { 1209 if (this.tipEL) { 1210 this.tipEL.style.display = 'none'; 1211 } 1212 return null; 1213 } 1214 1215 setTipLeft(x: number, struct: unknown): void { 1216 if (struct === null && this.tipEL) { 1217 this.tipEL.style.display = 'none'; 1218 return; 1219 } 1220 if (this.tipEL) { 1221 this.tipEL.style.display = 'flex'; 1222 if (x + this.tipEL.clientWidth > (this.canvasVessel!.clientWidth || 0)) { 1223 this.tipEL.style.transform = `translateX(${x - this.tipEL.clientWidth - 1}px)`; 1224 } else { 1225 this.tipEL.style.transform = `translateX(${x}px)`; 1226 } 1227 } 1228 } 1229 1230 onMouseLeave(x: number, y: number): void { 1231 if (this.tipEL) { 1232 this.tipEL.style.display = 'none'; 1233 } 1234 } 1235 1236 loadingPin1: number = 0; 1237 loadingPin2: number = 0; 1238 static currentActiveRows: Array<string> = []; 1239 1240 drawFrame(): void { 1241 if (!this.hasAttribute('row-hidden')) { 1242 if (!this.loadingFrame || window.isLastFrame || !this.isComplete) { 1243 if (this.needRefresh || window.isLastFrame) { 1244 this.loadingFrame = true; 1245 this.needRefresh = false; 1246 this.loadingPin1 = TraceRow.range?.startNS || 0; 1247 this.loadingPin2 = TraceRow.range?.endNS || 0; 1248 TraceRow.currentActiveRows.push(`${this.rowType}-${this.rowId}`); 1249 this.supplierFrame!().then((res) => { 1250 if (this.onComplete) { 1251 this.onComplete(); 1252 this.onComplete = undefined; 1253 } 1254 this.dataListCache = res; 1255 this.dataListCache.push(...this.fixedList); 1256 this.isComplete = true; 1257 this.loadingFrame = false; 1258 let idx = TraceRow.currentActiveRows.findIndex((it): boolean => it === `${this.rowType}-${this.rowId}`); 1259 if (idx !== -1) { 1260 TraceRow.currentActiveRows.splice(idx, 1); 1261 } 1262 requestAnimationFrame(() => { 1263 this.onThreadHandler?.(true, null); 1264 if (TraceRow.currentActiveRows) { 1265 window.publish(window.SmartEvent.UI.LoadFinish, ''); 1266 } 1267 window.publish(window.SmartEvent.UI.LoadFinishFrame, ''); 1268 }); 1269 }); 1270 } else if (this.fixedList.length > 0 && !this.dataListCache.includes(this.fixedList[0])) { 1271 this.dataListCache.push(this.fixedList[0]); 1272 } 1273 } 1274 this.onThreadHandler?.(true, null); 1275 } 1276 } 1277 1278 draw(useCache: boolean = false): void { 1279 this.dpr = window.devicePixelRatio || 1; 1280 if (this.sleeping) { 1281 return; 1282 } 1283 if (this.supplierFrame) { 1284 //如果设置了实时渲染,则调用drawFrame 1285 this.drawFrame(); 1286 return; 1287 } 1288 if (this.online) { 1289 if (!useCache && !TraceRow.isUserInteraction) { 1290 this.supplier?.().then((res) => { 1291 // @ts-ignore 1292 this.onThreadHandler?.(useCache, res as unknown); 1293 }); 1294 } 1295 this.onThreadHandler?.(useCache, null); 1296 return; 1297 } 1298 if (!this.isComplete) { 1299 if (this.supplier && !this.isLoading) { 1300 this.isLoading = true; 1301 this.must = true; 1302 let promise = this.supplier(); 1303 if (promise) { 1304 promise.then((res): void => { 1305 this.dataList = res; 1306 if (this.onComplete) { 1307 this.onComplete(); 1308 } 1309 window.publish(window.SmartEvent.UI.TraceRowComplete, this); 1310 this.isComplete = true; 1311 this.isLoading = false; 1312 this.draw(false); 1313 }); 1314 } else { 1315 this.isLoading = false; 1316 this.draw(false); 1317 } 1318 } 1319 } else { 1320 if (!this.hasAttribute('row-hidden')) { 1321 if (this.onThreadHandler && this.dataList) { 1322 this.onThreadHandler!(false, null); 1323 } 1324 } 1325 } 1326 } 1327 1328 canvasSave(ctx: CanvasRenderingContext2D): void { 1329 ctx.save(); 1330 ctx.translate(0, this.translateY); 1331 const clipRect = new Path2D(); // @ts-ignore 1332 clipRect.rect(0, 0, this.frame.width, this.frame.height); 1333 ctx.clip(clipRect); 1334 } 1335 1336 canvasRestore(ctx: CanvasRenderingContext2D, trace?: SpSystemTrace | null): void { 1337 drawSelectionRange(ctx, this); 1338 ctx.restore(); 1339 } 1340 1341 clearCanvas(ctx: CanvasRenderingContext2D): void { 1342 if (ctx) { 1343 this.canvas.forEach((it): void => { 1344 ctx.clearRect(0, 0, it!.clientWidth || 0, it!.clientHeight || 0); 1345 }); 1346 } 1347 } 1348 1349 drawLines(ctx: CanvasRenderingContext2D): void { 1350 if (ctx) { 1351 ctx.lineWidth = 1; 1352 ctx.strokeStyle = this.getLineColor(); 1353 TraceRow.range?.xs.forEach((it): void => { 1354 ctx.moveTo(Math.floor(it), 0); 1355 ctx.lineTo(Math.floor(it), this.shadowRoot?.host.clientHeight || 0); 1356 }); 1357 ctx.stroke(); 1358 } 1359 } 1360 1361 getLineColor(): string { 1362 return window.getComputedStyle(this.rootEL!, null).getPropertyValue('border-bottom-color'); 1363 } 1364 1365 drawSelection(ctx: CanvasRenderingContext2D): void { 1366 if (this.rangeSelect) { 1367 TraceRow.rangeSelectObject!.startX = Math.floor( 1368 ns2x( 1369 TraceRow.rangeSelectObject!.startNS!, 1370 TraceRow.range!.startNS, 1371 TraceRow.range!.endNS, 1372 TraceRow.range!.totalNS!, // @ts-ignore 1373 this.frame 1374 ) 1375 ); 1376 TraceRow.rangeSelectObject!.endX = Math.floor( 1377 ns2x( 1378 TraceRow.rangeSelectObject!.endNS!, 1379 TraceRow.range!.startNS, 1380 TraceRow.range!.endNS, 1381 TraceRow.range!.totalNS!, // @ts-ignore 1382 this.frame 1383 ) 1384 ); 1385 if (ctx) { 1386 ctx.globalAlpha = 0.5; 1387 ctx.fillStyle = '#666666'; 1388 ctx.fillRect( 1389 TraceRow.rangeSelectObject!.startX!, // @ts-ignore 1390 this.frame.y, 1391 TraceRow.rangeSelectObject!.endX! - TraceRow.rangeSelectObject!.startX!, // @ts-ignore 1392 this.frame.height 1393 ); 1394 ctx.globalAlpha = 1; 1395 } 1396 } 1397 } 1398 1399 isInTimeRange(startTime: number, duration: number): boolean { 1400 return ( 1401 (startTime || 0) + (duration || 0) > (TraceRow.range?.startNS || 0) && 1402 (startTime || 0) < (TraceRow.range?.endNS || 0) 1403 ); 1404 } 1405 1406 buildArgs(obj: unknown): unknown { 1407 let result: unknown = { 1408 list: this.must ? this.dataList : undefined, 1409 offscreen: !this.isTransferCanvas ? this.offscreen[0] : undefined, //是否离屏 1410 dpr: this.dpr, //屏幕dpr值 1411 xs: TraceRow.range?.xs, //线条坐标信息 1412 isHover: this.isHover, 1413 hoverX: this.hoverX, 1414 hoverY: this.hoverY, 1415 canvasWidth: this.canvasWidth, 1416 canvasHeight: this.canvasHeight, 1417 isRangeSelect: this.rangeSelect, 1418 rangeSelectObject: TraceRow.rangeSelectObject, 1419 lineColor: this.getLineColor(), 1420 chartColor: ColorUtils.MD_PALETTE[0], 1421 startNS: TraceRow.range?.startNS || 0, 1422 endNS: TraceRow.range?.endNS || 0, 1423 totalNS: TraceRow.range?.totalNS || 0, 1424 slicesTime: TraceRow.range?.slicesTime, 1425 range: TraceRow.range, 1426 frame: this.frame, 1427 flagMoveInfo: null, 1428 flagSelectedInfo: null, 1429 wakeupBean: null, 1430 }; // @ts-ignore 1431 Reflect.ownKeys(obj).forEach((it): void => { 1432 // @ts-ignore 1433 result[it] = obj[it]; 1434 }); 1435 return result; 1436 } 1437 1438 getTransferArray(): unknown[] { 1439 let tsf = []; 1440 if (!this.isTransferCanvas) { 1441 tsf.push(this.offscreen[0]); 1442 } 1443 if (this.must && this.dataList instanceof ArrayBuffer) { 1444 tsf.push(this.dataList); 1445 } 1446 return tsf; 1447 } 1448 1449 attributeChangedCallback(name: string, oldValue: string, newValue: string): void { 1450 switch (name) { 1451 case 'name': 1452 if (this.nameEL) { 1453 this.nameEL.textContent = newValue; 1454 this.nameEL.title = newValue; 1455 } 1456 break; 1457 case 'height': 1458 if (newValue !== oldValue) { 1459 // @ts-ignore 1460 if (!this.args.isOffScreen) { 1461 } 1462 } 1463 break; 1464 case 'check-type': 1465 if (newValue === 'check') { 1466 this.checkBoxEL?.setAttribute('checked', ''); 1467 } else { 1468 this.checkBoxEL?.removeAttribute('checked'); 1469 } 1470 break; 1471 } 1472 } 1473 1474 focusContain(e: MouseEvent, inFavoriteArea: boolean): boolean { 1475 let _y = (e.currentTarget as HTMLElement).getBoundingClientRect().y; 1476 let myRect = this.getBoundingClientRect(); 1477 let x = e.offsetX; 1478 let y = e.offsetY + _y; 1479 if (x >= myRect.x && x <= myRect.x + myRect.width && y >= myRect.y && y <= myRect.y + myRect.height) { 1480 this.hoverX = x - this.describeEl!.clientWidth; 1481 this.hoverY = y - myRect.y; 1482 this.isHover = this.collect === inFavoriteArea; 1483 return true; 1484 } else { 1485 this.isHover = false; 1486 if (this.tipEL) { 1487 this.tipEL.style.display = 'none'; 1488 } 1489 return false; 1490 } 1491 } 1492 1493 initHtml(): string { 1494 return TraceRowHtml; 1495 } 1496} 1497