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