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