• 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 { RangeSelectStruct, TraceRow } from './TraceRow';
17import { Rect } from '../timer-shaft/Rect';
18import { TimerShaftElement } from '../TimerShaftElement';
19import { info } from '../../../../log/Log';
20import './Extension';
21import { SpSystemTrace } from '../../SpSystemTrace';
22import { SpLtpoChart } from '../../chart/SpLTPO';
23import { isEmpty, isNotEmpty } from './Extension';
24import { SpAiAnalysisPage } from '../../SpAiAnalysisPage';
25
26export class RangeSelect {
27  private rowsEL: HTMLDivElement | undefined | null;
28  private rowsPaneEL: HTMLDivElement | undefined | null;
29  isMouseDown: boolean = false; // @ts-ignore
30  public rangeTraceRow: Array<TraceRow<unknown>> | undefined; // @ts-ignore
31  public selectHandler: ((ds: Array<TraceRow<unknown>>, refreshCheckBox: boolean) => void) | undefined;
32  private startPageX: number = 0;
33  private startPageY: number = 0;
34  private endPageX: number = 0;
35  private endPageY: number = 0;
36  private timerShaftEL: TimerShaftElement | null | undefined;
37  private isHover: boolean = false;
38  private movingMark: string = '';
39  private mark: { startMark: number; endMark: number } = {
40    startMark: 0,
41    endMark: 0,
42  };
43  private trace: SpSystemTrace | null | undefined;
44  drag = false;
45
46  constructor(trace: SpSystemTrace | null | undefined) {
47    this.trace = trace;
48    this.timerShaftEL = trace?.timerShaftEL;
49    this.rowsEL = trace?.rowsEL;
50    this.rowsPaneEL = trace?.rowsPaneEL;
51  }
52
53  isInRowsEl(ev: MouseEvent): boolean {
54    return this.rowsPaneEL!.containPoint(ev, { left: 248 });
55  }
56
57  isInSpacerEL(ev: MouseEvent): boolean {
58    return this.trace!.favoriteChartListEL!.containPoint(ev, { left: 248 });
59  }
60
61  mouseDown(eventDown: MouseEvent): void {
62    this.startPageX = eventDown.pageX;
63    this.startPageY = eventDown.pageY;
64    if (TraceRow.rangeSelectObject) {
65      this.handleTouchMark(eventDown);
66    } else {
67      this.isHover = false;
68    }
69    if (this.isHover) {
70      this.isMouseDown = true;
71      return;
72    }
73    this.rangeTraceRow = [];
74    this.isMouseDown = true;
75    TraceRow.rangeSelectObject = undefined;
76  }
77
78  mouseUp(mouseEventUp?: MouseEvent): void {
79    if (mouseEventUp) {
80      this.endPageX = mouseEventUp.pageX;
81      this.endPageY = mouseEventUp.pageY;
82    }
83    if (!this.trace!.isInSheet && this.drag) {
84      if (this.selectHandler) {
85        this.selectHandler(this.rangeTraceRow || [], !this.isHover);
86      }
87      //查询render_service数据
88      if (this.rangeTraceRow?.length) {
89        this.checkRowsName(this.rangeTraceRow);
90      }
91    }
92    this.isMouseDown = false;
93    this.isHover = false;
94  }
95  // @ts-ignore
96  checkRowsName(rowList: Array<TraceRow<unknown>>): void {
97    rowList.forEach((row): void => {
98      if (
99        row.getAttribute('row-type') === 'func' &&
100        row.parentRowEl?.getAttribute('name')?.startsWith('render_service')
101      ) {
102        row.frameRateList = [];
103        if (row.getAttribute('name')?.startsWith('render_service')) {
104          this.filterRateData(row, this.trace?.docomList);
105        } else if (row.getAttribute('name')?.startsWith('RSHardwareThrea')) {
106          this.filterRateData(row, this.trace?.repaintList);
107        } else if (row.getAttribute('name')?.startsWith('Present')) {
108          this.filterPresentData(row, this.trace?.presentList);
109        }
110      } else if (
111        row.getAttribute('row-type') === 'process' &&
112        row.getAttribute('name')?.startsWith('render_service') &&
113        Array.isArray(row.childrenList)
114      ) {
115        this.checkRowsName(row.childrenList);
116      }
117    });
118  }
119
120  // 过滤处理数据
121  // @ts-ignore
122  filterRateData(row: TraceRow<unknown>, data: unknown): void {
123    // @ts-ignore
124    data.forEach((it: unknown): void => {
125      if (
126        // @ts-ignore
127        it.startTime >= TraceRow.rangeSelectObject!.startNS! && // @ts-ignore
128        it.startTime <= TraceRow.rangeSelectObject!.endNS! && // @ts-ignore
129        Number(row.rowId) === Number(it.tid)
130      ) {
131        // @ts-ignore
132        row.frameRateList?.push(it.startTime);
133      }
134    });
135    if (row.frameRateList?.length) {
136      if (row.frameRateList.length < 2) {
137        row.frameRateList = [];
138      } else {
139        const CONVERT_SECONDS = 1000000000;
140        let cutres: number = row.frameRateList[row.frameRateList.length - 1] - row.frameRateList[0];
141        row.avgRateTxt = `${(((row.frameRateList.length - 1) / cutres) * CONVERT_SECONDS).toFixed(1)}fps`;
142      }
143    }
144  }
145
146  // 过滤并处理present数据
147  // @ts-ignore
148  filterPresentData(row: TraceRow<unknown>, data: unknown): void {
149    // @ts-ignore
150    data.forEach((it: unknown): void => {
151      if (
152        // @ts-ignore
153        it.endTime >= TraceRow.rangeSelectObject!.startNS! && // @ts-ignore
154        it.endTime <= TraceRow.rangeSelectObject!.endNS! && // @ts-ignore
155        Number(row.rowId) === Number(it.tid)
156      ) {
157        // @ts-ignore
158        row.frameRateList?.push(it.endTime);
159      }
160    });
161    if (row.frameRateList?.length) {
162      if (row.frameRateList?.length < 2) {
163        row.frameRateList = [];
164      } else {
165        if (row.frameRateList[row.frameRateList.length - 1] === null) {
166          row.frameRateList.pop();
167        }
168        let hitchTimeList: Array<number> = [];
169        for (let i = 0; i < SpLtpoChart.sendHitchDataArr.length; i++) {
170          if (
171            SpLtpoChart.sendHitchDataArr[i].startTs! >= row.frameRateList[0]! &&
172            SpLtpoChart.sendHitchDataArr[i].startTs! < row.frameRateList[row.frameRateList.length - 1]!
173          ) {
174            hitchTimeList.push(SpLtpoChart.sendHitchDataArr[i].value!);
175          } else if (SpLtpoChart.sendHitchDataArr[i].startTs! >= row.frameRateList[row.frameRateList.length - 1]!) {
176            break;
177          }
178        }
179        const CONVERT_SECONDS = 1000000000;
180        let cutres: number = row.frameRateList[row.frameRateList.length - 1] - row.frameRateList[0];
181        let avgRate: string = `${(((row.frameRateList.length - 1) / cutres) * CONVERT_SECONDS).toFixed(1)}fps`;
182        let sum: number = hitchTimeList.reduce((accumulator, currentValue) => accumulator + currentValue, 0); // ∑hitchTimeData
183        let hitchRate: number =
184          sum / ((TraceRow.rangeSelectObject!.endNS! - TraceRow.rangeSelectObject!.startNS!) / 1000000000);
185        row.avgRateTxt =
186          `${avgRate} ` + ',' + ' ' + 'HitchTime:' + ` ${sum.toFixed(1)}ms` + ' ' + ',' + ` ${hitchRate.toFixed(2)}ms/s`;
187      }
188    }
189  }
190
191  isDrag(): boolean {
192    return this.startPageX !== this.endPageX;
193  }
194
195  isTouchMark(ev: MouseEvent): boolean {
196    let notTimeHeight: boolean = this.rowsPaneEL!.containPoint(ev, {
197      left: 248,
198      top: -45,
199    });
200    if (!notTimeHeight) {
201      return false;
202    }
203    if ((isEmpty(this.rangeTraceRow) ?? false) && !this.isMouseDown) {
204      this.isHover = false;
205    }
206    return notTimeHeight && (isNotEmpty(this.rangeTraceRow) ?? false) && !this.isMouseDown;
207  }
208
209  mouseOut(mouseEventOut: MouseEvent): void {
210    this.endPageX = mouseEventOut.pageX;
211    this.endPageY = mouseEventOut.pageY;
212    if (this.drag) {
213      if (this.selectHandler && this.isMouseDown) {
214        this.selectHandler(this.rangeTraceRow || [], !this.isHover);
215      }
216    }
217    document.getSelection()?.removeAllRanges();
218    this.isMouseDown = false;
219    this.isHover = false;
220  }
221  // @ts-ignore
222  mouseMove(rows: Array<TraceRow<unknown>>, ev: MouseEvent): void {
223    this.endPageX = ev.pageX;
224    this.endPageY = ev.pageY;
225    if (this.isTouchMark(ev) && TraceRow.rangeSelectObject) {
226      this.handleTouchMark(ev);
227    } else {
228      document.body.style.cursor = 'default';
229    }
230    if (this.isHover && this.isMouseDown) {
231      this.handleRangeSelectAndDraw(rows, ev);
232      return;
233    }
234    if (!this.isMouseDown) {
235      this.isHover = false;
236      this.handleDrawForNotMouseDown();
237      return;
238    }
239    this.handleRangeSelect(rows);
240    this.timerShaftEL!.sportRuler!.isRangeSelect = this.rangeTraceRow!.length > 0;
241    this.timerShaftEL!.sportRuler!.draw();
242  }
243  // @ts-ignore
244  private handleRangeSelect(rows: Array<TraceRow<unknown>>): void {
245    let rangeSelect: RangeSelectStruct | undefined;
246    let favoriteRect = this.trace?.favoriteChartListEL?.getBoundingClientRect();
247    let favoriteLimit = favoriteRect!.top + favoriteRect!.height;
248    this.rangeTraceRow = rows.filter((it): boolean => {
249      let domRect = it.getBoundingClientRect();
250      let itRect = { x: domRect.x, y: domRect.y, width: domRect.width, height: domRect.height } as Rect;
251      if (itRect.y < favoriteLimit && !it.collect) {
252        let offset = favoriteLimit - itRect.y;
253        itRect.y = itRect.y + offset;
254        itRect.height = itRect.height - offset;
255      }
256      if (it.sticky) {
257        itRect.y = 0;
258        itRect.height = 0;
259      }
260      let result: boolean;
261      if (this.isIntersect(itRect, it.collect, favoriteLimit)) {
262        if (!rangeSelect) {
263          it.setTipLeft(0, null);
264          rangeSelect = new RangeSelectStruct();
265          let startX = Math.min(this.startPageX, this.endPageX) - it.describeEl!.getBoundingClientRect().right;
266          let endX = Math.max(this.startPageX, this.endPageX) - it.describeEl!.getBoundingClientRect().right;
267          if (startX <= 0) {
268            startX = 0;
269          }
270          if (endX > it.frame.width) {
271            endX = it.frame.width;
272          }
273          rangeSelect.startX = startX;
274          rangeSelect.endX = endX;
275          rangeSelect.startNS = RangeSelect.SetNS(it, startX);
276          rangeSelect.endNS = RangeSelect.SetNS(it, endX);
277        }
278        TraceRow.rangeSelectObject = rangeSelect;
279        SpAiAnalysisPage.selectChangeListener(rangeSelect.startNS!, rangeSelect.endNS!);
280        it.rangeSelect = true;
281        result = true;
282      } else {
283        it.rangeSelect = false;
284        result = false;
285      }
286      return result;
287    });
288    this.updateRangeSelectionState();
289  }
290
291  private isIntersect(itRect: Rect, collect: boolean, favoriteLimit: number): boolean {
292    return (
293      Rect.intersect(itRect, {
294        x: Math.min(this.startPageX, this.endPageX),
295        y: Math.min(this.startPageY, this.endPageY),
296        width: Math.abs(this.startPageX - this.endPageX),
297        height: Math.abs(this.startPageY - this.endPageY),
298      } as Rect) && //所有框选情况 1.只框选收藏泳道
299      ((collect && this.startPageY < favoriteLimit) ||
300        // 2.只框选非收藏泳道
301        (!collect && this.startPageY > favoriteLimit) ||
302        // 3.框选收藏泳道和非收藏泳道 从上往下框
303        (this.startPageY < favoriteLimit && this.endPageY > favoriteLimit) ||
304        // 4.框选收藏泳道和非收藏泳道 从下往上框
305        (this.endPageY < favoriteLimit && this.startPageY > favoriteLimit))
306    );
307  }
308
309  private updateRangeSelectionState(): void {
310    if (this.rangeTraceRow && this.rangeTraceRow.length) {
311      if (this.rangeTraceRow[0].parentRowEl) {
312        for (let i = 0; i < this.rangeTraceRow[0].parentRowEl.childrenList.length; i++) {
313          this.rangeTraceRow[0].parentRowEl.childrenList[i].frameRateList = [];
314          this.rangeTraceRow[0].parentRowEl.childrenList[i].avgRateTxt = null;
315        }
316      }
317    }
318  }
319
320  private handleDrawForNotMouseDown(): void {
321    this.timerShaftEL!.sportRuler!.isRangeSelect = isNotEmpty(this.rangeTraceRow) ?? false;
322    this.timerShaftEL!.sportRuler!.draw();
323  }
324  // @ts-ignore
325  private handleRangeSelectAndDraw(rows: Array<TraceRow<unknown>>, ev: MouseEvent): void {
326    let rangeSelect: RangeSelectStruct | undefined;
327    let result: boolean;
328    this.rangeTraceRow = rows.filter((it) => {
329      if (it.rangeSelect) {
330        if (!rangeSelect) {
331          rangeSelect = new RangeSelectStruct();
332          let mouseX = ev.pageX - this.rowsEL!.getBoundingClientRect().left - 248;
333          mouseX = mouseX < 0 ? 0 : mouseX;
334          let markA = this.movingMark === 'markA' ? mouseX : this.mark.startMark;
335          let markB = this.movingMark === 'markB' ? mouseX : this.mark.endMark;
336          let startX = markA < markB ? markA : markB;
337          let endX = markB < markA ? markA : markB;
338          rangeSelect.startX = startX;
339          rangeSelect.endX = endX;
340          rangeSelect.startNS = RangeSelect.SetNS(it, startX);
341          rangeSelect.endNS = RangeSelect.SetNS(it, endX);
342          if (rangeSelect.startNS <= TraceRow.range!.startNS) {
343            rangeSelect.startNS = TraceRow.range!.startNS;
344          }
345          if (rangeSelect.endNS >= TraceRow.range!.endNS) {
346            rangeSelect.endNS = TraceRow.range!.endNS;
347          }
348          if (startX < 0) {
349            rangeSelect.startNS = TraceRow.rangeSelectObject!.startNS!;
350          } // @ts-ignore
351          if (endX > it.frame.width) {
352            rangeSelect.endNS = TraceRow.rangeSelectObject!.endNS!;
353          }
354        }
355        TraceRow.rangeSelectObject = rangeSelect;
356        SpAiAnalysisPage.selectChangeListener(rangeSelect.startNS!, rangeSelect.endNS!);
357        result = true;
358      } else {
359        result = false;
360      }
361      return result;
362    });
363    this.timerShaftEL!.sportRuler!.isRangeSelect = (this.rangeTraceRow?.length || 0) > 0;
364    this.timerShaftEL!.sportRuler!.draw();
365  }
366
367  private handleTouchMark(ev: MouseEvent): void {
368    info('isTouchMark');
369    let x1 =
370      ((TraceRow.rangeSelectObject!.startNS! - TraceRow.range!.startNS) *
371        (this.timerShaftEL?.canvas?.clientWidth || 0)) /
372      (TraceRow.range!.endNS - TraceRow.range!.startNS);
373    let x2 =
374      ((TraceRow.rangeSelectObject!.endNS! - TraceRow.range!.startNS) * (this.timerShaftEL?.canvas?.clientWidth || 0)) /
375      (TraceRow.range!.endNS - TraceRow.range!.startNS);
376    this.mark = { startMark: x1, endMark: x2 };
377    let mouseX = ev.pageX - this.rowsPaneEL!.getBoundingClientRect().left - 248;
378    if (mouseX > x1 - 5 && mouseX < x1 + 5) {
379      this.isHover = true;
380      document.body.style.cursor = 'ew-resize';
381      this.movingMark = x1 < x2 ? 'markA' : 'markB';
382    } else if (mouseX > x2 - 5 && mouseX < x2 + 5) {
383      this.isHover = true;
384      document.body.style.cursor = 'ew-resize';
385      this.movingMark = x2 < x1 ? 'markA' : 'markB';
386    } else {
387      this.isHover = false;
388      document.body.style.cursor = 'default';
389    }
390  }
391  // @ts-ignore
392  static SetNS(row: TraceRow<unknown>, num: number): number {
393    return Math.floor(
394      // @ts-ignore
395      ((TraceRow.range!.endNS - TraceRow.range!.startNS) * num) / row.frame.width + TraceRow.range!.startNS!
396    );
397  }
398}
399