• 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 {Graph} from "./Graph.js";
17import {Rect} from "./Rect.js";
18import {ns2s} from "../TimerShaftElement.js";
19import {ColorUtils} from "../base/ColorUtils.js";
20import {CpuStruct} from "../../../bean/CpuStruct.js";
21
22const markPadding = 5;
23
24export class Mark extends Graph {
25    get isHover(): boolean {
26        return this._isHover;
27    }
28
29    set isHover(value: boolean) {
30        this._isHover = value;
31        if (value) {
32            document.body.style.cursor = 'ew-resize'
33        } else {
34            document.body.style.cursor = 'default'
35        }
36    }
37
38    name: string | undefined
39    private _isHover: boolean = false
40    inspectionFrame: Rect
41
42    constructor(canvas: HTMLCanvasElement | undefined | null, name: string, c: CanvasRenderingContext2D, frame: Rect) {
43        super(canvas, c, frame);
44        this.name = name;
45        this.inspectionFrame = new Rect(frame.x - markPadding, frame.y, frame.width + markPadding * 2, frame.height)
46    }
47
48    draw(): void {
49        this.c.beginPath();
50        this.c.lineWidth = 7
51        this.c.strokeStyle = '#999999'
52        this.c.moveTo(this.frame.x, this.frame.y);
53        this.c.lineTo(this.frame.x, this.frame.y + this.frame.height / 3)
54        this.c.stroke();
55        this.c.lineWidth = 1
56        this.c.strokeStyle = '#999999'
57        this.c.moveTo(this.frame.x, this.frame.y);
58        this.c.lineTo(this.frame.x, this.frame.y + this.frame.height)
59        this.c.stroke();
60        this.c.closePath();
61    }
62}
63
64export interface TimeRange {
65    totalNS: number
66    startX: number
67    endX: number
68    startNS: number
69    endNS: number
70    xs: Array<number>
71    xsTxt: Array<string>
72}
73
74export class RangeRuler extends Graph {
75    public rangeRect: Rect
76    public markA: Mark
77    public markB: Mark
78    public range: TimeRange;
79    private readonly notifyHandler: (r: TimeRange) => void;
80    private scale: number = 0;
81    private scales: Array<number> = [50, 100, 200, 500, 1_000, 2_000, 5_000, 10_000, 20_000, 50_000, 100_000, 200_000, 500_000,
82        1_000_000, 2_000_000, 5_000_000, 10_000_000, 20_000_000, 50_000_000, 100_000_000, 200_000_000, 500_000_000,
83        1_000_000_000, 2_000_000_000, 5_000_000_000, 10_000_000_000, 20_000_000_000, 50_000_000_000,
84        100_000_000_000, 200_000_000_000, 500_000_000_000];
85
86    constructor(canvas: HTMLCanvasElement | undefined | null, c: CanvasRenderingContext2D, frame: Rect, range: TimeRange, notifyHandler: (r: TimeRange) => void) {
87        super(canvas, c, frame)
88        this.range = range;
89        this.notifyHandler = notifyHandler;
90        this.markA = new Mark(canvas, 'A', c, new Rect(range.startX, frame.y, 1, frame.height))
91        this.markB = new Mark(canvas, 'B', c, new Rect(range.endX, frame.y, 1, frame.height))
92        this.rangeRect = new Rect(range.startX, frame.y, range.endX - range.startX, frame.height)
93    }
94
95    private _cpuUsage: Array<{ cpu: number, ro: number, rate: number }> = []
96
97    set cpuUsage(value: Array<{ cpu: number, ro: number, rate: number }>) {
98        this._cpuUsage = value
99        this.draw();
100    }
101
102    drawCpuUsage() {
103        let maxNum = Math.round(this._cpuUsage.length / 100)
104        let miniHeight = Math.round(this.frame.height / CpuStruct.cpuCount);
105        let miniWidth = Math.ceil(this.frame.width / 100);
106        for (let i = 0; i < this._cpuUsage.length; i++) {
107            let it = this._cpuUsage[i]
108            this.c.fillStyle = ColorUtils.MD_PALETTE[it.cpu]
109            this.c.globalAlpha = it.rate
110            this.c.fillRect(this.frame.x + miniWidth * it.ro, this.frame.y + it.cpu * miniHeight, miniWidth, miniHeight)
111        }
112    }
113
114    draw(discardNotify: boolean = false): void {
115        this.c.clearRect(this.frame.x - markPadding, this.frame.y, this.frame.width + markPadding * 2, this.frame.height)
116        this.c.beginPath();
117        if (this._cpuUsage.length > 0) {
118            this.drawCpuUsage()
119            this.c.globalAlpha = 0;
120        } else {
121            this.c.globalAlpha = 1;
122        }
123
124        this.c.fillStyle = window.getComputedStyle(this.canvas!, null).getPropertyValue("background-color");
125        this.rangeRect.x = this.markA.frame.x < this.markB.frame.x ? this.markA.frame.x : this.markB.frame.x
126        this.rangeRect.width = Math.abs(this.markB.frame.x - this.markA.frame.x)
127        this.c.fillRect(this.rangeRect.x, this.rangeRect.y, this.rangeRect.width, this.rangeRect.height)
128        this.c.globalAlpha = 1;
129        this.c.globalAlpha = .5;
130        this.c.fillStyle = "#999999"
131        this.c.fillRect(this.frame.x, this.frame.y, this.rangeRect.x, this.rangeRect.height)
132        this.c.fillRect(this.rangeRect.x + this.rangeRect.width, this.frame.y, this.frame.width - this.rangeRect.width, this.rangeRect.height)
133        this.c.globalAlpha = 1;
134        this.c.closePath();
135        this.markA.draw();
136        this.markB.draw();
137        if (this.notifyHandler) {
138            this.range.startX = this.rangeRect.x
139            this.range.endX = this.rangeRect.x + this.rangeRect.width
140            this.range.startNS = this.range.startX * this.range.totalNS / (this.canvas?.clientWidth || 0)
141            this.range.endNS = this.range.endX * this.range.totalNS / (this.canvas?.clientWidth || 0)
142            let l20 = (this.range.endNS - this.range.startNS) / 20;
143            let min = 0;
144            let max = 0;
145            let weight = 0;
146            for (let index = 0; index < this.scales.length; index++) {
147                if (this.scales[index] > l20) {
148                    if (index > 0) {
149                        min = this.scales[index - 1];
150                    } else {
151                        min = 0;
152                    }
153                    max = this.scales[index];
154                    weight = (l20 - min) * 1.0 / (max - min);
155                    if (weight > 0.243) {
156                        this.scale = max;
157                    } else {
158                        this.scale = min;
159                    }
160                    break;
161                }
162            }
163            if (this.scale == 0) {
164                this.scale = this.scales[0];
165            }
166            let tmpNs = 0;
167            let yu = this.range.startNS % this.scale;
168            let realW = (this.scale * this.frame.width) / (this.range.endNS - this.range.startNS);
169            let startX = 0;
170            if (this.range.xs) {
171                this.range.xs.length = 0
172            } else {
173                this.range.xs = []
174            }
175            if (this.range.xsTxt) {
176                this.range.xsTxt.length = 0
177            } else {
178                this.range.xsTxt = []
179            }
180            if (yu != 0) {
181                let firstNodeWidth = ((this.scale - yu) / this.scale * realW);
182                startX += firstNodeWidth;
183                tmpNs += yu;
184                this.range.xs.push(startX)
185                this.range.xsTxt.push(ns2s(tmpNs))
186            }
187            while (tmpNs < this.range.endNS - this.range.startNS) {
188                startX += realW;
189                tmpNs += this.scale;
190                this.range.xs.push(startX)
191                this.range.xsTxt.push(ns2s(tmpNs))
192            }
193            if (!discardNotify) {
194                this.notifyHandler(this.range)
195            }
196        }
197    }
198
199
200    mouseDownOffsetX = 0
201    mouseDownMovingMarkX = 0
202    movingMark: Mark | undefined | null;
203    isMouseDown: boolean = false;
204    isMovingRange: boolean = false;
205    isNewRange: boolean = false;
206    markAX: number = 0;
207    markBX: number = 0;
208    isPress: boolean = false
209    pressFrameId: number = -1
210    currentDuration: number = 0
211
212    mouseDown(ev: MouseEvent) {
213        let x = ev.offsetX - (this.canvas?.offsetLeft || 0)
214        let y = ev.offsetY - (this.canvas?.offsetTop || 0)
215        this.isMouseDown = true;
216        this.mouseDownOffsetX = x;
217        if (this.markA.isHover) {
218            this.movingMark = this.markA;
219            this.mouseDownMovingMarkX = this.movingMark.frame.x || 0
220        } else if (this.markB.isHover) {
221            this.movingMark = this.markB;
222            this.mouseDownMovingMarkX = this.movingMark.frame.x || 0
223        } else {
224            this.movingMark = null;
225        }
226        if (this.rangeRect.containsWithPadding(x, y, 5, 0)) {
227            this.isMovingRange = true;
228            this.markAX = this.markA.frame.x;
229            this.markBX = this.markB.frame.x;
230            document.body.style.cursor = "move"
231        } else if (this.frame.containsWithMargin(x, y, 20, 0, 0, 0) && !this.rangeRect.containsWithMargin(x, y, 0, markPadding, 0, markPadding)) {
232            this.isNewRange = true;
233        }
234    }
235
236    mouseUp(ev: MouseEvent) {
237        this.isMouseDown = false;
238        this.isMovingRange = false;
239        this.isNewRange = false;
240        this.movingMark = null;
241    }
242
243    centerXPercentage: number = 0;
244
245    mouseMove(ev: MouseEvent) {
246        let x = ev.offsetX - (this.canvas?.offsetLeft || 0);
247        let y = ev.offsetY - (this.canvas?.offsetTop || 0)
248        this.centerXPercentage = x / (this.canvas?.clientWidth || 0)
249        if (this.centerXPercentage <= 0) {
250            this.centerXPercentage = 0
251        } else if (this.centerXPercentage >= 1) {
252            this.centerXPercentage = 1
253        }
254        let maxX = this.canvas?.clientWidth || 0
255        if (this.markA.inspectionFrame.contains(x, y)) {
256            this.markA.isHover = true
257        } else if (this.markB.inspectionFrame.contains(x, y)) {
258            this.markB.isHover = true;
259        } else {
260            this.markA.isHover = false;
261            this.markB.isHover = false;
262        }
263        if (this.movingMark) {
264            let result = x - this.mouseDownOffsetX + this.mouseDownMovingMarkX;
265            if (result >= 0 && result <= maxX) {
266                this.movingMark.frame.x = result
267            } else if (result < 0) {
268                this.movingMark.frame.x = 0
269            } else {
270                this.movingMark.frame.x = maxX
271            }
272            this.movingMark.inspectionFrame.x = this.movingMark.frame.x - markPadding
273            requestAnimationFrame(() => this.draw());
274        } else if (this.rangeRect.containsWithPadding(x, y, markPadding, 0)) {
275            document.body.style.cursor = "move"
276        } else if (this.frame.containsWithMargin(x, y, 20, 0, 0, 0) && !this.rangeRect.containsWithMargin(x, y, 0, markPadding, 0, markPadding)) {
277            document.body.style.cursor = "crosshair";
278        }
279        if (this.isMovingRange && this.isMouseDown) {
280            let result = x - this.mouseDownOffsetX;
281            let mA = result + this.markAX
282            let mB = result + this.markBX
283            if (mA >= 0 && mA <= maxX) {
284                this.markA.frame.x = mA
285            } else if (mA < 0) {
286                this.markA.frame.x = 0
287            } else {
288                this.markA.frame.x = maxX
289            }
290            this.markA.inspectionFrame.x = this.markA.frame.x - markPadding
291            if (mB >= 0 && mB <= maxX) {
292                this.markB.frame.x = mB;
293            } else if (mB < 0) {
294                this.markB.frame.x = 0
295            } else {
296                this.markB.frame.x = maxX
297            }
298            this.markB.inspectionFrame.x = this.markB.frame.x - markPadding
299            requestAnimationFrame(() => this.draw());
300        } else if (this.isNewRange) {
301            this.markA.frame.x = this.mouseDownOffsetX;
302            this.markA.inspectionFrame.x = this.mouseDownOffsetX - markPadding;
303            if (x >= 0 && x <= maxX) {
304                this.markB.frame.x = x;
305            } else if (x < 0) {
306                this.markB.frame.x = 0;
307            } else {
308                this.markB.frame.x = maxX;
309            }
310            this.markB.inspectionFrame.x = this.markB.frame.x - markPadding;
311            requestAnimationFrame(() => this.draw());
312        }
313    }
314
315    mouseOut(ev: MouseEvent) {
316        this.movingMark = null;
317    }
318
319    animaStartTime: number | undefined
320    animTime: number = 100;
321    p: number = 800;
322
323    fillX() {
324        if (this.range.startNS < 0) this.range.startNS = 0;
325        if (this.range.endNS < 0) this.range.endNS = 0;
326        if (this.range.endNS > this.range.totalNS) this.range.endNS = this.range.totalNS;
327        if (this.range.startNS > this.range.totalNS) this.range.startNS = this.range.totalNS;
328        this.range.startX = this.range.startNS * (this.canvas?.clientWidth || 0) / this.range.totalNS
329        this.range.endX = this.range.endNS * (this.canvas?.clientWidth || 0) / this.range.totalNS
330        this.markA.frame.x = this.range.startX
331        this.markA.inspectionFrame.x = this.markA.frame.x - markPadding
332        this.markB.frame.x = this.range.endX
333        this.markB.inspectionFrame.x = this.markB.frame.x - markPadding
334    }
335
336    keyPress(ev: KeyboardEvent) {
337        if (this.animaStartTime === undefined) {
338            this.animaStartTime = new Date().getTime();
339        }
340        let startTime = new Date().getTime();
341        let duration = (startTime - this.animaStartTime);
342        if (duration < this.animTime) duration = this.animTime
343        this.currentDuration = duration
344        if (this.isPress) return
345        this.isPress = true
346        switch (ev.key.toLocaleLowerCase()) {
347            case "w":
348                let animW = () => {
349                    if (this.scale === 50) return;
350                    this.range.startNS += (this.centerXPercentage * this.currentDuration * 2 * this.scale / this.p);
351                    this.range.endNS -= ((1 - this.centerXPercentage) * this.currentDuration * 2 * this.scale / this.p);
352                    this.fillX();
353                    this.draw();
354                    this.pressFrameId = requestAnimationFrame(animW)
355                }
356                this.pressFrameId = requestAnimationFrame(animW)
357                break;
358            case "s":
359                let animS = () => {
360                    if (this.range.startNS <= 0 && this.range.endNS >= this.range.totalNS) return;
361                    this.range.startNS -= (this.centerXPercentage * this.currentDuration * 2 * this.scale / this.p);
362                    this.range.endNS += ((1 - this.centerXPercentage) * this.currentDuration * 2 * this.scale / this.p);
363                    this.fillX();
364                    this.draw();
365                    this.pressFrameId = requestAnimationFrame(animS)
366                }
367                this.pressFrameId = requestAnimationFrame(animS)
368                break;
369            case "a":
370                let animA = () => {
371                    if (this.range.startNS == 0) return;
372                    let s = this.scale / this.p * this.currentDuration;
373                    this.range.startNS -= s;
374                    this.range.endNS -= s;
375                    this.fillX();
376                    this.draw();
377                    this.pressFrameId = requestAnimationFrame(animA)
378                }
379                this.pressFrameId = requestAnimationFrame(animA)
380                break;
381            case "d":
382                let animD = () => {
383                    if (this.range.endNS >= this.range.totalNS) return;
384                    this.range.startNS += this.scale / this.p * this.currentDuration;
385                    this.range.endNS += this.scale / this.p * this.currentDuration;
386                    this.fillX();
387                    this.draw();
388                    this.pressFrameId = requestAnimationFrame(animD)
389                }
390                this.pressFrameId = requestAnimationFrame(animD)
391                break;
392        }
393    }
394
395    keyUp(ev: KeyboardEvent) {
396        this.animaStartTime = undefined;
397        this.isPress = false
398        if (this.pressFrameId != -1) {
399            cancelAnimationFrame(this.pressFrameId)
400        }
401        let startTime = new Date().getTime();
402        switch (ev.key) {
403            case "w":
404                let animW = () => {
405                    if (this.scale === 50) return;
406                    let dur = (new Date().getTime() - startTime);
407                    this.range.startNS += (this.centerXPercentage * 100 * this.scale / this.p);
408                    this.range.endNS -= ((1 - this.centerXPercentage) * 100 * this.scale / this.p);
409                    this.fillX();
410                    this.draw();
411                    if (dur < 200) {
412                        requestAnimationFrame(animW)
413                    }
414                }
415                requestAnimationFrame(animW)
416                break;
417            case "s":
418                let animS = () => {
419                    if (this.range.startNS <= 0 && this.range.endNS >= this.range.totalNS) return;
420                    let dur = (new Date().getTime() - startTime);
421                    this.range.startNS -= (this.centerXPercentage * 100 * this.scale / this.p);
422                    this.range.endNS += ((1 - this.centerXPercentage) * 100 * this.scale / this.p);
423                    this.fillX();
424                    this.draw();
425                    if (dur < 200) {
426                        requestAnimationFrame(animS)
427                    }
428                }
429                requestAnimationFrame(animS)
430                break;
431            case "a":
432                let animA = () => {
433                    if (this.range.startNS <= 0) return
434                    let dur = (new Date().getTime() - startTime);
435                    let s = this.scale * 80 / this.p;
436                    this.range.startNS -= s;
437                    this.range.endNS -= s;
438                    this.fillX();
439                    this.draw();
440                    if (dur < 200) {
441                        requestAnimationFrame(animA)
442                    }
443                }
444                animA();
445                break;
446            case "d":
447                let animD = () => {
448                    if (this.range.endNS >= this.range.totalNS) return;
449                    let dur = (new Date().getTime() - startTime);
450                    let s = this.scale * 80 / this.p;
451                    this.range.startNS += s;
452                    this.range.endNS += s;
453                    this.fillX();
454                    this.draw();
455                    if (dur < 200) {
456                        requestAnimationFrame(animD)
457                    }
458                }
459                animD();
460                break;
461        }
462    }
463}
464