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