• 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.js";
17import {TimeRuler} from "./timer-shaft/TimeRuler.js";
18import {Rect} from "./timer-shaft/Rect.js";
19import {RangeRuler, TimeRange} from "./timer-shaft/RangeRuler.js";
20import {SportRuler} from "./timer-shaft/SportRuler.js";
21import {procedurePool} from "../../database/Procedure.js";
22import {Flag} from "./timer-shaft/Flag.js";
23import {info} from "../../../log/Log.js";
24import {TraceRow} from "./base/TraceRow.js";
25
26//随机生成十六位进制颜色
27export function randomRgbColor() {
28    const letters = '0123456789ABCDEF';
29    let color = '#';
30    for (let i = 0; i < 6; i++) {
31        color += letters[Math.floor(Math.random() * 16)]
32    }
33    return color;
34}
35
36export function ns2s(ns: number): string {
37    let second1 = 1_000_000_000; // 1 second
38    let millisecond1 = 1_000_000; // 1 millisecond
39    let microsecond1 = 1_000; // 1 microsecond
40    let nanosecond1 = 1000.0;
41    let res;
42    if (ns >= second1) {
43        res = (ns / 1000 / 1000 / 1000).toFixed(1) + " s";
44    } else if (ns >= millisecond1) {
45        res = (ns / 1000 / 1000).toFixed(1) + " ms";
46    } else if (ns >= microsecond1) {
47        res = (ns / 1000).toFixed(1) + " μs";
48    } else if (ns > 0) {
49        res = ns.toFixed(1) + " ns";
50    } else {
51        res = ns.toFixed(1) + " s";
52    }
53    return res;
54}
55
56export function ns2x(ns: number, startNS: number, endNS: number, duration: number, rect: Rect) {
57    if (endNS == 0) {
58        endNS = duration;
59    }
60    let xSize: number = (ns - startNS) * rect.width / (endNS - startNS);
61    if (xSize < 0) {
62        xSize = 0;
63    }
64    if (xSize > rect.width) {
65        xSize = rect.width;
66    }
67    return xSize;
68}
69
70@element('timer-shaft-element')
71export class TimerShaftElement extends BaseElement {
72    // @ts-ignore
73    offscreen: OffscreenCanvas | undefined;
74    isOffScreen: boolean = false;
75    public ctx: CanvasRenderingContext2D | undefined | null
76    public canvas: HTMLCanvasElement | null | undefined
77    public totalEL: HTMLDivElement | null | undefined
78    public timeTotalEL: HTMLSpanElement | null | undefined
79    public timeOffsetEL: HTMLSpanElement | null | undefined
80    public loadComplete: boolean = false
81    rangeChangeHandler: ((timeRange: TimeRange) => void) | undefined = undefined
82    flagChangeHandler: ((hoverFlag: Flag | undefined | null, selectFlag: Flag | undefined | null) => void) | undefined = undefined
83    flagClickHandler: ((flag: Flag | undefined | null) => void) | undefined = undefined
84    /**
85     * 离线渲染需要的变量
86     */
87    dpr = window.devicePixelRatio || 1;
88    frame: Rect = new Rect(0, 0, 0, 0);
89    must: boolean = true
90    hoverX: number = 0
91    hoverY: number = 0
92    canvasWidth: number = 0
93    canvasHeight: number = 0
94    _cpuUsage: Array<{ cpu: number, ro: number, rate: number }> = []
95    protected timeRuler: TimeRuler | undefined;
96    protected rangeRuler: RangeRuler | undefined;
97    protected _sportRuler: SportRuler | undefined;
98    private root: HTMLDivElement | undefined | null
99    private _totalNS: number = 10_000_000_000;
100    private _startNS: number = 0;
101    private _endNS: number = 10_000_000_000;
102
103    get sportRuler(): SportRuler | undefined {
104        return this._sportRuler;
105    }
106
107    set cpuUsage(value: Array<{ cpu: number, ro: number, rate: number }>) {
108        info("set cpuUsage values :", value);
109        this._cpuUsage = value;
110        if (this.rangeRuler) {
111            this.rangeRuler.cpuUsage = this._cpuUsage;
112        }
113    }
114
115    get totalNS(): number {
116        return this._totalNS;
117    }
118
119    set totalNS(value: number) {
120        info("set totalNS values :", value);
121        this._totalNS = value;
122        if (this.timeRuler) this.timeRuler.totalNS = value;
123        if (this.rangeRuler) this.rangeRuler.range.totalNS = value;
124        if (this.timeTotalEL) this.timeTotalEL.textContent = `${ns2s(value)}`
125        requestAnimationFrame(() => this.render())
126    }
127
128    get startNS(): number {
129        return this._startNS;
130    }
131
132    set startNS(value: number) {
133        this._startNS = value;
134    }
135
136    get endNS(): number {
137        return this._endNS;
138    }
139
140    set endNS(value: number) {
141        this._endNS = value;
142    }
143
144    isScaling(): boolean {
145        return this.rangeRuler?.isPress || false;
146    }
147
148    reset(): void {
149        this.loadComplete = false;
150        if (this.rangeRuler) {
151            this.rangeRuler.markA.frame.x = 0;
152            this.rangeRuler.markB.frame.x = this.rangeRuler.frame.width
153            this.rangeRuler.cpuUsage = []
154            this.sportRuler!.flagList.length = 0
155            this.sportRuler!.isRangeSelect = false
156            this.setSlicesMark();
157        }
158        this.removeTriangle("inverted");
159        this.totalNS = 10_000_000_000;
160    }
161
162    initElements(): void {
163        this.root = this.shadowRoot?.querySelector('.root')
164        this.canvas = this.shadowRoot?.querySelector('.panel')
165        this.totalEL = this.shadowRoot?.querySelector('.total')
166        this.timeTotalEL = this.shadowRoot?.querySelector('.time-total')
167        this.timeOffsetEL = this.shadowRoot?.querySelector('.time-offset')
168        procedurePool.timelineChange = (a: any) => {
169            this.rangeChangeHandler?.(a);
170        }
171    }
172
173    connectedCallback() {
174        if (this.canvas) {
175            if (this.isOffScreen) {
176                // @ts-ignore
177                this.offscreen = this.canvas.transferControlToOffscreen();
178                return;
179            } else {
180                this.ctx = this.canvas?.getContext('2d', {alpha: true});
181            }
182        }
183        if (this.timeTotalEL) this.timeTotalEL.textContent = ns2s(this._totalNS)
184        if (this.timeOffsetEL) this.timeOffsetEL.textContent = ns2s(this._startNS)
185        const width = this.canvas?.clientWidth || 0;
186        const height = this.canvas?.clientHeight || 0;
187        if (!this.timeRuler) {
188            this.timeRuler = new TimeRuler(this, new Rect(0, 0, width, 20), this._totalNS);
189        }
190        if (!this._sportRuler) {
191            this._sportRuler = new SportRuler(this, new Rect(0, 100, width, height - 100),
192                (hoverFlag, selectFlag) => {
193                    this.flagChangeHandler?.(hoverFlag, selectFlag);
194                }, (flag) => {
195                    this.flagClickHandler?.(flag);
196                });
197        }
198        if (!this.rangeRuler) {
199            this.rangeRuler = new RangeRuler(this, new Rect(0, 25, width, 75), {
200                slicesTime: {
201                    startTime: null,
202                    endTime: null,
203                    color: null,
204                },
205                scale: 0,
206                startX: 0,
207                endX: this.canvas?.clientWidth || 0,
208                startNS: 0,
209                endNS: this.totalNS,
210                totalNS: this.totalNS,
211                refresh: true,
212                xs: [],
213                xsTxt: []
214            }, (a) => {
215                if (this._sportRuler) {
216                    this._sportRuler.range = a;
217                }
218                if (this.timeOffsetEL) {
219                    this.timeOffsetEL.textContent = ns2s(a.startNS)
220                }
221                if (this.loadComplete) {
222                    this.rangeChangeHandler?.(a)
223                }
224            });
225        }
226        this.rangeRuler.frame.width = width;
227        this._sportRuler.frame.width = width;
228        this.timeRuler.frame.width = width;
229    }
230
231    setRangeNS(startNS: number, endNS: number) {
232        info("set startNS values :" + startNS + "endNS values : " + endNS);
233        this.rangeRuler?.setRangeNS(startNS, endNS);
234    }
235
236    getRange(): TimeRange | undefined {
237        return this.rangeRuler?.getRange();
238    }
239
240    updateWidth(width: number) {
241        this.dpr = window.devicePixelRatio || 1;
242        this.canvas!.width = width - (this.totalEL?.clientWidth || 0);
243        this.canvas!.height = this.shadowRoot!.host.clientHeight || 0;
244        let oldWidth = this.canvas!.width;
245        let oldHeight = this.canvas!.height;
246        this.canvas!.width = Math.ceil((oldWidth) * this.dpr);
247        this.canvas!.height = Math.ceil(oldHeight * this.dpr);
248        this.canvas!.style.width = oldWidth + 'px';
249        this.canvas!.style.height = oldHeight + 'px';
250        this.ctx?.scale(this.dpr, this.dpr);
251        this.ctx?.translate(0, 0)
252        this.rangeRuler!.frame.width = oldWidth;
253        this._sportRuler!.frame.width = oldWidth;
254        this.timeRuler!.frame.width = oldWidth;
255        this.rangeRuler?.fillX()
256        this.render()
257    }
258
259    documentOnMouseDown = (ev: MouseEvent) => {
260        if ((window as any).isSheetMove) return;
261        this.rangeRuler?.mouseDown(ev);
262    }
263
264    documentOnMouseUp = (ev: MouseEvent) => {
265        if ((window as any).isSheetMove) return;
266        this.rangeRuler?.mouseUp(ev);
267        this.sportRuler?.mouseUp(ev);
268    }
269
270    documentOnMouseMove = (ev: MouseEvent) => {
271        this.rangeRuler?.mouseMove(ev);
272        this.sportRuler?.mouseMove(ev);
273    }
274
275    documentOnMouseOut = (ev: MouseEvent) => {
276        this.rangeRuler?.mouseOut(ev);
277    }
278
279    documentOnKeyPress = (ev: KeyboardEvent) => {
280        if ((window as any).isSheetMove) return;
281        if ((window as any).flagInputFocus) return;
282        this.rangeRuler?.keyPress(ev);
283    }
284
285    documentOnKeyUp = (ev: KeyboardEvent) => {
286        if ((window as any).isSheetMove) return;
287        if ((window as any).flagInputFocus) return;
288        this.rangeRuler?.keyUp(ev);
289    }
290
291    disconnectedCallback() {
292    }
293
294    firstRender = true;
295
296    render() {
297        this.dpr = window.devicePixelRatio||1;
298        if (this.ctx) {
299            this.ctx.fillStyle = 'transparent';
300            this.ctx?.fillRect(0, 0, this.canvas?.width || 0, this.canvas?.height || 0)
301            this.timeRuler?.draw()
302            this.rangeRuler?.draw()
303            this._sportRuler?.draw()
304        } else {
305            procedurePool.submitWithName(`timeline`, `timeline`, {
306                offscreen: this.must ? this.offscreen : undefined,//是否离屏
307                dpr: this.dpr,//屏幕dpr值
308                hoverX: this.hoverX,
309                hoverY: this.hoverY,
310                canvasWidth: this.canvasWidth,
311                canvasHeight: this.canvasHeight,
312                keyPressCode: null,
313                keyUpCode: null,
314                lineColor: "#dadada",
315                startNS: this.startNS,
316                endNS: this.endNS,
317                totalNS: this.totalNS,
318                frame: this.frame,
319            }, this.must ? this.offscreen : undefined, (res: any) => {
320                this.must = false;
321            })
322        }
323    }
324
325    modifyFlagList(flag: Flag | null | undefined) {
326        this._sportRuler?.modifyFlagList(flag);
327    }
328
329    cancelPressFrame() {
330        this.rangeRuler?.cancelPressFrame();
331    }
332
333    cancelUpFrame() {
334        this.rangeRuler?.cancelUpFrame();
335    }
336
337
338    drawTriangle(time: number, type: string) {
339        return this._sportRuler?.drawTriangle(time, type);
340    }
341
342    removeTriangle(type: string) {
343        this._sportRuler?.removeTriangle(type)
344    }
345
346    setSlicesMark(startTime: null | number = null, endTime: null | number = null) {
347        this._sportRuler?.setSlicesMark(startTime, endTime)
348    }
349
350    initHtml(): string {
351        return `
352        <style>
353        :host{
354            box-sizing: border-box;
355            display: flex;
356            width: 100%;
357            height: 147px;
358            border-bottom: 1px solid var(--dark-background,#dadada);
359            border-top: 1px solid var(--dark-background,#dadada);
360        }
361        *{
362            box-sizing: border-box;
363            user-select: none;
364        }
365        .root{
366            width: 100%;
367            height: 100%;
368            display: grid;
369            grid-template-rows: 100%;
370            grid-template-columns: 248px 1fr;
371            background: var(--dark-background4,#FFFFFF);
372        }
373        .total{
374            display: grid;
375            grid-template-columns: 1fr;
376            grid-template-rows: min-content 1fr;
377            background-color: transparent;
378        }
379        .panel{
380            color: var(--dark-border,#dadada);
381            width: 100%;
382            height: 100%;
383            overflow: visible;
384            background-color: var(--dark-background4,#ffffff);
385        }
386        .time-div{
387            box-sizing: border-box;
388            width: 100%;border-top: 1px solid var(--dark-background,#dadada);height: 100%;display: flex;justify-content: space-between;background-color: var(--dark-background1,white);color: var(--dark-color1,#212121);font-size: 0.7rem;
389            border-right: 1px solid var(--dark-background,#999);
390            padding: 2px 6px;
391            display: flex;justify-content: space-between;
392            user-select: none;
393        }
394        .time-total::after{
395            content: " +";
396        }
397
398        </style>
399        <div class="root">
400            <div class="total">
401                <div style="width: 100%;height: 100px;background: var(--dark-background4,#F6F6F6)"></div>
402                <div class="time-div">
403                    <span class="time-total">10</span>
404                    <span class="time-offset">0</span>
405                </div>
406            </div>
407            <canvas class="panel"></canvas>
408        </div>
409        `;
410    }
411}
412