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 { LitTable } from '../../../base-ui/table/lit-table';
18import { LitChartColumn } from '../../../base-ui/chart/column/LitChartColumn';
19import '../../../base-ui/chart/column/LitChartColumn';
20import './CheckCpuSetting';
21import '../../../base-ui/icon/LitIcon';
22import { CheckCpuSetting } from './CheckCpuSetting';
23import { procedurePool } from '../../database/Procedure';
24import { info } from '../../../log/Log';
25import '../../../base-ui/progress-bar/LitProgressBar';
26import { LitProgressBar } from '../../../base-ui/progress-bar/LitProgressBar';
27import './TableNoData';
28import { TableNoData } from './TableNoData';
29import { getProbablyTime } from '../../database/logic-worker/ProcedureLogicWorkerCommon';
30import { SpSchedulingAnalysis } from './SpSchedulingAnalysis';
31import { Top20ThreadCpuUsageHtml } from './Top20ThreadCpuUsage.html';
32
33@element('top20-thread-cpu-usage')
34export class Top20ThreadCpuUsage extends BaseElement {
35  traceChange: boolean = false;
36  private table: LitTable | null | undefined;
37  private tableBig: LitTable | null | undefined;
38  private tableMid: LitTable | null | undefined;
39  private tableSmall: LitTable | null | undefined;
40  private chartTotal: LitChartColumn | null | undefined;
41  private chart2: LitChartColumn | null | undefined;
42  private chart3: LitChartColumn | null | undefined;
43  private chart4: LitChartColumn | null | undefined;
44  private cpuSetting: CheckCpuSetting | undefined | null;
45  private setting: HTMLDivElement | null | undefined;
46  private progress: LitProgressBar | null | undefined;
47  private nodata: TableNoData | null | undefined;
48  private map: Map<string, { chart: LitChartColumn; table: LitTable }> | undefined;
49  private data: Array<unknown> = [];
50  private dataBig: Array<unknown> = [];
51  private dataMid: Array<unknown> = [];
52  private dataSmall: Array<unknown> = [];
53  private sort: unknown = {
54    total: { key: '', sort: 0 },
55    small: { key: '', sort: 0 },
56    mid: { key: '', sort: 0 },
57    big: { key: '', sort: 0 },
58  };
59
60  private publicColumns = `
61                <lit-table-column width="50px" title=" " data-index="no" key="no" align="flex-start"></lit-table-column>
62                <lit-table-column width="50px" title="" data-index="visible" key="visible" align="flex-start">
63                    <template>
64                        <lit-icon name="{{ visible === 1 ? 'eye':'eye-close' }}" onclick="{
65                            let data = this.parentElement.parentElement.data;
66                            data.visible = data.visible === 1 ? 0 : 1
67                            this.name = data.visible === 1 ? 'eye':'eye-close'
68                            data.hideHandler()
69                        }" size="20"></lit-icon>
70                    </template>
71                </lit-table-column>
72                <lit-table-column width="100px" title="tid" data-index="tid" key="tid" align="flex-start" order></lit-table-column>
73                <lit-table-column width="200px" title="t_name" data-index="tName" key="tName" align="flex-start" order></lit-table-column>
74                <lit-table-column width="100px" title="pid" data-index="pid" key="pid" align="flex-start" order></lit-table-column>
75                <lit-table-column width="200px" title="p_name" data-index="pName" key="pName" align="flex-start" order></lit-table-column>
76        `;
77  private bigColumn = `
78                <lit-table-column width="100px" title="big core" data-index="bigTimeStr" key="bigTimeStr" align="flex-start" order></lit-table-column>
79                <lit-table-column width="100px" title="%" data-index="bigPercent" key="bigPercent" align="flex-start" order></lit-table-column>
80        `;
81  private midColumn = `
82                <lit-table-column width="100px" title="middle core" data-index="midTimeStr" key="midTimeStr" align="flex-start" order></lit-table-column>
83                <lit-table-column width="100px" title="%" data-index="midPercent" key="midPercent" align="flex-start" order></lit-table-column>
84        `;
85  private smallColumn = `
86                <lit-table-column width="100px" title="small core" data-index="smallTimeStr" key="smallTimeStr" align="flex-start" order></lit-table-column>
87                <lit-table-column width="100px" title="%" data-index="smallPercent" key="smallPercent" align="flex-start" order></lit-table-column>
88        `;
89
90  initElements(): void {
91    this.nodata = this.shadowRoot!.querySelector<TableNoData>('#nodata');
92    this.progress = this.shadowRoot!.querySelector<LitProgressBar>('#loading');
93    this.table = this.shadowRoot!.querySelector<LitTable>('#tb-thread-usage');
94    this.tableBig = this.shadowRoot!.querySelector<LitTable>('#tb-thread-big');
95    this.tableMid = this.shadowRoot!.querySelector<LitTable>('#tb-thread-mid');
96    this.tableSmall = this.shadowRoot!.querySelector<LitTable>('#tb-thread-small');
97    this.chartTotal = this.shadowRoot!.querySelector<LitChartColumn>('#chart_total');
98    this.chart2 = this.shadowRoot!.querySelector<LitChartColumn>('#chart_2');
99    this.chart3 = this.shadowRoot!.querySelector<LitChartColumn>('#chart_3');
100    this.chart4 = this.shadowRoot!.querySelector<LitChartColumn>('#chart_4');
101    this.map = new Map<string, { chart: LitChartColumn; table: LitTable }>();
102    this.map.set('total', { chart: this.chartTotal!, table: this.table! });
103    this.map.set('small', { chart: this.chart2!, table: this.tableSmall! });
104    this.map.set('mid', { chart: this.chart3!, table: this.tableMid! });
105    this.map.set('big', { chart: this.chart4!, table: this.tableBig! });
106    this.setting = this.shadowRoot!.querySelector<HTMLDivElement>('#setting');
107    this.cpuSetting = this.shadowRoot!.querySelector<CheckCpuSetting>('#cpu_setting');
108    this.cpuSetting!.cpuSetListener = (): void => {
109      this.cpuSetting!.style.display = 'none';
110      //@ts-ignore
111      (this.shadowRoot!.querySelector('#total')! as unknown).style.display = 'grid';
112      //@ts-ignore
113      (this.shadowRoot!.querySelector('#small')! as unknown).style.display =
114        CheckCpuSetting.small_cores.length > 0 ? 'grid' : 'none';
115      //@ts-ignore
116      (this.shadowRoot!.querySelector('#mid')! as unknown).style.display =
117        CheckCpuSetting.mid_cores.length > 0 ? 'grid' : 'none';
118      //@ts-ignore
119      (this.shadowRoot!.querySelector('#big')! as unknown).style.display =
120        CheckCpuSetting.big_cores.length > 0 ? 'grid' : 'none';
121      this.queryData();
122    };
123    this.setting?.addEventListener('click', (): void => {
124      for (let node of this.shadowRoot!.querySelectorAll('.content_grid')) {
125        //@ts-ignore
126        (node as unknown).style.display = 'none';
127      }
128      this.cpuSetting!.style.display = 'inline';
129      this.cpuSetting?.init();
130    });
131    this.tabListener();
132  }
133
134  private tabListener(): void {
135    for (let key of this.map!.keys()) {
136      let tab = this.map!.get(key)!.table;
137      let chart = this.map!.get(key)!.chart;
138      tab!.addEventListener('row-click', (evt: unknown): void => {
139        //@ts-ignore
140        let data = evt.detail.data;
141        data.isSelected = true;
142        // @ts-ignore
143        if ((evt.detail as unknown).callBack) {
144          // @ts-ignore
145          (evt.detail as unknown).callBack(true);
146        }
147      });
148      tab!.addEventListener('column-click', (evt: unknown): void => {
149        //@ts-ignore
150        this.sort[key].key = evt.detail.key;
151        //@ts-ignore
152        this.sort[key].sort = evt.detail.sort;
153        if (key === 'total') {
154          //@ts-ignore
155          this.sortByColumn(evt.detail, tab, this.data);
156        } else if (key === 'small') {
157          //@ts-ignore
158          this.sortByColumn(evt.detail, tab, this.dataSmall);
159        } else if (key === 'mid') {
160          //@ts-ignore
161          this.sortByColumn(evt.detail, tab, this.dataMid);
162        } else if (key === 'big') {
163          //@ts-ignore
164          this.sortByColumn(evt.detail, tab, this.dataBig);
165        }
166      });
167      tab!.addEventListener('row-hover', (evt: unknown): void => {
168        //@ts-ignore
169        if (evt.detail.data) {
170          //@ts-ignore
171          let data = evt.detail.data;
172          data.isHover = true;
173          //@ts-ignore
174          if ((evt.detail as unknown).callBack) {
175            //@ts-ignore
176            (evt.detail as unknown).callBack(true);
177          }
178          chart.showHoverColumn(data.no);
179        }
180      });
181    }
182  }
183
184  sortByColumn(detail: unknown, table: LitTable | null | undefined, data: Array<unknown>): void {
185    // @ts-ignore
186    function compare(threadCpuUsageProperty, sort, type) {
187      return function (a: unknown, b: unknown) {
188        if (type === 'number') {
189          return sort === 2
190            ? // @ts-ignore
191              parseFloat(b[threadCpuUsageProperty]) - parseFloat(a[threadCpuUsageProperty])
192            : //@ts-ignore
193              parseFloat(a[threadCpuUsageProperty]) - parseFloat(b[threadCpuUsageProperty]);
194        } else {
195          if (sort === 2) {
196            //@ts-ignore
197            return b[threadCpuUsageProperty].toString().localeCompare(a[threadCpuUsageProperty].toString());
198          } else {
199            //@ts-ignore
200            return a[threadCpuUsageProperty].toString().localeCompare(b[threadCpuUsageProperty].toString());
201          }
202        }
203      };
204    }
205
206    let type = 'number';
207    //@ts-ignore
208    let key = detail.key;
209
210    if (key === 'bigTimeStr') {
211      key = 'big';
212    } else if (key === 'midTimeStr') {
213      key = 'mid';
214    } else if (key === 'smallTimeStr') {
215      key = 'small';
216    } else if (
217      key === 'bigPercent' ||
218      key === 'ratio' ||
219      key === 'tid' ||
220      key === 'pid' ||
221      key === 'midPercent' ||
222      key.includes('cpu')
223    ) {
224    } else {
225      type = 'string';
226    }
227    //@ts-ignore
228    data.sort(compare(key, detail.sort, type));
229    table!.recycleDataSource = data;
230  }
231
232  init(): void {
233    if (!this.traceChange) {
234      for (let key of this.map!.keys()) {
235        this.map!.get(key)!.table.reMeauseHeight();
236      }
237      return;
238    }
239    this.traceChange = false;
240    for (let key of this.map!.keys()) {
241      let table = this.map!.get(key)!.table;
242      table.innerHTML = '';
243      let columns = this.getTableColumns(key);
244      for (let i = 0; i < SpSchedulingAnalysis.cpuCount; i++) {
245        columns = `
246                ${columns}
247                <lit-table-column width="120px" title="cpu${i}(us)" data-index="cpu${i}" key="cpu${i}" align="flex-start" order></lit-table-column>
248            `;
249      }
250      table.innerHTML = columns;
251    }
252
253    if (!CheckCpuSetting.init_setting) {
254      for (let node of this.shadowRoot!.querySelectorAll('.content_grid')) {
255        //@ts-ignore
256        (node as unknown).style.display = 'none';
257      }
258      this.cpuSetting!.style.display = 'inline';
259      this.cpuSetting?.init();
260    } else {
261      this.queryData();
262    }
263  }
264
265  clearData(): void {
266    this.traceChange = true;
267    for (let key of this.map!.keys()) {
268      this.map!.get(key)!.chart.dataSource = [];
269      this.map!.get(key)!.table.recycleDataSource = [];
270    }
271  }
272
273  queryData(): void {
274    this.progress!.loading = true;
275    this.queryLogicWorker('scheduling-Thread CpuUsage', 'query Thread Cpu Usage Analysis Time:', (res): void => {
276      //@ts-ignore
277      this.nodata!.noData = res.keys().length === 0;
278      for (let key of this.map!.keys()) {
279        let obj = this.map!.get(key)!;
280        //@ts-ignore
281        let source: unknown[] = res.get(key) || [];
282        source = source.map((it: unknown, index: number) => {
283          let data: unknown = {
284            //@ts-ignore
285            pid: it.pid,
286            //@ts-ignore
287            pName: it.pName,
288            //@ts-ignore
289            tid: it.tid,
290            //@ts-ignore
291            tName: it.tName,
292            //@ts-ignore
293            total: it.total,
294            //@ts-ignore
295            big: it.big,
296            //@ts-ignore
297            mid: it.mid,
298            //@ts-ignore
299            small: it.small,
300            no: index + 1,
301            visible: 1,
302            //@ts-ignore
303            bigPercent: it.bigPercent,
304            //@ts-ignore
305            midPercent: it.midPercent,
306            //@ts-ignore
307            smallPercent: it.smallPercent,
308            //@ts-ignore
309            bigTimeStr: it.bigTimeStr,
310            //@ts-ignore
311            midTimeStr: it.midTimeStr,
312            //@ts-ignore
313            smallTimeStr: it.smallTimeStr,
314            hideHandler: (): void => {
315              //@ts-ignore
316              let arr = source.filter((o) => o.visible === 1);
317              obj.chart.dataSource = this.getArrayDataBySize(key, arr);
318            },
319          };
320          for (let i = 0; i < SpSchedulingAnalysis.cpuCount; i++) {
321            //@ts-ignore
322            data[`cpu${i}`] = (it[`cpu${i}`] || 0) / 1000;
323          }
324          return data;
325        });
326        this.setChartConfig(obj, key, source);
327        this.assignmentData(key, source, obj);
328      }
329      this.progress!.loading = false;
330    });
331  }
332
333  private assignmentData(key: string, source: unknown[], obj: { chart: LitChartColumn; table: LitTable }): void {
334    if (key === 'total') {
335      this.data = source;
336    } else if (key === 'small') {
337      this.dataSmall = source;
338    } else if (key === 'mid') {
339      this.dataMid = source;
340    } else if (key === 'big') {
341      this.dataBig = source;
342    }
343    //@ts-ignore
344    if (this.sort[key].key !== '') {
345      //@ts-ignore
346      this.sortByColumn(this.sort[key], obj.table, source);
347    } else {
348      obj.table.recycleDataSource = source;
349    }
350  }
351
352  private setChartConfig(obj: { chart: LitChartColumn; table: LitTable }, key: string, source: unknown[]): void {
353    obj.chart.config = {
354      data: this.getArrayDataBySize(key, source),
355      appendPadding: 10,
356      xField: 'tid',
357      yField: 'total',
358      seriesField: key === 'total' ? 'size' : '',
359      color: (a): string => {
360        //@ts-ignore
361        if (a.size === 'big core') {
362          return '#2f72f8'; //@ts-ignore
363        } else if (a.size === 'middle core') {
364          return '#ffab67'; //@ts-ignore
365        } else if (a.size === 'small core') {
366          return '#a285d2';
367        } else {
368          return '#0a59f7';
369        }
370      },
371      hoverHandler: (no): void => {
372        this.setHover(source, no, obj);
373      },
374      tip: (a): string => {
375        //@ts-ignore
376        if (a && a[0]) {
377          let tip = '';
378          let total = 0; //@ts-ignore
379          for (let obj of a) {
380            total += obj.obj.total;
381            tip = `${tip}
382                                <div style="display:flex;flex-direction: row;align-items: center;">
383                                    <div style="width: 10px;height: 5px;background-color: ${obj.color};
384                                    margin-right: 5px"></div>
385                                    <div>${obj.type || key}:${obj.obj.timeStr}</div>
386                                </div>
387                            `;
388          }
389          tip = `<div>
390                                        <div>tid:${
391                                          //@ts-ignore
392                                          a[0].obj.tid
393                                        }</div>
394                                        ${tip}
395                                        ${
396                                          //@ts-ignore
397                                          a.length > 1 ? `<div>total:${getProbablyTime(total)}</div>` : ''
398                                        }
399                                    </div>`;
400          return tip;
401        } else {
402          return '';
403        }
404      },
405      label: null,
406    };
407  }
408
409  private setHover(source: unknown[], no: number, obj: { chart: LitChartColumn; table: LitTable }): void {
410    //@ts-ignore
411    let data = source.find((it) => it.no === no);
412    if (data) {
413      //@ts-ignore
414      data.isHover = true;
415      obj.table!.setCurrentHover(data);
416    } else {
417      obj.table!.mouseOut();
418    }
419  }
420
421  getArrayDataBySize(type: string, arr: Array<unknown>): unknown[] {
422    let data: unknown[] = [];
423    for (let obj of arr) {
424      if (type === 'total') {
425        data.push({
426          //@ts-ignore
427          pid: obj.pid, //@ts-ignore
428          pName: obj.pName, //@ts-ignore
429          tid: obj.tid, //@ts-ignore
430          tName: obj.tName, //@ts-ignore
431          total: obj.big,
432          size: 'big core', //@ts-ignore
433          no: obj.no, //@ts-ignore
434          timeStr: obj.bigTimeStr,
435        });
436        data.push({
437          //@ts-ignore
438          pid: obj.pid, //@ts-ignore
439          pName: obj.pName, //@ts-ignore
440          tid: obj.tid, //@ts-ignore
441          tName: obj.tName, //@ts-ignore
442          total: obj.mid,
443          size: 'middle core', //@ts-ignore
444          no: obj.no, //@ts-ignore
445          timeStr: obj.midTimeStr,
446        });
447        data.push({
448          //@ts-ignore
449          pid: obj.pid, //@ts-ignore
450          pName: obj.pName, //@ts-ignore
451          tid: obj.tid, //@ts-ignore
452          tName: obj.tName, //@ts-ignore
453          total: obj.small,
454          size: 'small core', //@ts-ignore
455          no: obj.no, //@ts-ignore
456          timeStr: obj.smallTimeStr,
457        });
458      } else {
459        data.push({
460          //@ts-ignore
461          pid: obj.pid, //@ts-ignore
462          pName: obj.pName, //@ts-ignore
463          tid: obj.tid, //@ts-ignore
464          tName: obj.tName, //@ts-ignore
465          total: obj[type], //@ts-ignore
466          no: obj.no, //@ts-ignore
467          timeStr: obj[`${type}TimeStr`],
468        });
469      }
470    }
471    return data;
472  }
473
474  queryLogicWorker(option: string, log: string, handler: (res: unknown) => void): void {
475    let time = new Date().getTime();
476    procedurePool.submitWithName(
477      'logic0',
478      option,
479      {
480        bigCores: CheckCpuSetting.big_cores,
481        midCores: CheckCpuSetting.mid_cores,
482        smallCores: CheckCpuSetting.small_cores,
483      },
484      undefined,
485      handler
486    );
487    let durTime = new Date().getTime() - time;
488    info(log, durTime);
489  }
490
491  getTableColumns(type: string): string {
492    if (type === 'total') {
493      return `${this.publicColumns}${this.bigColumn}${this.midColumn}${this.smallColumn}`;
494    } else if (type === 'big') {
495      return `${this.publicColumns}${this.bigColumn}`;
496    } else if (type === 'mid') {
497      return `${this.publicColumns}${this.midColumn}`;
498    } else if (type === 'small') {
499      return `${this.publicColumns}${this.smallColumn}`;
500    } else {
501      return '';
502    }
503  }
504
505  initHtml(): string {
506    return Top20ThreadCpuUsageHtml;
507  }
508}
509