• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { BaseElement, element } from '../../../base-ui/BaseElement';
17import { TraceRow } from './base/TraceRow';
18import { dpr } from './base/Extension';
19import {
20  drawFlagLineSegment,
21  drawLines,
22  drawLinkLines,
23  drawLogsLineSegment,
24  drawWakeUp,
25  drawWakeUpList,
26  PairPoint,
27  Rect,
28} from '../../database/ui-worker/ProcedureWorkerCommon';
29import { Flag } from './timer-shaft/Flag';
30import { TimerShaftElement } from './TimerShaftElement';
31import { CpuStruct } from '../../database/ui-worker/cpu/ProcedureWorkerCPU';
32import { WakeupBean } from '../../bean/WakeupBean';
33import { LitIcon } from '../../../base-ui/icon/LitIcon';
34
35const maxScale = 0.8; //收藏最大高度为界面最大高度的80%
36const topHeight = 150; // 顶部cpu使用率部分高度固定为150px
37const minHeight = 40; //泳道最低高度为40
38const mouseMoveRange = 5;
39
40@element('sp-chart-list')
41export class SpChartList extends BaseElement {
42  private static COLLECT_G1 = '1';
43  private static COLLECT_G2 = '2';
44  private collectEl1: HTMLDivElement | null | undefined;
45  private collectEl2: HTMLDivElement | null | undefined;
46  private groupTitle1: HTMLDivElement | null | undefined;
47  private groupTitle2: HTMLDivElement | null | undefined;
48  private icon1: LitIcon | null | undefined;
49  private icon2: LitIcon | null | undefined;
50  private removeCollectIcon1: LitIcon | null | undefined;
51  private removeCollectIcon2: LitIcon | null | undefined;
52  private rootEl: HTMLDivElement | null | undefined;
53  private fragmentGroup1: DocumentFragment = document.createDocumentFragment();
54  private fragmentGroup2: DocumentFragment = document.createDocumentFragment();
55  private canvas: HTMLCanvasElement | null | undefined; //绘制收藏泳道图
56  private canvasCtx: CanvasRenderingContext2D | undefined | null;
57  private canResize: boolean = false;
58  private isPress: boolean = false;
59  private startPageY = 0;
60  private startClientHeight: number = 0;
61  private scrollTimer: any;
62  private collect1Expand: boolean = true;
63  private collect2Expand: boolean = true;
64  private collectRowList1: Array<TraceRow<any>> = [];
65  private collectRowList2: Array<TraceRow<any>> = [];
66  private maxHeight = 0;
67  private manualHeight = 0;
68
69  initElements(): void {
70    this.collectEl1 = this.shadowRoot?.querySelector<HTMLDivElement>('#collect-group-1');
71    this.collectEl2 = this.shadowRoot?.querySelector<HTMLDivElement>('#collect-group-2');
72    this.groupTitle1 = this.shadowRoot?.querySelector<HTMLDivElement>('#group-1-title');
73    this.groupTitle2 = this.shadowRoot?.querySelector<HTMLDivElement>('#group-2-title');
74    this.icon1 = this.shadowRoot?.querySelector<LitIcon>('#group_1_expand');
75    this.icon2 = this.shadowRoot?.querySelector<LitIcon>('#group_2_expand');
76    this.removeCollectIcon1 = this.shadowRoot?.querySelector<LitIcon>('#group_1_collect');
77    this.removeCollectIcon2 = this.shadowRoot?.querySelector<LitIcon>('#group_2_collect');
78    this.rootEl = this.shadowRoot?.querySelector<HTMLDivElement>('.root');
79    this.canvas = this.shadowRoot?.querySelector<HTMLCanvasElement>('.panel-canvas');
80    this.canvasCtx = this.canvas?.getContext('2d');
81    window.subscribe(window.SmartEvent.UI.RowHeightChange, (data: { expand: number; value: number }) => {
82      this.resizeHeight();
83      if (!data.expand) {
84        let offset = this.scrollTop - data.value;
85        offset = offset < 0 ? 0 : offset;
86        this.scrollTop = offset;
87      }
88      this.refreshFavoriteCanvas();
89    });
90    this.initChartListListener();
91  }
92
93  private initChartListListener(): void {
94    this.icon1?.addEventListener('click', () => {
95      this.collect1Expand = !this.collect1Expand;
96      if (this.collect1Expand) {
97        this.icon1!.style.transform = 'rotateZ(0deg)';
98        this.collectEl1?.appendChild(this.fragmentGroup1);
99      } else {
100        this.icon1!.style.transform = 'rotateZ(-90deg)';
101        this.collectRowList1.forEach((row) => this.fragmentGroup1.appendChild(row));
102      }
103      this.resizeHeight();
104    });
105    this.icon2?.addEventListener('click', () => {
106      this.collect2Expand = !this.collect2Expand;
107      if (this.collect2Expand) {
108        this.icon2!.style.transform = 'rotateZ(0deg)';
109        this.collectEl2?.appendChild(this.fragmentGroup2);
110        this.resizeHeight();
111        this.scrollTop = this.scrollHeight;
112      } else {
113        this.icon2!.style.transform = 'rotateZ(-90deg)';
114        this.collectRowList2.forEach((row) => this.fragmentGroup2.appendChild(row));
115        this.resizeHeight();
116        this.scrollTop = 0;
117      }
118    });
119    this.removeCollectIcon1?.addEventListener('click', () => {
120      for (let i = 0; i < this.collectRowList1.length; i++) {
121        this.collectRowList1[i].collectEL?.click();
122        i--;
123      }
124    });
125    this.removeCollectIcon2?.addEventListener('click', () => {
126      for (let i = 0; i < this.collectRowList2.length; i++) {
127        this.collectRowList2[i].collectEL?.click();
128        i--;
129      }
130    });
131  }
132
133  private resizeHeight(): void {
134    this.maxHeight = 0;
135    this.collectEl1!.childNodes.forEach((item) => (this.maxHeight += (item as any).clientHeight));
136    this.collectEl2!.childNodes.forEach((item) => (this.maxHeight += (item as any).clientHeight));
137    if (this.groupTitle1) {
138      this.maxHeight += this.groupTitle1.clientHeight;
139    }
140    if (this.groupTitle2) {
141      this.maxHeight += this.groupTitle2.clientHeight;
142    }
143
144    this.maxHeight = Math.min(this.getMaxLimitHeight(), this.maxHeight);
145    if (this.manualHeight > 0) {
146      this.style.height = `${Math.min(this.maxHeight, this.manualHeight)}px`;
147    } else {
148      this.style.height = `${this.maxHeight}px`;
149    }
150  }
151
152  private getMaxLimitHeight(): number {
153    return (this.parentElement!.clientHeight - topHeight) * maxScale;
154  }
155
156  getCollectRows(filter?: (row: TraceRow<any>) => boolean): Array<TraceRow<any>> | [] {
157    if (filter) {
158      return [...this.collectRowList1.filter(filter), ...this.collectRowList2.filter(filter)];
159    } else {
160      return this.getAllCollectRows();
161    }
162  }
163
164  expandSearchRowGroup(row: TraceRow<any>): void {
165    this.updateGroupDisplay();
166    if (row.collectGroup === SpChartList.COLLECT_G1) {
167      if (!this.collect1Expand) {
168        this.collect1Expand = true;
169        this.icon1!.style.transform = 'rotateZ(0deg)';
170        this.collectEl1?.appendChild(this.fragmentGroup1);
171      }
172    } else {
173      if (!this.collect2Expand) {
174        this.collect2Expand = true;
175        this.icon2!.style.transform = 'rotateZ(0deg)';
176        this.collectEl2?.appendChild(this.fragmentGroup2);
177        this.scrollTop = this.scrollHeight;
178      }
179    }
180    this.resizeHeight();
181  }
182
183  getCollectRow(filter: (row: TraceRow<any>) => boolean): TraceRow<any> | undefined {
184    return this.collectRowList1.find(filter) || this.collectRowList2.find(filter);
185  }
186
187  getAllCollectRows(): Array<TraceRow<any>> {
188    return [...this.collectRowList1, ...this.collectRowList2];
189  }
190
191  getAllSelectCollectRows(): Array<TraceRow<any>> {
192    const rows: Array<TraceRow<any>> = [];
193    for (const row of this.collectRowList1) {
194      if (row.checkType === '2') {
195        rows.push(row);
196      }
197    }
198    for (const row of this.collectRowList2) {
199      if (row.checkType === '2') {
200        rows.push(row);
201      }
202    }
203    return rows;
204  }
205
206  insertRowBefore(node: Node, child: Node): void {
207    if (child === null || (child as TraceRow<any>).collectGroup === (node as TraceRow<any>).collectGroup) {
208      if ((node as TraceRow<any>).collectGroup === SpChartList.COLLECT_G1) {
209        this.collectEl1!.insertBefore(node, child);
210        this.collectRowList1 = Array.from(this.collectEl1!.children) as TraceRow<any>[];
211      } else {
212        this.collectEl2!.insertBefore(node, child);
213        this.collectRowList2 = Array.from(this.collectEl2!.children) as TraceRow<any>[];
214      }
215    }
216  }
217
218  reset(): void {
219    this.maxHeight = 0;
220    this.clearRect();
221    this.collect1Expand = true;
222    this.collect2Expand = true;
223    this.icon1!.style.transform = 'rotateZ(0deg)';
224    this.icon2!.style.transform = 'rotateZ(0deg)';
225    this.collectRowList1.forEach((row) => {
226      row.clearMemory();
227    });
228    this.collectRowList2.forEach((row) => {
229      row.clearMemory();
230    });
231    this.collectRowList1 = [];
232    this.collectRowList2 = [];
233    this.fragmentGroup1 = document.createDocumentFragment();
234    this.fragmentGroup2 = document.createDocumentFragment();
235    this.collectEl1!.innerHTML = '';
236    this.collectEl2!.innerHTML = '';
237    this.updateGroupDisplay();
238    this.style.height = 'auto';
239  }
240
241  context(): CanvasRenderingContext2D | undefined | null {
242    return this.canvasCtx;
243  }
244
245  getCanvas(): HTMLCanvasElement | null | undefined {
246    return this.canvas;
247  }
248
249  connectedCallback(): void {
250    super.connectedCallback();
251    window.addEventListener('mousedown', this.onMouseDown);
252    window.addEventListener('mouseup', this.onMouseUp);
253    window.addEventListener('mousemove', this.onMouseMove);
254    this.addEventListener('scroll', this.onScroll, { passive: true });
255  }
256
257  disconnectedCallback(): void {
258    super.disconnectedCallback();
259    window.removeEventListener('mousedown', this.onMouseDown);
260    window.removeEventListener('mouseup', this.onMouseUp);
261    window.removeEventListener('mousemove', this.onMouseMove);
262    this.removeEventListener('scroll', this.onScroll);
263  }
264
265  onScroll = (ev: Event): void => {
266    this.canvas!.style.transform = `translateY(${this.scrollTop}px)`;
267    if (this.scrollTimer) {
268      clearTimeout(this.scrollTimer);
269    }
270    this.scrollTimer = setTimeout(() => {
271      TraceRow.range!.refresh = true;
272      window.publish(window.SmartEvent.UI.RefreshCanvas, {});
273    }, 100);
274    window.publish(window.SmartEvent.UI.RefreshCanvas, {});
275  };
276
277  onMouseDown = (ev: MouseEvent): void => {
278    this.isPress = true;
279    this.startPageY = ev.pageY;
280    this.startClientHeight = this.clientHeight;
281    if (this.containPoint(ev)) {
282      if (
283        this.getBoundingClientRect().bottom > ev.pageY - mouseMoveRange &&
284        this.getBoundingClientRect().bottom < ev.pageY + mouseMoveRange
285      ) {
286        this.style.cursor = 'row-resize';
287        this.canResize = true;
288      } else {
289        this.style.cursor = 'default';
290        this.canResize = false;
291      }
292      (window as any).collectResize = this.canResize;
293    }
294  };
295
296  onMouseMove = (ev: MouseEvent): void => {
297    if (this.containPoint(ev)) {
298      let inResizeArea =
299        this.getBoundingClientRect().bottom > ev.pageY - mouseMoveRange &&
300        this.getBoundingClientRect().bottom < ev.pageY + mouseMoveRange;
301      if ((this.isPress && this.canResize) || inResizeArea) {
302        this.style.cursor = 'row-resize';
303      } else {
304        this.style.cursor = 'default';
305      }
306    }
307    //防止点击触发move时间
308    if (Math.abs(ev.pageY - this.startPageY) < 2) {
309      return;
310    }
311    if (this.canResize && this.isPress) {
312      (window as any).collectResize = true;
313      // 拖动超过所有泳道最大高度 或小于一个泳道的高度,不支持拖动
314      let newHeight = this.startClientHeight + ev.pageY - this.startPageY;
315      if (newHeight > this.maxHeight) {
316        newHeight = this.maxHeight;
317      }
318      if (newHeight > this.getMaxLimitHeight()) {
319        newHeight = this.getMaxLimitHeight();
320      }
321      if (newHeight < minHeight) {
322        newHeight = minHeight;
323      }
324      this!.style.height = `${newHeight}px`;
325      this.manualHeight = newHeight;
326    } else {
327      (window as any).collectResize = false;
328    }
329  };
330
331  onMouseUp = (ev: MouseEvent): void => {
332    this.isPress = false;
333    this.canResize = false;
334    this.style.cursor = 'default';
335    (window as any).collectResize = false;
336    this.refreshFavoriteCanvas();
337  };
338
339  insertRow(row: TraceRow<any>, group: string, updateGroup: boolean): void {
340    this.style.display = 'flex';
341    let collectGroup = !updateGroup && row.collectGroup ? row.collectGroup : group;
342    if (row.collectGroup !== SpChartList.COLLECT_G1 && row.collectGroup !== SpChartList.COLLECT_G2) {
343      row.collectGroup = group;
344    }
345    if (updateGroup) {
346      row.collectGroup = group;
347    }
348    if (collectGroup === SpChartList.COLLECT_G1) {
349      if (!this.collect1Expand) {
350        this.collect1Expand = true;
351        this.icon1!.style.transform = 'rotateZ(0deg)';
352      }
353      if (this.collectRowList1.indexOf(row) === -1) {
354        this.collectRowList1.push(row);
355      }
356      if (!this.fragmentGroup1.contains(row)) {
357        this.fragmentGroup1.appendChild(row);
358      }
359      this.collectEl1?.appendChild(this.fragmentGroup1);
360      this.scrollTo({ top: this.collectEl1?.clientHeight });
361    } else {
362      if (!this.collect2Expand) {
363        this.collect2Expand = true;
364        this.icon2!.style.transform = 'rotateZ(0deg)';
365      }
366      if (this.collectRowList2.indexOf(row) === -1) {
367        this.collectRowList2.push(row);
368      }
369      if (!this.fragmentGroup2.contains(row)) {
370        this.fragmentGroup2.appendChild(row);
371      }
372      this.collectEl2!.appendChild(this.fragmentGroup2);
373      this.scrollTo({ top: this.scrollHeight });
374    }
375    this.updateGroupDisplay();
376    this.resizeHeight();
377    this.refreshFavoriteCanvas();
378    row.currentContext = this.canvasCtx;
379  }
380
381  deleteRow(row: TraceRow<any>, clearCollectGroup: boolean): void {
382    if (row.collectGroup === SpChartList.COLLECT_G1) {
383      this.collectRowList1.splice(this.collectRowList1.indexOf(row), 1);
384      if (!this.fragmentGroup1.contains(row)) {
385        this.fragmentGroup1.appendChild(row);
386      }
387      this.fragmentGroup1.removeChild(row);
388    } else {
389      this.collectRowList2.splice(this.collectRowList2.indexOf(row), 1);
390      if (!this.fragmentGroup2.contains(row)) {
391        this.fragmentGroup2.appendChild(row);
392      }
393      this.fragmentGroup2.removeChild(row);
394    }
395    if (clearCollectGroup) {
396      row.collectGroup = undefined;
397    }
398    this.updateGroupDisplay();
399    this.resizeHeight();
400    this.scrollTop = 0;
401    this.refreshFavoriteCanvas();
402    row.currentContext = undefined;
403    if (this.collectRowList1.length === 0 && this.collectRowList2.length === 0) {
404      this.style.height = 'auto';
405      this.style.display = 'none';
406      this.manualHeight = 0;
407    }
408  }
409
410  hideCollectArea(): void {
411    if (this.collect1Expand) {
412      this.collectRowList1.forEach((row) => this.fragmentGroup1.appendChild(row));
413    }
414    if (this.collect2Expand) {
415      this.collectRowList2.forEach((row) => this.fragmentGroup2.appendChild(row));
416    }
417    this.groupTitle1!.style.display = 'none';
418    this.groupTitle2!.style.display = 'none';
419    this.resizeHeight();
420  }
421
422  showCollectArea(): void {
423    if (this.collect1Expand) {
424      this.collectEl1?.appendChild(this.fragmentGroup1);
425    }
426    if (this.collect2Expand) {
427      this.collectEl2?.appendChild(this.fragmentGroup2);
428    }
429    this.updateGroupDisplay();
430    this.resizeHeight();
431  }
432
433  updateGroupDisplay(): void {
434    this.groupTitle1!.style.display = this.collectRowList1.length === 0 ? 'none' : 'flex';
435    this.groupTitle2!.style.display = this.collectRowList2.length === 0 ? 'none' : 'flex';
436  }
437
438  hasCollectRow(): boolean {
439    return this.collectRowList2.length > 0 || this.collectRowList1.length > 0;
440  }
441
442  clearRect(): void {
443    this.canvasCtx?.clearRect(0, 0, this.canvas?.clientWidth ?? 0, this.canvas?.clientHeight ?? 0);
444  }
445
446  drawLines(xs: number[] | undefined, color: string): void {
447    drawLines(this.canvasCtx!, xs ?? [], this.clientHeight, color);
448  }
449
450  drawFlagLineSegment(
451    hoverFlag: Flag | undefined | null,
452    selectFlag: Flag | undefined | null,
453    tse: TimerShaftElement
454  ): void {
455    drawFlagLineSegment(
456      this.canvasCtx,
457      hoverFlag,
458      selectFlag,
459      {
460        x: 0,
461        y: 0,
462        width: TraceRow.FRAME_WIDTH,
463        height: this.canvas?.clientHeight,
464      },
465      tse
466    );
467  }
468
469  drawWakeUp(): void {
470    drawWakeUp(
471      this.canvasCtx,
472      CpuStruct.wakeupBean,
473      TraceRow.range!.startNS,
474      TraceRow.range!.endNS,
475      TraceRow.range!.totalNS,
476      {
477        x: 0,
478        y: 0,
479        width: TraceRow.FRAME_WIDTH,
480        height: this.canvas!.clientHeight!,
481      } as Rect
482    );
483  }
484
485  drawWakeUpList(bean: WakeupBean): void {
486    drawWakeUpList(this.canvasCtx, bean, TraceRow.range!.startNS, TraceRow.range!.endNS, TraceRow.range!.totalNS, {
487      x: 0,
488      y: 0,
489      width: TraceRow.FRAME_WIDTH,
490      height: this.canvas!.clientHeight!,
491    } as Rect);
492  }
493
494  drawLogsLineSegment(bean: Flag | null | undefined, timeShaft: TimerShaftElement): void {
495    drawLogsLineSegment(
496      this.canvasCtx,
497      bean,
498      {
499        x: 0,
500        y: 0,
501        width: TraceRow.FRAME_WIDTH,
502        height: this.canvas!.clientHeight,
503      },
504      timeShaft
505    );
506  }
507
508  drawLinkLines(nodes: PairPoint[][], tse: TimerShaftElement, isFavorite: boolean, favoriteHeight: number): void {
509    drawLinkLines(this.canvasCtx!, nodes, tse, isFavorite, favoriteHeight);
510  }
511
512  refreshFavoriteCanvas(): void {
513    this.canvas!.style.width = `${this.clientWidth - 248}px`;
514    this.canvas!.style.left = `248px`;
515    this.canvas!.width = this.canvas?.clientWidth! * dpr();
516    this.canvas!.height = this.clientHeight * dpr();
517    this.canvas!.getContext('2d')!.scale(dpr(), dpr());
518    window.publish(window.SmartEvent.UI.RefreshCanvas, {});
519  }
520
521  private getHtmlCss(): string {
522    return `<style>
523    :host{
524        display: none;
525        width: 100%;
526        height: auto;
527        overflow-anchor: none;
528        z-index: 1;
529        box-shadow: 0 10px 10px #00000044;
530        position: relative;
531        overflow: auto;
532        overflow-x: hidden;
533        scroll-behavior: smooth;
534    }
535    .root{
536        width: 100%;
537        box-sizing: border-box;
538    }
539    .panel-canvas{
540        position: absolute;
541        top: 0;
542        right: 0;
543        bottom: 0;
544        box-sizing: border-box;
545    }
546    .icon:hover {
547     color:#ecb93f;
548    }
549    .icon {
550        margin-right: 10px;
551        cursor: pointer;
552    }
553    </style>`;
554  }
555
556  initHtml(): string {
557    return `
558 ${this.getHtmlCss()}
559<canvas id="canvas-panel" class="panel-canvas" ondragstart="return false"></canvas>
560<div class="root">
561    <div id="group-1-title" style="background-color: #efefef;padding: 10px;align-items: center">
562        <lit-icon id="group_1_expand" class="icon" name="caret-down" size="19"></lit-icon>
563        <span style="width: 184px;font-size: 10px;color: #898989">G1</span>
564        <lit-icon id="group_1_collect" name="star-fill" style="color: #5291FF;cursor: pointer" size="19"></lit-icon>
565    </div>
566    <div id="collect-group-1"></div>
567    <div id="group-2-title" style="background-color: #efefef;padding: 10px;align-items: center">
568        <lit-icon id="group_2_expand" class="icon" name="caret-down" size="19"></lit-icon>
569        <span style="width: 184px;font-size: 10px;color: #898989">G2</span>
570        <lit-icon id="group_2_collect" name="star-fill" style="color: #f56940;cursor: pointer" size="19"></lit-icon>
571    </div>
572    <div id="collect-group-2"></div>
573</div>
574`;
575  }
576}
577