• 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';
17import { TimeRuler } from './timer-shaft/TimeRuler';
18import { Rect } from './timer-shaft/Rect';
19import { RangeRuler, TimeRange } from './timer-shaft/RangeRuler';
20import { SlicesTime, SportRuler } from './timer-shaft/SportRuler';
21import { procedurePool } from '../../database/Procedure';
22import { Flag } from './timer-shaft/Flag';
23import { info } from '../../../log/Log';
24import { TraceSheet } from './base/TraceSheet';
25import { SelectionParam } from '../../bean/BoxSelection';
26import { type SpSystemTrace, CurrentSlicesTime } from '../SpSystemTrace';
27import './timer-shaft/CollapseButton';
28import { TimerShaftElementHtml } from './TimerShaftElement.html';
29import { SpChartList } from './SpChartList';
30//随机生成十六位进制颜色
31//@ts-ignore
32export function randomRgbColor(): string {
33  let r = Math.floor(Math.random() * 255);
34  let g = Math.floor(Math.random() * 255);
35  let b = Math.floor(Math.random() * 255);
36  let color = '';
37  if (r * 0.299 + g * 0.587 + b * 0.114 < 192) {
38    let r16 = r.toString(16).length === 1 && r.toString(16) <= 'f' ? 0 + r.toString(16) : r.toString(16);
39    let g16 = g.toString(16).length === 1 && g.toString(16) <= 'f' ? 0 + g.toString(16) : g.toString(16);
40    let b16 = b.toString(16).length === 1 && b.toString(16) <= 'f' ? 0 + b.toString(16) : b.toString(16);
41    color = '#' + r16 + g16 + b16;
42  } else {
43    randomRgbColor();
44  }
45  return color;
46}
47
48export function ns2s(ns: number): string {
49  let oneSecond = 1_000_000_000; // 1 second
50  let oneMillisecond = 1_000_000; // 1 millisecond
51  let oneMicrosecond = 1_000; // 1 microsecond
52  let nanosecond1 = 1000.0;
53  let result;
54  if (ns >= oneSecond) {
55    result = (ns / 1000 / 1000 / 1000).toFixed(1) + ' s';
56  } else if (ns >= oneMillisecond) {
57    result = (ns / 1000 / 1000).toFixed(1) + ' ms';
58  } else if (ns >= oneMicrosecond) {
59    result = (ns / 1000).toFixed(1) + ' μs';
60  } else if (ns > 0) {
61    result = ns.toFixed(1) + ' ns';
62  } else {
63    result = ns.toFixed(1) + ' s';
64  }
65  return result;
66}
67
68export function ns2UnitS(ns: number, scale: number): string {
69  let oneSecond = 1_000_000_000; // 1 second
70  let result;
71  if (scale >= 10_000_000_000) {
72    result = (ns / oneSecond).toFixed(0) + ' s';
73  } else if (scale >= 1_000_000_000) {
74    result = (ns / oneSecond).toFixed(1) + ' s';
75  } else if (scale >= 100_000_000) {
76    result = (ns / oneSecond).toFixed(2) + ' s';
77  } else if (scale >= 10_000_000) {
78    result = (ns / oneSecond).toFixed(3) + ' s';
79  } else if (scale >= 1_000_000) {
80    result = (ns / oneSecond).toFixed(4) + ' s';
81  } else if (scale >= 100_000) {
82    result = (ns / oneSecond).toFixed(5) + ' s';
83  } else {
84    result = (ns / oneSecond).toFixed(6) + ' s';
85  }
86  return result;
87}
88
89export function ns2x(ns: number, startNS: number, endNS: number, duration: number, rect: Rect): number {
90  if (endNS === 0) {
91    endNS = duration;
92  }
93  let xSize: number = ((ns - startNS) * rect.width) / (endNS - startNS);
94  if (xSize < 0) {
95    xSize = 0;
96  }
97  if (xSize > rect.width) {
98    xSize = rect.width;
99  }
100  return xSize;
101}
102
103@element('timer-shaft-element')
104export class TimerShaftElement extends BaseElement {
105  // @ts-ignore
106  offscreen: OffscreenCanvas | undefined;
107  isOffScreen: boolean = false;
108  public ctx: CanvasRenderingContext2D | undefined | null;
109  public canvas: HTMLCanvasElement | null | undefined;
110  public totalEL: HTMLDivElement | null | undefined;
111  public timeTotalEL: HTMLSpanElement | null | undefined;
112  public timeOffsetEL: HTMLSpanElement | null | undefined;
113  public collectGroup: HTMLDivElement | null | undefined;
114  public collect1: HTMLInputElement | null | undefined;
115  public loadComplete: boolean = false;
116  public collecBtn: HTMLElement | null | undefined;
117  rangeChangeHandler: ((timeRange: TimeRange) => void) | undefined = undefined;
118  rangeClickHandler: ((sliceTime: SlicesTime | undefined | null) => void) | undefined = undefined;
119  flagChangeHandler: ((hoverFlag: Flag | undefined | null, selectFlag: Flag | undefined | null) => void) | undefined =
120    undefined;
121  flagClickHandler: ((flag: Flag | undefined | null) => void) | undefined = undefined;
122  /**
123   * 离线渲染需要的变量
124   */
125  dpr = window.devicePixelRatio || 1;
126  frame: Rect = new Rect(0, 0, 0, 0);
127  must: boolean = true;
128  hoverX: number = 0;
129  hoverY: number = 0;
130  canvasWidth: number = 0;
131  canvasHeight: number = 0;
132  _cpuUsage: Array<{ cpu: number; ro: number; rate: number }> = [];
133  protected timeRuler: TimeRuler | undefined;
134  protected _rangeRuler: RangeRuler | undefined;
135  protected _sportRuler: SportRuler | undefined;
136  private root: HTMLDivElement | undefined | null;
137  private _totalNS: number = 10_000_000_000;
138  private _startNS: number = 0;
139  private _endNS: number = 10_000_000_000;
140  private traceSheetEL: TraceSheet | undefined | null;
141  private sliceTime: SlicesTime | undefined | null;
142  public selectionList: Array<SelectionParam> = [];
143  public selectionMap: Map<string, SelectionParam> = new Map<string, SelectionParam>();
144  public usageEL: HTMLDivElement | null | undefined;
145  public timerShaftEL: TimerShaftElement | null | undefined;
146  public rowsPaneEL: HTMLDivElement | null | undefined;
147  public favoriteChartListEL: SpChartList | undefined | null;
148  _checkExpand: boolean = false; //是否展开
149  _usageFoldHeight: number = 56.25; //初始化时折叠的负载区高度
150  usageExpandHeight: number = 75; //给定的展开的负载区高度
151  _cpuUsageCount: Array<{ cpu: number; ro: number; rate: number }> = [];
152
153  get sportRuler(): SportRuler | undefined {
154    return this._sportRuler;
155  }
156
157  get rangeRuler(): RangeRuler | undefined {
158    return this._rangeRuler;
159  }
160
161  set cpuUsage(value: Array<{ cpu: number; ro: number; rate: number }>) {
162    info('set cpuUsage values :', value);
163    this._cpuUsage = value;
164
165    this._cpuUsageCount = value;
166    if (this._cpuUsageCount.length) {
167      this.usageEL!.innerHTML = 'CPU Usage';
168    }
169
170    if (this._rangeRuler) {
171      this._rangeRuler.cpuUsage = this._cpuUsage;
172    }
173  }
174
175  get checkExpand(): boolean {
176    return this._checkExpand;
177  }
178
179  set checkExpand(value: boolean) {
180    this._checkExpand = value;
181  }
182
183  get usageFoldHeight(): number {
184    return this._usageFoldHeight;
185  }
186  set usageFoldHeight(value: number) {
187    this._usageFoldHeight = value;
188  }
189
190  get totalNS(): number {
191    return this._totalNS;
192  }
193
194  set totalNS(value: number) {
195    info('set totalNS values :', value);
196    this._totalNS = value;
197    if (this.timeRuler) {
198      this.timeRuler.totalNS = value;
199    }
200    if (this._rangeRuler) {
201      this._rangeRuler.range.totalNS = value;
202    }
203    if (this.timeTotalEL) {
204      this.timeTotalEL.textContent = `${ns2s(value)}`;
205    }
206    requestAnimationFrame(() => this.render());
207  }
208
209  get startNS(): number {
210    return this._startNS;
211  }
212
213  set startNS(value: number) {
214    this._startNS = value;
215  }
216
217  get endNS(): number {
218    return this._endNS;
219  }
220
221  set endNS(value: number) {
222    this._endNS = value;
223  }
224
225  reset(): void {
226    this.loadComplete = false;
227    this.totalNS = 10_000_000_000;
228    this.startNS = 0;
229    this.endNS = 10_000_000_000;
230    if (this._rangeRuler) {
231      this._rangeRuler.drawMark = false;
232      this._rangeRuler.range.totalNS = this.totalNS;
233      this._rangeRuler.markAObj.frame.x = 0;
234      this._rangeRuler.markBObj.frame.x = this._rangeRuler.frame.width;
235      this._rangeRuler.cpuUsage = [];
236      this.sportRuler!.flagList.length = 0;
237      this.sportRuler!.slicesTimeList.length = 0;
238      this.selectionList.length = 0;
239      this.selectionMap.clear();
240      this._rangeRuler.rangeRect = new Rect(0, 25, this.canvas?.clientWidth || 0, 75);
241      this.sportRuler!.isRangeSelect = false;
242      this.setSlicesMark();
243    }
244    this.removeTriangle('inverted');
245    this.setRangeNS(0, this.endNS);
246    //---------------每次导入trace时触发渲染-----------------
247    if (this._rangeRuler && this._sportRuler) {
248      this.canvas!.width = this.canvas!.clientWidth || 0;
249      sessionStorage.setItem('foldHeight', String(56.25));
250      if (this._checkExpand && this._checkExpand === true) {
251        this._checkExpand = false;
252        sessionStorage.setItem('expand', String(this._checkExpand));
253      }
254      sessionStorage.setItem('expand', String(this._checkExpand));
255      this.usageEL!.innerHTML = '';
256      this.usageEL!.style.height = `${100 - 56.25}px`;
257      this.usageEL!.style.lineHeight = `${100 - 56.25}px`;
258      this.timerShaftEL!.style.height = `${146 - 56.25 + 2}px`;
259      this.canvas!.style.height = `${146 - 56.25}px`;
260      this.canvas!.height = 146 - 56.25;
261      this.rowsPaneEL!.style.maxHeight = '100%';
262      this._sportRuler.frame.y = 43.75;
263
264      this.render();
265      this._checkExpand = true;
266      this._cpuUsageCount = []; //清空判断数据
267    }
268  }
269
270  initElements(): void {
271    this.root = this.shadowRoot?.querySelector('.root');
272    this.canvas = this.shadowRoot?.querySelector('.panel');
273    this.totalEL = this.shadowRoot?.querySelector('.total');
274    this.collect1 = this.shadowRoot?.querySelector('#collect1');
275    this.timeTotalEL = this.shadowRoot?.querySelector('.time-total');
276    this.timeOffsetEL = this.shadowRoot?.querySelector('.time-offset');
277    this.collecBtn = this.shadowRoot?.querySelector('.time-collect');
278    this.collectGroup = this.shadowRoot?.querySelector('.collect_group');
279    this.collectGroup?.addEventListener('click', (e) => {
280      // @ts-ignore
281      if (e.target && e.target.tagName === 'INPUT') {
282        // @ts-ignore
283        window.publish(window.SmartEvent.UI.CollectGroupChange, e.target.value);
284      }
285    });
286    // @ts-ignore
287    procedurePool.timelineChange = (a: unknown): void => this.rangeChangeHandler?.(a);
288    // @ts-ignore
289    window.subscribe(window.SmartEvent.UI.TimeRange, (b) => this.setRangeNS(b.startNS, b.endNS));
290    // -----------------------------点击负载区展开折叠---------------------------------
291    this.usageEL = this.shadowRoot?.querySelector('.cpu-usage');
292    this.timerShaftEL = this.shadowRoot!.host.parentNode?.querySelector('.timer-shaft');
293    this.rowsPaneEL = this.shadowRoot!.host.parentNode?.querySelector('.rows-pane');
294    this.favoriteChartListEL = this.shadowRoot!.host.parentNode?.querySelector('#favorite-chart-list');
295    const height = this.canvas?.clientHeight || 0;
296    // 点击cpu usage部分,切换折叠展开
297    this.usageEL?.addEventListener('click', (e) => {
298      if (this._rangeRuler && this.sportRuler && this._cpuUsageCount.length) {
299        // 计算需要被收起来的高度:总高度75-(总高度/cpu数量)* 2
300        this._usageFoldHeight = this.usageExpandHeight - (this.usageExpandHeight / this._rangeRuler.cpuCountData!) * 2;
301        this.canvas!.width = this.canvas!.clientWidth || 0;
302        if (this._checkExpand) {
303          sessionStorage.setItem('expand', String(this._checkExpand));
304          sessionStorage.setItem('foldHeight', String(this._usageFoldHeight));
305          this.usageEL!.style.height = '100px';
306          this.usageEL!.style.lineHeight = '100px';
307          this.timerShaftEL!.style.height = `${height + 2}px`;
308          this.canvas!.style.height = `${height}px`;
309          this.canvas!.height = height;
310          this._rangeRuler.frame.height = 75;
311          this.sportRuler.frame.y = 100;
312          this.render();
313          this._checkExpand = false;
314          this.favoriteChartListEL?.refreshFavoriteCanvas(); //刷新收藏泳道画布高度
315        } else {
316          sessionStorage.setItem('expand', String(this._checkExpand));
317          sessionStorage.setItem('foldHeight', String(this._usageFoldHeight));
318          this.usageEL!.style.height = `${100 - this._usageFoldHeight}px`;
319          this.usageEL!.style.lineHeight = `${100 - this._usageFoldHeight}px`;
320          this.timerShaftEL!.style.height = `${height - this._usageFoldHeight + 2}px`;
321          this.canvas!.style.height = `${height - this._usageFoldHeight}px`;
322          this.canvas!.height = height - this._usageFoldHeight;
323          this._rangeRuler.frame.height = 75 - this._usageFoldHeight;
324          this.sportRuler.frame.y = 100 - this._usageFoldHeight;
325          this.render();
326          this._checkExpand = true;
327          this.favoriteChartListEL?.refreshFavoriteCanvas(); //刷新收藏泳道画布高度
328        }
329      }
330    });
331  }
332
333  getRangeRuler(): RangeRuler | undefined {
334    return this._rangeRuler;
335  }
336
337  connectedCallback(): RangeRuler | undefined {
338    if (this.canvas) {
339      if (this.isOffScreen) {
340        // @ts-ignore
341        this.offscreen = this.canvas.transferControlToOffscreen();
342        return;
343      } else {
344        this.ctx = this.canvas?.getContext('2d', { alpha: true });
345      }
346    }
347    if (this.timeTotalEL) {
348      this.timeTotalEL.textContent = ns2s(this._totalNS);
349    }
350    if (this.timeOffsetEL && this._rangeRuler) {
351      this.timeOffsetEL.textContent = ns2UnitS(this._startNS, this._rangeRuler.getScale());
352    }
353    const width = this.canvas?.clientWidth || 0;
354    const height = this.canvas?.clientHeight || 0;
355    this.setTimeRuler(width);
356    this.setSportRuler(width, height);
357    this.setRangeRuler(width);
358  }
359
360  private setTimeRuler(width: number): void {
361    if (!this.timeRuler) {
362      this.timeRuler = new TimeRuler(this, new Rect(0, 0, width, 20), this._totalNS);
363    }
364    this.timeRuler.frame.width = width;
365  }
366
367  private setSportRuler(width: number, height: number): void {
368    if (!this._sportRuler) {
369      this._sportRuler = new SportRuler(
370        this,
371        new Rect(0, 100, width, height - 100),
372        (hoverFlag, selectFlag) => {
373          this.flagChangeHandler?.(hoverFlag, selectFlag);
374        },
375        (flag) => {
376          this.flagClickHandler?.(flag);
377        },
378        (slicetime) => {
379          this.rangeClickHandler?.(slicetime);
380        }
381      );
382    }
383    this._sportRuler.frame.width = width;
384  }
385
386  private setRangeRuler(width: number): void {
387    if (!this._rangeRuler) {
388      this._rangeRuler = new RangeRuler(
389        this,
390        new Rect(0, 25, width, 75 - this._usageFoldHeight),
391        {
392          slicesTime: {
393            startTime: null,
394            endTime: null,
395            color: null,
396          },
397          scale: 0,
398          startX: 0,
399          endX: this.canvas?.clientWidth || 0,
400          startNS: 0,
401          endNS: this.totalNS,
402          totalNS: this.totalNS,
403          refresh: true,
404          xs: [],
405          xsTxt: [],
406        },
407        (a) => {
408          if (a.startNS >= 0 && a.endNS >= 0) {
409            if (this._sportRuler) {
410              this._sportRuler.range = a;
411            }
412            if (this.timeOffsetEL && this._rangeRuler) {
413              this.timeOffsetEL.textContent = ns2UnitS(a.startNS, this._rangeRuler.getScale());
414            }
415            if (this.loadComplete) {
416              this.rangeChangeHandler?.(a);
417            }
418          }
419        }
420      );
421    }
422    this._rangeRuler.frame.width = width;
423  }
424
425  setRangeNS(startNS: number, endNS: number): void {
426    info('set startNS values :' + startNS + 'endNS values : ' + endNS);
427    this._rangeRuler?.setRangeNS(startNS, endNS);
428  }
429
430  getRange(): TimeRange | undefined {
431    return this._rangeRuler?.getRange();
432  }
433
434  updateWidth(width: number): void {
435    this.dpr = window.devicePixelRatio || 1;
436    this.canvas!.width = width - (this.totalEL?.clientWidth || 0);
437    this.canvas!.height = this.shadowRoot!.host.clientHeight || 0;
438    let oldWidth = this.canvas!.width;
439    let oldHeight = this.canvas!.height;
440    this.canvas!.width = Math.ceil(oldWidth * this.dpr);
441    this.canvas!.height = Math.ceil(oldHeight * this.dpr);
442    this.canvas!.style.width = oldWidth + 'px';
443    this.canvas!.style.height = oldHeight + 'px';
444    this.ctx?.scale(this.dpr, this.dpr);
445    this.ctx?.translate(0, 0);
446    this._rangeRuler!.frame.width = oldWidth;
447    this._sportRuler!.frame.width = oldWidth;
448    this.timeRuler!.frame.width = oldWidth;
449    this._rangeRuler?.fillX();
450    this.render();
451  }
452
453  documentOnMouseDown = (ev: MouseEvent): void => {
454    // @ts-ignore
455    if ((window as unknown).isSheetMove) {
456      return;
457    }
458    this._rangeRuler?.mouseDown(ev);
459  };
460
461  documentOnMouseUp = (ev: MouseEvent): void => {
462    // @ts-ignore
463    if ((window as unknown).isSheetMove) {
464      return;
465    }
466    this._rangeRuler?.mouseUp(ev);
467    this.sportRuler?.mouseUp(ev);
468  };
469
470  documentOnMouseMove = (ev: MouseEvent, trace: SpSystemTrace): void => {
471    trace.style.cursor = 'default';
472    let x = ev.offsetX - (this.canvas?.offsetLeft || 0); // 鼠标的x轴坐标
473    let y = ev.offsetY; // 鼠标的y轴坐标
474    let findSlicestime = this.sportRuler?.findSlicesTime(x, y); // 查找帽子
475    if (!findSlicestime) {
476      // 如果在该位置没有找到一个“帽子”,则可以显示一个旗子。
477      this.sportRuler?.showHoverFlag();
478      this._rangeRuler?.mouseMove(ev, trace);
479      if (this.sportRuler?.edgeDetection(ev)) {
480        this.sportRuler?.mouseMove(ev);
481      } else {
482        this.sportRuler?.mouseOut(ev);
483      }
484    } else {
485      this.sportRuler?.clearHoverFlag();
486      this.sportRuler?.modifyFlagList(null); //重新绘制旗子,清除hover flag
487    }
488  };
489
490  documentOnMouseOut = (ev: MouseEvent): void => {
491    this._rangeRuler?.mouseOut(ev);
492    this.sportRuler?.mouseOut(ev);
493  };
494
495  documentOnKeyPress = (ev: KeyboardEvent, currentSlicesTime?: CurrentSlicesTime): void => {
496    // @ts-ignore
497    if ((window as unknown).flagInputFocus) {
498      return;
499    }
500    this._rangeRuler?.keyPress(ev, currentSlicesTime);
501    this.sportRuler?.clearHoverFlag();
502  };
503
504  documentOnKeyUp = (ev: KeyboardEvent): void => {
505    // @ts-ignore
506    if ((window as unknown).flagInputFocus) {
507      return;
508    }
509    this._rangeRuler?.keyUp(ev);
510  };
511
512  disconnectedCallback(): void { }
513
514  firstRender = true;
515
516  lineColor(): string {
517    return window.getComputedStyle(this.canvas!, null).getPropertyValue('color');
518  }
519
520  render(): void {
521    this.dpr = window.devicePixelRatio || 1;
522    if (this.ctx) {
523      this.ctx.fillStyle = 'transparent';
524      this.ctx?.fillRect(0, 0, this.canvas?.width || 0, this.canvas?.height || 0);
525      this.timeRuler?.draw();
526      this._rangeRuler?.draw();
527      this._sportRuler?.draw();
528    } else {
529      procedurePool.submitWithName(
530        'timeline',
531        'timeline',
532        {
533          offscreen: this.must ? this.offscreen : undefined, //是否离屏
534          dpr: this.dpr, //屏幕dpr值
535          hoverX: this.hoverX,
536          hoverY: this.hoverY,
537          canvasWidth: this.canvasWidth,
538          canvasHeight: this.canvasHeight,
539          keyPressCode: null,
540          keyUpCode: null,
541          lineColor: '#dadada',
542          startNS: this.startNS,
543          endNS: this.endNS,
544          totalNS: this.totalNS,
545          frame: this.frame,
546        },
547        this.must ? this.offscreen : undefined,
548        (res: unknown) => {
549          this.must = false;
550        }
551      );
552    }
553  }
554
555  modifyFlagList(flag: Flag | null | undefined): void {
556    this._sportRuler?.modifyFlagList(flag);
557  }
558
559  modifySlicesList(slicestime: SlicesTime | null | undefined): void {
560    this._sportRuler?.modifySicesTimeList(slicestime);
561  }
562  cancelPressFrame(): void {
563    this._rangeRuler?.cancelPressFrame();
564  }
565
566  cancelUpFrame(): void {
567    this._rangeRuler?.cancelUpFrame();
568  }
569
570  stopWASD(ev: unknown): void {
571    // @ts-ignore
572    this._rangeRuler?.keyUp(ev);
573  }
574
575  drawTriangle(time: number, type: string): unknown {
576    return this._sportRuler?.drawTriangle(time, type);
577  }
578
579  removeTriangle(type: string): void {
580    this._sportRuler?.removeTriangle(type);
581  }
582
583  setSlicesMark(
584    startTime: null | number = null,
585    endTime: null | number = null,
586    shiftKey: null | boolean = false
587  ): SlicesTime | null | undefined {
588    let sliceTime = this._sportRuler?.setSlicesMark(startTime, endTime, shiftKey);
589    if (sliceTime && sliceTime !== undefined) {
590      this.traceSheetEL?.displayCurrent(sliceTime); // 给当前pane准备数据
591
592      // 取最新创建的那个selection对象
593      let selection = this.selectionList[this.selectionList.length - 1];
594      if (selection) {
595        selection.isCurrentPane = true; // 设置当前面板为可以显示的状态
596        //把刚刚创建的slicetime和selection对象关联起来,以便后面再次选中“跑道”的时候显示对应的面板。
597        this.selectionMap.set(sliceTime.id, selection);
598        this.traceSheetEL?.rangeSelect(selection); // 显示选中区域对应的面板
599      }
600    }
601    return sliceTime;
602  }
603
604  displayCollect(showCollect: boolean): void {
605    if (showCollect) {
606      this.collecBtn!.style.display = 'flex';
607    } else {
608      this.collecBtn!.style.display = 'none';
609    }
610  }
611
612  initHtml(): string {
613    return TimerShaftElementHtml;
614  }
615}
616