• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2023 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 { LitButton } from '../../../../../base-ui/button/LitButton';
18import { LitTable, RedrawTreeForm } from '../../../../../base-ui/table/lit-table';
19import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection';
20import {
21  querySchedThreadStates,
22  querySingleCutData,
23  queryLoopCutData,
24} from '../../../../database/sql/ProcessThread.sql';
25import { Utils } from '../../base/Utils';
26import { resizeObserver } from '../SheetUtils';
27import { LitChartColumn } from '../../../../../base-ui/chart/column/LitChartColumn';
28import { SpSegmentationChart } from '../../../../../trace/component/chart/SpSegmentationChart';
29import {
30  type TreeSwitchConfig,
31  HistogramSourceConfig,
32  ThreadInitConfig,
33  CutDataObjConfig,
34  SchedSwitchCountBean,
35} from '../../../../bean/SchedSwitchStruct';
36const UNIT: number = 1000000.0;
37const NUM_DIGITS: number = 3;
38const SINGLE_BUTTON_TEXT: string = 'Single';
39const LOOP_BUTTON_TEXT: string = 'Loop';
40@element('tabpane-schedswitch')
41export class TabPaneSchedSwitch extends BaseElement {
42  private schedSwitchTbl: LitTable | null | undefined;
43  private threadQueryDIV: Element | null | undefined;
44  private rightDIV: HTMLDivElement | null | undefined;
45  private threadIdInput: HTMLInputElement | null | undefined;
46  private funcNameInput: HTMLInputElement | null | undefined;
47  private singleBtn: LitButton | null | undefined;
48  private loopBtn: LitButton | null | undefined;
49  private cycleALeftInput: HTMLInputElement | null | undefined;
50  private cycleARightInput: HTMLInputElement | null | undefined;
51  private cycleBLeftInput: HTMLInputElement | null | undefined;
52  private cycleBRightInput: HTMLInputElement | null | undefined;
53  private queryButton: LitButton | null | undefined;
54  private selectionParam: SelectionParam | undefined;
55  private canvansName: HTMLDivElement | null | undefined;
56  private threadMap: Map<string, Array<ThreadInitConfig>> = new Map<string, Array<ThreadInitConfig>>();
57  private chartTotal: LitChartColumn | null | undefined;
58  private histogramSource: Array<HistogramSourceConfig> = [];
59  private rangeA: HistogramSourceConfig = new HistogramSourceConfig();
60  private rangeB: HistogramSourceConfig = new HistogramSourceConfig();
61  private rangeTotal: HistogramSourceConfig = new HistogramSourceConfig();
62  private getThreadChildren: Array<TreeSwitchConfig> = [];
63  private hasThreadStatesData: boolean = false;
64  private clickThreadName: string = '';
65
66  set data(threadStatesParam: SelectionParam) {
67    if (this.selectionParam === threadStatesParam) {
68      return;
69    }
70    let tabpaneSwitch = this.parentElement as HTMLElement;
71    tabpaneSwitch.style.overflow = 'hidden';
72    this.schedSwitchTbl!.recycleDataSource = [];
73    this.queryButton!.style.pointerEvents = 'none';
74    this.schedSwitchTbl!.loading = false;
75    this.hasThreadStatesData = false;
76    // @ts-ignore
77    this.schedSwitchTbl!.value = [];
78    this.funcNameInput!.style.border = '1px solid rgb(151,151,151)';
79    this.threadIdInput!.style.border = '1px solid rgb(151,151,151)';
80    SpSegmentationChart.setChartData('SCHED-SWITCH', []);
81    SpSegmentationChart.tabHover('SCHED-SWITCH', false, -1);
82    this.canvansName!.textContent = 'sched switch平均分布图';
83    this.selectionParam = threadStatesParam;
84    this.canQueryButtonClick(false);
85    this.isSingleBtnColor(false);
86    this.isLoopBtnColor(false);
87    this.isCanvansHidden(true);
88    //当Tab页发生变化时,ResizeObserver会被通知并且检测到Tab页布局和大小的变化,可以按需求调整页面UI
89    new ResizeObserver((entries) => {
90      // @ts-ignore
91      let lastHeight = this.schedSwitchTbl!.tableElement!.offsetHeight;
92      //控制右侧区域高度实时变化与左侧区域Div保持一致,避免滚动滚动条时出现空白的情况
93      this.rightDIV!.style.height = String(lastHeight) + 'px';
94    }).observe(this.schedSwitchTbl!);
95  }
96  initElements(): void {
97    this.schedSwitchTbl = this.shadowRoot!.querySelector<LitTable>('#tb-running');
98    this.threadQueryDIV = this.shadowRoot?.querySelector('#data-cut');
99    this.rightDIV = this.shadowRoot?.querySelector('#right');
100    this.canvansName = this.shadowRoot!.querySelector<HTMLDivElement>('.sched-subheading');
101    this.chartTotal = this.shadowRoot!.querySelector<LitChartColumn>('#chart_total');
102    this.threadIdInput = this.shadowRoot?.getElementById('cut-threadid') as HTMLInputElement;
103    this.funcNameInput = this.shadowRoot?.querySelector('#cut-thread-func') as HTMLInputElement;
104    this.singleBtn = this.shadowRoot?.querySelector<LitButton>('.single-btn');
105    this.loopBtn = this.shadowRoot?.querySelector<LitButton>('.loop-btn');
106    this.cycleALeftInput = this.shadowRoot?.getElementById('leftA') as HTMLInputElement;
107    this.cycleARightInput = this.shadowRoot?.getElementById('rightA') as HTMLInputElement;
108    this.cycleBLeftInput = this.shadowRoot?.getElementById('leftB') as HTMLInputElement;
109    this.cycleBRightInput = this.shadowRoot?.getElementById('rightB') as HTMLInputElement;
110    this.queryButton = this.shadowRoot?.querySelector<LitButton>('.query-btn');
111    this.singleBtn?.addEventListener('click', (e) => {
112      this.queryCutInfoFn(this.singleBtn!.innerHTML);
113    });
114    this.loopBtn?.addEventListener('click', (e) => {
115      this.queryCutInfoFn(this.loopBtn!.innerHTML);
116    });
117    this.queryButton!.addEventListener('click', (e) => {
118      this.queryCycleRangeData();
119    });
120    this.threadIdInput!.addEventListener('keyup', (ev) => {
121      if (ev.key.toLocaleLowerCase() === String.fromCharCode(47)) {
122        ev.stopPropagation();
123      }
124    });
125    this.funcNameInput!.addEventListener('keyup', (ev) => {
126      if (ev.key.toLocaleLowerCase() === String.fromCharCode(47)) {
127        ev.stopPropagation();
128      }
129    });
130    this.schedSwitchTbl!.addEventListener('row-click', (evt) => {
131      this.clickTreeRowEvent(evt);
132    });
133    this.listenInputEvent();
134  }
135  //监听周期A、B对应输入框的值
136  listenInputEvent(): void {
137    this.cycleALeftInput!.addEventListener('input', (evt) => {
138      this.checkInputRangeFn(
139        this.cycleALeftInput,
140        this.cycleARightInput,
141        this.cycleBLeftInput,
142        this.cycleBRightInput,
143        this.cycleALeftInput!.value,
144        this.cycleARightInput!.value
145      );
146    });
147    this.cycleARightInput!.addEventListener('input', (evt) => {
148      this.checkInputRangeFn(
149        this.cycleARightInput,
150        this.cycleALeftInput,
151        this.cycleBLeftInput,
152        this.cycleBRightInput,
153        this.cycleALeftInput!.value,
154        this.cycleARightInput!.value
155      );
156    });
157    this.cycleBLeftInput!.addEventListener('input', (evt) => {
158      this.checkInputRangeFn(
159        this.cycleBLeftInput,
160        this.cycleBRightInput,
161        this.cycleALeftInput,
162        this.cycleARightInput,
163        this.cycleBLeftInput!.value,
164        this.cycleBRightInput!.value
165      );
166    });
167    this.cycleBRightInput!.addEventListener('input', (evt) => {
168      this.checkInputRangeFn(
169        this.cycleBRightInput,
170        this.cycleBLeftInput,
171        this.cycleALeftInput,
172        this.cycleARightInput,
173        this.cycleBLeftInput!.value,
174        this.cycleBRightInput!.value
175      );
176    });
177  }
178  //校验周期A、B对应输入框的值是否符合要求,不符合时给出相应提示
179  checkInputRangeFn(
180    firstInput: HTMLInputElement | null | undefined,
181    secondInput: HTMLInputElement | null | undefined,
182    thirdInput: HTMLInputElement | null | undefined,
183    fourInput: HTMLInputElement | null | undefined,
184    lVal: string,
185    rVal: string
186  ): void {
187    let leftVal: number = Number(lVal);
188    let rightVal: number = Number(rVal);
189    if (firstInput!.value !== '' && secondInput!.value !== '') {
190      if (firstInput!.value !== '' && secondInput!.value !== '') {
191        if (leftVal >= rightVal) {
192          firstInput!.style.color = 'red';
193          this.queryButton!.style.pointerEvents = 'none';
194          this.canQueryButtonClick(false);
195        } else if (leftVal < rightVal) {
196          firstInput!.style.color = 'black';
197          secondInput!.style.color = 'black';
198          this.queryButton!.style.pointerEvents = 'auto';
199          this.canQueryButtonClick(true);
200        }
201      }
202    } else if (
203      (firstInput!.value === '' && secondInput!.value !== '') ||
204      (firstInput!.value !== '' && secondInput!.value === '')
205    ) {
206      this.queryButton!.style.pointerEvents = 'none';
207      this.canQueryButtonClick(false);
208    } else if (
209      firstInput!.value === '' &&
210      secondInput!.value === '' &&
211      thirdInput!.value !== '' &&
212      fourInput!.value !== ''
213    ) {
214      this.queryButton!.style.pointerEvents = 'auto';
215      this.canQueryButtonClick(true);
216    }
217    if (
218      (thirdInput!.value === '' && fourInput!.value !== '') ||
219      (thirdInput!.value !== '' && fourInput!.value === '')
220    ) {
221      this.queryButton!.style.pointerEvents = 'none';
222      this.canQueryButtonClick(false);
223    }
224  }
225  //点击树节点不同层级触发相应的功能
226  clickTreeRowEvent(evt: Event): void {
227    //@ts-ignore
228    let data = evt.detail.data;
229    if (data.level === 'process') {
230      //点击树节点时节点高亮
231      data.isSelected = true;
232      this.schedSwitchTbl!.clearAllSelection(data);
233      this.schedSwitchTbl!.setCurrentSelection(data);
234      //点击进程canvans相关内容隐藏
235      this.isCanvansHidden(true);
236      SpSegmentationChart.setChartData('SCHED-SWITCH', []);
237      SpSegmentationChart.tabHover('SCHED-SWITCH', false, -1);
238      this.clickThreadName = data.nodeFlag;
239    } else if (data.level === 'thread') {
240      //点击线程绘制canvans,相同线程canvans不会重新绘制,切换线程时清空周期输入框等相关内容
241      if (this.clickThreadName !== data.nodeFlag) {
242        this.cycleALeftInput!.value = '';
243        this.cycleARightInput!.value = '';
244        this.cycleBLeftInput!.value = '';
245        this.cycleBRightInput!.value = '';
246        this.histogramSource = [];
247        SpSegmentationChart.tabHover('SCHED-SWITCH', false, -1); //清空上一次的泳道高亮
248        this.getThreadChildren = data.children;
249        this.queryButton!.style.pointerEvents = 'none';
250        this.isCanvansHidden(false);
251        this.canQueryButtonClick(false);
252        this.clickThreadName = data.nodeFlag;
253        //绘制canvans柱状图需要的数据
254        this.rangeTotal = {
255          value: data.value,
256          cycleNum: data.children.length,
257          average: data.children.length ? Math.ceil(data.value / data.children.length) : 0,
258          size: 'Total',
259          isHover: false,
260          color: '#2f72f8',
261        };
262        this.histogramSource.push(this.rangeTotal);
263        //清空高亮的树节点
264        this.schedSwitchTbl!.clearAllSelection(data);
265        this.drawHistogramChart();
266      }
267      //点击树节点时高亮
268      data.isSelected = true;
269      this.schedSwitchTbl!.setCurrentSelection(data);
270      //点击线程绘制对应泳道图
271      SpSegmentationChart.setChartData('SCHED-SWITCH', data.children);
272    } else if (data.level === 'cycle') {
273      if (this.clickThreadName === data.nodeFlag) {
274        data.isSelected = true;
275        this.schedSwitchTbl!.clearAllSelection(data);
276        this.schedSwitchTbl!.setCurrentSelection(data);
277        SpSegmentationChart.tabHover('SCHED-SWITCH', true, data!.cycle);
278      }
279    }
280  }
281  //点击single或loop按钮时触发
282  async queryCutInfoFn(btnHtml: string): Promise<void> {
283    let funcData: Array<ThreadInitConfig> = [];
284    let threadId: string = this.threadIdInput!.value.trim();
285    let threadFunName: string = this.funcNameInput!.value.trim();
286    let leftStartNs: number = this.selectionParam!.leftNs + this.selectionParam!.recordStartNs;
287    let rightEndNs: number = this.selectionParam!.rightNs + this.selectionParam!.recordStartNs;
288    if (threadId !== '' && threadFunName !== '') {
289      this.threadIdInput!.style.border = '1px solid rgb(151,151,151)';
290      this.funcNameInput!.style.border = '1px solid rgb(151,151,151)';
291      //@ts-ignore
292      this.schedSwitchTbl!.value = [];
293      this.isCanvansHidden(true);
294      this.schedSwitchTbl!.loading = true;
295      this.clickThreadName = '';
296      SpSegmentationChart.setChartData('SCHED-SWITCH', []);
297      SpSegmentationChart.tabHover('SCHED-SWITCH', false, -1);
298      //首次点击single或loop时去查询数据
299      if (!this.hasThreadStatesData) {
300        this.initThreadStateData(this.selectionParam);
301      }
302      if (btnHtml === SINGLE_BUTTON_TEXT) {
303        this.isSingleBtnColor(true);
304        this.isLoopBtnColor(false); //@ts-ignore
305        funcData = await querySingleCutData(threadFunName, threadId, leftStartNs, rightEndNs);
306      }
307      if (btnHtml === LOOP_BUTTON_TEXT) {
308        this.isSingleBtnColor(false);
309        this.isLoopBtnColor(true); //@ts-ignore
310        funcData = await queryLoopCutData(threadFunName, threadId, leftStartNs, rightEndNs);
311      }
312      //获取到线程数据和方法数据,处理周期
313      this.handleCycleLogic(funcData, btnHtml);
314    } else {
315      if (threadId === '') {
316        this.threadIdInput!.style.border = '2px solid rgb(255,0,0)';
317        this.threadIdInput!.setAttribute('placeholder', 'Please input thread id');
318      } else {
319        this.threadIdInput!.style.border = '1px solid rgb(151,151,151)';
320      }
321      if (threadFunName === '') {
322        this.funcNameInput!.style.border = '2px solid rgb(255,0,0)';
323        this.funcNameInput!.setAttribute('placeholder', 'Please input function name');
324      } else {
325        this.funcNameInput!.style.border = '1px solid rgb(151,151,151)';
326      }
327    }
328  }
329
330  //获取每次被框选线程对应的state数据
331  async initThreadStateData(threadParam: SelectionParam | null | undefined): Promise<void> {
332    let threadSourceData: Array<ThreadInitConfig> = [];
333    let leftStartNs: number = threadParam!.leftNs + threadParam!.recordStartNs;
334    let rightEndNs: number = threadParam!.rightNs + threadParam!.recordStartNs;
335    let processIds: Array<number> = [...new Set(threadParam!.processIds)]; //@ts-ignore
336    let res: Array<ThreadInitConfig> = await querySchedThreadStates(
337      processIds,
338      threadParam!.threadIds,
339      leftStartNs,
340      rightEndNs
341    );
342    threadSourceData = JSON.parse(JSON.stringify(res));
343    //每次新款选线程时清空Map对象
344    this.threadMap.clear();
345    //state数据转换成以pid+tid为key值的Map对象
346    for (let i = 0; i < threadSourceData.length; i++) {
347      let stateItem = threadSourceData[i];
348      if (this.threadMap.has(`${stateItem.pid} - ${stateItem.tid}`)) {
349        let obj = this.threadMap.get(`${stateItem.pid} - ${stateItem.tid}`);
350        obj!.push(stateItem);
351      } else {
352        this.threadMap.set(`${stateItem.pid} - ${stateItem.tid}`, [stateItem]);
353      }
354    }
355    this.hasThreadStatesData = true;
356  }
357
358  //线程下周期的处理逻辑
359  handleCycleLogic(res: Array<ThreadInitConfig>, btnHtml: string): void {
360    //处理sql查询数据为0条,或者当loop切割获取的数据时1条
361    if (res.length === 0 || this.threadMap.size === 0 || (btnHtml === LOOP_BUTTON_TEXT && res.length === 1)) {
362      this.schedSwitchTbl!.recycleDataSource = [];
363      this.schedSwitchTbl!.loading = false; // @ts-ignore
364      this.clickTableLabel(this.schedSwitchTbl!.recycleDataSource);
365      return;
366    }
367    let group: { [key: number]: SchedSwitchCountBean } = {};
368    for (let value of this.threadMap.values()) {
369      let cyclesArr: Array<SchedSwitchCountBean> = [];
370      let processInfo: string | undefined = Utils.getInstance().getProcessMap().get(value[0].pid);
371      let process: string | undefined = processInfo === null || processInfo!.length === 0 ? '[NULL]' : processInfo;
372      let threadInfo: string | undefined = Utils.getInstance().getThreadMap().get(value[0].tid);
373      let thread: string | undefined = threadInfo === null || threadInfo!.length === 0 ? '[NULL]' : threadInfo;
374      //整理出一个对象并将周期空数组放进对象里
375      let cutDataObj: CutDataObjConfig = {
376        cyclesArr: cyclesArr,
377        pid: value[0].pid,
378        tid: value[0].tid,
379        process: process,
380        thread: thread,
381        processTitle: `${process}[${value[0].pid}]`,
382        threadTitle: `${thread}[${[value[0].tid]}]`,
383        threadCountTotal: 0,
384        threadDurTotal: 0,
385      };
386      //此处根据切割方法不同处理一下方法循环长度
387      for (let idx = 0; idx < (btnHtml === LOOP_BUTTON_TEXT ? res.length - 1 : res.length); idx++) {
388        if (btnHtml === LOOP_BUTTON_TEXT) {
389          res[idx].cycleEndTime = res[idx + 1].cycleStartTime;
390        } //当切割方法为loop时,处理出周期结束时间
391        let duration = ((res[idx].cycleEndTime - res[idx].cycleStartTime) / UNIT).toFixed(NUM_DIGITS);
392        let dur = Number(duration) * UNIT;
393        value.map((item: ThreadInitConfig) => {
394          //当idx变化时添加周期
395          if (cyclesArr.length !== idx + 1) {
396            let nodeFlag = `${process} - ${item.pid} - ${thread} - ${item.tid}`;
397            let startNS = res[idx].cycleStartTime - this.selectionParam!.recordStartNs;
398            let cycleStartTime = (startNS / UNIT).toFixed(NUM_DIGITS);
399            let title = `cycle ${idx + 1}-` + thread; //周期名称
400            cyclesArr.push(
401              new SchedSwitchCountBean(
402                nodeFlag,
403                startNS,
404                cycleStartTime,
405                dur,
406                duration,
407                idx + 1,
408                title,
409                0,
410                'cycle',
411                9,
412                []
413              )
414            );
415            cutDataObj!.threadDurTotal = (Number(duration) + Number(cutDataObj.threadDurTotal)).toFixed(NUM_DIGITS); //本次线程下所有周期的dur和
416          }
417          //判断数据是否符合这个周期,符合的进入判断累加count
418          if (res[idx].cycleEndTime > item.endTs && item.endTs > res[idx].cycleStartTime) {
419            let index = cyclesArr.length - 1;
420            if (index === idx) {
421              cyclesArr[index].value += 1;
422            }
423            cutDataObj.threadCountTotal += 1;
424          }
425        });
426      }
427      //本轮线程处理过的数据传入并处理成树结构,放入group对象中
428      this.translateIntoTree(cutDataObj, group);
429    }
430    this.schedSwitchTbl!.recycleDataSource = Object.values(group);
431    this.schedSwitchTbl!.loading = false; // @ts-ignore
432    this.clickTableLabel(this.schedSwitchTbl!.recycleDataSource);
433  }
434  //根据处理好的单个线程对应的周期数据、count总数、dur总数以及所属进程、线程相关信息,转换成树结构数据
435  translateIntoTree(data: CutDataObjConfig, group: { [key: number]: SchedSwitchCountBean }): void {
436    //给进程节点、线程节点添加节点标志
437    let nodeFlag = `${data.process} - ${data.pid} - ${data.thread} - ${data.tid}`;
438    //将线程放到对应的进程节点下
439    let dur = Number(data.threadDurTotal) * UNIT;
440    if (group[data.pid]) {
441      let process = group[data.pid];
442      process.value += data.threadCountTotal;
443      process.duration = (Number(data.threadDurTotal) + Number(process.duration)).toFixed(NUM_DIGITS);
444      process.children.push(
445        new SchedSwitchCountBean(
446          nodeFlag,
447          -1,
448          '',
449          dur,
450          data.threadDurTotal,
451          -1,
452          data.threadTitle,
453          data.threadCountTotal,
454          'thread',
455          9,
456          data.cyclesArr
457        )
458      );
459    } else {
460      group[data.pid] = new SchedSwitchCountBean(
461        '',
462        -1,
463        '',
464        dur,
465        data.threadDurTotal,
466        -1,
467        data.processTitle,
468        data.threadCountTotal,
469        'process',
470        9,
471        [
472          new SchedSwitchCountBean(
473            nodeFlag,
474            -1,
475            '',
476            dur,
477            data.threadDurTotal,
478            -1,
479            data.threadTitle,
480            data.threadCountTotal,
481            'thread',
482            9,
483            data.cyclesArr
484          ),
485        ]
486      );
487    }
488  }
489  //点击表格标签,判断点击的是那个标签,进而展开或收缩对应的节点
490  clickTableLabel(data: Array<TreeSwitchConfig>): void {
491    let labelList = this.schedSwitchTbl!.shadowRoot?.querySelector('.th > .td')!.querySelectorAll('label');
492    if (labelList) {
493      for (let i = 0; i < labelList.length; i++) {
494        let lable = labelList[i].innerHTML;
495        labelList[i].addEventListener('click', (e) => {
496          if (lable.includes('Process')) {
497            this.schedSwitchTbl!.setStatus(data, false);
498            this.schedSwitchTbl!.recycleDs = this.schedSwitchTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract);
499          } else if (lable.includes('Thread')) {
500            for (let item of data) {
501              item.status = true;
502              if (item.children !== undefined && item.children.length > 0) {
503                this.schedSwitchTbl!.setStatus(item.children, false);
504              }
505            }
506            this.schedSwitchTbl!.recycleDs = this.schedSwitchTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract);
507          } else if (lable.includes('Cycle')) {
508            this.schedSwitchTbl!.setStatus(data, true);
509            this.schedSwitchTbl!.recycleDs = this.schedSwitchTbl!.meauseTreeRowElement(data, RedrawTreeForm.Expand);
510          }
511        });
512      }
513    }
514  }
515  //根据A、B周期输入的dur的范围值,计算对应周期的数据
516  queryCycleRangeData(): void {
517    let cycleALeft: string = this.cycleALeftInput!.value.trim();
518    let cycleARight: string = this.cycleARightInput!.value.trim();
519    let cycleBLeft: string = this.cycleBLeftInput!.value.trim();
520    let cycleBRight: string = this.cycleBRightInput!.value.trim();
521    this.histogramSource = [];
522    this.histogramSource.push(this.rangeTotal);
523    //周期A处理
524    if (cycleALeft !== '' && cycleARight !== '' && cycleALeft !== cycleARight) {
525      let countA: number = 0;
526      let rangeFilterA: Array<TreeSwitchConfig> = this.getThreadChildren.filter(
527        (it: TreeSwitchConfig) => Number(it.duration) >= Number(cycleALeft) && Number(it.duration) < Number(cycleARight)
528      );
529      rangeFilterA.forEach((item: TreeSwitchConfig) => {
530        countA += item.value;
531      });
532      this.rangeA = {
533        value: countA,
534        cycleNum: rangeFilterA.length,
535        average: rangeFilterA.length ? Math.ceil(countA / rangeFilterA.length) : 0,
536        size: 'Cycle A',
537        isHover: false,
538        color: '#ffab67',
539      };
540      this.histogramSource.push(this.rangeA);
541    }
542    //周期B处理
543    if (cycleBLeft !== '' && cycleBRight !== '' && cycleBLeft !== cycleBRight) {
544      let countB: number = 0;
545      let rangeFilterB: Array<TreeSwitchConfig> = this.getThreadChildren.filter(
546        (it: TreeSwitchConfig) => Number(it.duration) >= Number(cycleBLeft) && Number(it.duration) < Number(cycleBRight)
547      );
548      rangeFilterB.forEach((item: TreeSwitchConfig) => {
549        countB += item.value;
550      });
551      this.rangeB = {
552        value: countB,
553        cycleNum: rangeFilterB.length,
554        average: rangeFilterB.length ? Math.ceil(countB / rangeFilterB.length) : 0,
555        size: 'Cycle B',
556        isHover: false,
557        color: '#a285d2',
558      };
559      this.histogramSource.push(this.rangeB);
560    }
561    this.drawHistogramChart();
562  }
563  //绘制柱状图
564  drawHistogramChart(): void {
565    let source = [];
566    source = this.histogramSource.map((it: HistogramSourceConfig, index: number) => {
567      let data = {
568        cycle: it.size,
569        average: it.average,
570        visible: 1,
571        color: it.color,
572      };
573      return data;
574    });
575    this.chartTotal!.config = {
576      data: source, //画柱状图的数据源
577      appendPadding: 10,
578      xField: 'cycle', //x轴代表属于那个周期
579      yField: 'average', //y轴代表count/周期个数的值
580      notSort: true, //绘制的柱状图不排序
581      removeUnit: true, //移除单位换算
582      seriesField: '',
583      //设置柱状图的颜色
584      color(a): string {
585        //@ts-ignore
586        if (a.cycle === 'Total') {
587          return '#2f72f8'; //@ts-ignore
588        } else if (a.cycle === 'Cycle A') {
589          return '#ffab67'; //@ts-ignore
590        } else if (a.cycle === 'Cycle B') {
591          return '#a285d2';
592        } else {
593          return '#0a59f7';
594        }
595      },
596      //鼠标悬浮柱状图上方时显示对应的提示信息
597      tip(a): string {
598        //@ts-ignore
599        if (a && a[0]) {
600          let tip = ''; //@ts-ignore
601          for (let obj of a) {
602            tip = `${tip}
603              <div>Average count:${obj.obj.average}</div>
604            `;
605          }
606          return tip;
607        } else {
608          return '';
609        }
610      },
611      label: null,
612    };
613  }
614  //canvans涉及的区域是否被隐藏
615  isCanvansHidden(flag: boolean): void {
616    if (flag) {
617      this.setAttribute('canvans-hidden', '');
618    } else {
619      this.removeAttribute('canvans-hidden');
620    }
621  }
622  //切换single按钮时颜色是否变化
623  isSingleBtnColor(flag: boolean): void {
624    if (flag) {
625      this.setAttribute('single', '');
626    } else {
627      this.removeAttribute('single');
628    }
629  }
630  //切换single按钮时颜色是否变化
631  isLoopBtnColor(flag: boolean): void {
632    if (flag) {
633      this.setAttribute('loop', '');
634    } else {
635      this.removeAttribute('loop');
636    }
637  }
638  //Query按钮能不能被点击,即在此处设置符合条件时鼠标箭头为手的样式表示可点击,反之表示禁止触发点击事件
639  canQueryButtonClick(flag: boolean): void {
640    if (flag) {
641      this.setAttribute('query-button', '');
642    } else {
643      this.removeAttribute('query-button');
644    }
645  }
646  //回调函数,this.schedSwitchTbl首次插入DOM时执行的初始化回调
647  connectedCallback(): void {
648    super.connectedCallback();
649    resizeObserver(this.parentElement!, this.schedSwitchTbl!);
650  }
651  initHtml(): string {
652    return (
653      `
654      <style>
655      :host{
656          padding: 10px 10px;
657          display: flex;
658          flex-direction: column;
659      }
660      #data-cut{
661          display: flex;
662          justify-content: space-between;
663          width:100%;
664          height:20px;
665          margin-bottom:2px;
666          align-items:center;
667      }
668      button{
669          width:40%;
670          height:100%;
671          border: solid 1px #666666;
672          background-color: rgba(0,0,0,0);
673          border-radius:10px;
674      }
675      #content-section{
676          display: flex;
677          width: 100%;
678      }
679      #query-section{
680          height: 78px;
681          display: flex;
682          flex-direction: column;
683          justify-content: space-evenly;
684          padding: 20px 0px 10px 20px;
685      }
686      .sched-subheading{
687          font-weight: bold;
688          text-align: center;
689      }
690      .labels{
691          display: flex;
692          flex-direction: row;
693          align-items: center;
694          justify-content: center;
695          font-size: 9pt;
696          padding-right: 15px;
697      }
698    ` +
699      this.initStyleContent() +
700      this.initTopContent() +
701      this.initMainContent()
702    );
703  }
704  initStyleContent(): string {
705    return `
706      #right {
707        padding-right: 10px;
708        flex-grow: 1;
709        overflow: auto;
710      }
711      .range-input {
712          width: 120px;
713          height: 18px;
714          border-radius:10px;
715          border:solid 1px #979797;
716          font-size:15px;
717          text-indent:3%;
718      }
719      #cut-threadid {
720          width: 15%;
721          height:90%;
722          border-radius:10px;
723          border:solid 1px #979797;
724          font-size:15px;
725          text-indent:3%
726      }
727      #cut-thread-func {
728          width: 20%;
729          height:90%;
730          border-radius:10px;
731          border:solid 1px #979797;
732          font-size:15px;
733          text-indent:3%
734      }
735      .hint-label {
736          width: 20px;
737          height: 10px;
738          margin-right: 5px
739      }
740      .cycle-title {
741          display: inline-block;
742          width: 61px;
743          height: 100%;
744      }
745      .range {
746          display:flex;
747          align-items: center;
748      }
749    `;
750  }
751  initTopContent(): string {
752    return `
753      .query-btn{
754        height: 20px;
755        width: 90px;
756        border: solid 1px #666666;
757        background-color: rgba(0,0,0,0);
758        border-radius:10px;
759      }
760      button:hover{
761          background-color:#666666;
762          color:white;
763      }
764      :host([canvans-hidden]) #right {
765          display: none
766      }
767      :host([single]) .single-btn {
768          background-color: #666666;
769          color: white
770      }
771      :host([loop]) .loop-btn {
772          background-color: #666666;
773          color: white
774      }
775      :host([query-button]) .query-btn:hover {
776          cursor: pointer;
777      }
778      </style>
779      <div id='data-cut'>
780        <input id="cut-threadid" type="text" placeholder="Please input threadId" value='' onblur="this.value=this.value.replace(/\\D/g,'')"/>
781        <input id="cut-thread-func" type="text" placeholder="Please input funcName" value='' />
782        <div style="width:20%;height: 100%;display:flex;justify-content: space-around;">
783            <button class="single-btn cut-button">Single</button>
784            <button class="loop-btn cut-button">Loop</button>
785        </div>
786      </div>
787    `;
788  }
789  initMainContent(): string {
790    return `
791      <div id="content-section">
792        <div style="height: auto; width: 60%; overflow: auto">
793          <lit-table id="tb-running" style="min-height: 380px; width: 100%" tree>
794            <lit-table-column class="running-percent-column" width="300px" title="Process/Thread/Cycle" data-index="title" key="title" align="flex-start" width="27%" retract>
795            </lit-table-column>
796            <lit-table-column class="running-percent-column" width="160px" title="Cycle start time(ms)" data-index="cycleStartTime" key="cycleStartTime" align="flex-start">
797            </lit-table-column>
798            <lit-table-column class="running-percent-column" width="130px" title="duration(ms)" data-index="duration" key="duration" align="flex-start">
799            </lit-table-column>
800            <lit-table-column class="running-percent-column" width="1fr" title="value" data-index="value" key="value" align="flex-start">
801            </lit-table-column>
802          </lit-table>
803        </div>
804        <lit-slicer-track ></lit-slicer-track>
805        <div id="right">
806          <div id="query-section">
807            <div>
808              <span class="cycle-title">Cycle A:</span>
809              <input id="leftA" type="text" class="range-input" value='' oninput="this.value=this.value.replace(/[^0-9\.]/g,'')" placeholder="duration(ms)"/>
810              <span>~</span>
811              <input id="rightA" type="text" class="range-input" value='' oninput="this.value=this.value.replace(/[^0-9\.]/g,'')" placeholder="duration(ms)"/>
812            </div>
813            <div style="display: flex; justify-content: space-between">
814              <div>
815                <span class="cycle-title">Cycle B:</span>
816                <input id="leftB" type="text" class="range-input" value='' oninput="this.value=this.value.replace(/[^0-9\.]/g,'')" placeholder="duration(ms)"/>
817                <span>~</span>
818                <input id="rightB" type="text" class="range-input" value='' oninput="this.value=this.value.replace(/[^0-9\.]/g,'')" placeholder="duration(ms)"/>
819              </div>
820              <button class="query-btn">Query</button>
821            </div>
822          </div>
823          <div class="sched-subheading"></div>
824          <lit-chart-column id="chart_total" style="width:100%;height:300px"></lit-chart-column>
825          <div style="height: 30px;width: 100%;display: flex;flex-direction: row;align-items: center;justify-content: center">
826            <div class="labels"><div class="hint-label" style="background-color: #2f72f8"></div>Total</div>
827            <div class="labels"><div class="hint-label" style="background-color: #ffab67"></div>Cycle A</div>
828            <div class="labels"><div class="hint-label" style="background-color: #a285d2"></div>Cycle B</div>
829          </div>
830        </div>
831      </div>
832    `;
833  }
834}
835