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