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 { procedurePool } from '../../database/Procedure';
19import { info } from '../../../log/Log';
20import '../../../base-ui/chart/pie/LitChartPie';
21import { LitChartPie } from '../../../base-ui/chart/pie/LitChartPie';
22import { LitSelect } from '../../../base-ui/select/LitSelect';
23import { LitSelectOption } from '../../../base-ui/select/LitSelectOption';
24import '../../../base-ui/progress-bar/LitProgressBar';
25import { LitProgressBar } from '../../../base-ui/progress-bar/LitProgressBar';
26import './TableNoData';
27import { TableNoData } from './TableNoData';
28import { getProbablyTime } from '../../database/logic-worker/ProcedureLogicWorkerCommon';
29import { queryThreads } from '../../database/sql/ProcessThread.sql';
30import { Top20FrequencyThreadHtml } from './Top20FrequencyThread.html';
31
32@element('top20-frequency-thread')
33export class Top20FrequencyThread extends BaseElement {
34  static threads: { id: number; tid: number; name: string }[] | undefined;
35  traceChange: boolean = false;
36  private frequencyThreadTbl: LitTable | null | undefined;
37  private threadSelect: LitSelect | null | undefined;
38  private frequencyThreadPie: LitChartPie | null | undefined;
39  private currentThread: HTMLDivElement | null | undefined;
40  private frequencyThreadProgress: LitProgressBar | null | undefined;
41  private nodata: TableNoData | null | undefined;
42  private currentTid: number = 0;
43  private frequencyThreadData: Array<unknown> = [];
44  private sortColumn: string = '';
45  private sortType: number = 0;
46
47  initElements(): void {
48    this.nodata = this.shadowRoot!.querySelector<TableNoData>('#nodata');
49    this.frequencyThreadProgress = this.shadowRoot!.querySelector<LitProgressBar>('#loading');
50    this.frequencyThreadTbl = this.shadowRoot!.querySelector<LitTable>('#tb-process-thread-count');
51    this.currentThread = this.shadowRoot!.querySelector<HTMLDivElement>('#current_thread');
52    this.threadSelect = this.shadowRoot!.querySelector<LitSelect>('#thread_select');
53    this.frequencyThreadPie = this.shadowRoot!.querySelector<LitChartPie>('#pie');
54
55    this.threadSelect!.onchange = (e): void => {
56      //@ts-ignore
57      this.currentThread!.textContent = (e as unknown).detail.text;
58      //@ts-ignore
59      this.currentTid = parseInt(e.detail.selectValue);
60      this.frequencyThreadProgress!.loading = true;
61      this.queryData();
62    };
63
64    this.frequencyThreadTbl!.addEventListener('row-click', (evt: unknown): void => {
65      //@ts-ignore
66      let data = evt.detail.data;
67      data.isSelected = true; //@ts-ignore
68      if ((evt.detail as unknown).callBack) {
69        //@ts-ignore
70        (evt.detail as unknown).callBack(true);
71      }
72    });
73
74    this.frequencyThreadTbl!.addEventListener('column-click', (evt: unknown): void => {
75      //@ts-ignore
76      this.sortColumn = evt.detail.key; //@ts-ignore
77      this.sortType = evt.detail.sort;
78      // @ts-ignore
79      this.sortByColumn(evt.detail);
80    });
81    this.frequencyThreadTbl!.addEventListener('row-hover', (evt: unknown): void => {
82      //@ts-ignore
83      if (evt.detail.data) {
84        //@ts-ignore
85        let data = evt.detail.data;
86        data.isHover = true; //@ts-ignore
87        if ((evt.detail as unknown).callBack) {
88          //@ts-ignore
89          (evt.detail as unknown).callBack(true);
90        }
91      }
92      this.frequencyThreadPie?.showHover();
93    }); // @ts-ignore
94    this.frequencyThreadTbl!.itemTextHandleMap.set('freq', (value) => (value === -1 ? 'unknown' : value));
95  }
96
97  sortByColumn(detail: unknown): void {
98    // @ts-ignore
99    function compare(frequencyThreadProperty, sort, type) {
100      return function (a: unknown, b: unknown) {
101        if (type === 'number') {
102          // @ts-ignore
103          return sort === 2
104            ? // @ts-ignore
105              parseFloat(b[frequencyThreadProperty]) - parseFloat(a[frequencyThreadProperty])
106            : //@ts-ignore
107              parseFloat(a[frequencyThreadProperty]) - parseFloat(b[frequencyThreadProperty]);
108        } else {
109          if (sort === 2) {
110            //@ts-ignore
111            return b[frequencyThreadProperty].toString().localeCompare(a[frequencyThreadProperty].toString());
112          } else {
113            //@ts-ignore
114            return a[frequencyThreadProperty].toString().localeCompare(b[frequencyThreadProperty].toString());
115          }
116        }
117      };
118    }
119
120    //@ts-ignore
121    if (detail.key === 'timeStr') {
122      //@ts-ignore
123      detail.key = 'time'; //@ts-ignore
124      this.frequencyThreadData.sort(compare(detail.key, detail.sort, 'number')); //@ts-ignore
125    } else if (detail.key === 'no' || detail.key === 'cpu' || detail.key === 'freq' || detail.key === 'ratio') {
126      //@ts-ignore
127      this.frequencyThreadData.sort(compare(detail.key, detail.sort, 'number'));
128    } else {
129      //@ts-ignore
130      this.frequencyThreadData.sort(compare(detail.key, detail.sort, 'string'));
131    }
132    this.frequencyThreadTbl!.recycleDataSource = this.frequencyThreadData;
133  }
134
135  async init(): Promise<void> {
136    if (!this.traceChange) {
137      if (this.frequencyThreadTbl!.recycleDataSource.length > 0) {
138        this.frequencyThreadTbl?.reMeauseHeight();
139      }
140      return;
141    }
142    this.traceChange = false;
143    this.frequencyThreadProgress!.loading = true;
144    if (Top20FrequencyThread.threads === undefined) {
145      //@ts-ignore
146      Top20FrequencyThread.threads = (await queryThreads()) || [];
147      this.nodata!.noData = Top20FrequencyThread.threads === undefined || Top20FrequencyThread.threads.length === 0;
148      this.threadSelect!.innerHTML = ''; //@ts-ignore
149      let threads = Top20FrequencyThread.threads.map((it) => {
150        let option = new LitSelectOption();
151        option.setAttribute('value', `${it.tid}`);
152        option.textContent = it.name;
153        return option;
154      });
155      this.threadSelect!.append(...threads);
156      this.threadSelect?.initOptions(); //@ts-ignore
157      this.threadSelect!.value = `${Top20FrequencyThread.threads[0].tid}`; //@ts-ignore
158      this.currentThread!.textContent = Top20FrequencyThread.threads[0].name; //@ts-ignore
159      this.currentTid = Top20FrequencyThread.threads[0].tid;
160      this.queryData();
161    }
162  }
163
164  queryData(): void {
165    this.queryLogicWorker('scheduling-Thread Freq', 'query Thread Top 20 Frequency Time:', (res): void => {
166      this.nodata!.noData =
167        Top20FrequencyThread.threads === undefined ||
168        Top20FrequencyThread.threads.length === 0 ||
169        res === undefined || //@ts-ignore
170        res.length === 0;
171      (res as unknown[]).map((it: unknown, index: number): void => {
172        //@ts-ignore
173        it.no = index + 1;
174      }); //@ts-ignore
175      this.frequencyThreadData = res;
176      if (this.sortColumn !== '') {
177        this.sortByColumn({
178          key: this.sortColumn,
179          sort: this.sortType,
180        });
181      } else {
182        //@ts-ignore
183        this.frequencyThreadTbl!.recycleDataSource = res;
184      }
185      this.frequencyThreadTbl!.reMeauseHeight();
186      this.setThreadPieConfig(res);
187      this.frequencyThreadProgress!.loading = false;
188      this.shadowRoot!.querySelector('#tb_vessel')!.scrollTop = 0;
189    });
190  }
191
192  private setThreadPieConfig(res: unknown): void {
193    this.frequencyThreadPie!.config = {
194      appendPadding: 10, //@ts-ignore
195      data: this.getPieChartData(res),
196      angleField: 'time',
197      colorField: 'freq',
198      colorFieldTransferHandler: (value) => (value === -1 ? 'unknown' : value),
199      radius: 0.8,
200      label: {
201        type: 'outer',
202      },
203      tip: (obj): string => {
204        return `<div>
205                             <div>freq:${
206                               // @ts-ignore
207                               obj.obj.freq === -1 ? 'unknown' : obj.obj.freq
208                             }</div> 
209                             <div>cpu:${
210                               // @ts-ignore
211                               obj.obj.cpu
212                             }</div> 
213                             <div>time:${
214                               // @ts-ignore
215                               obj.obj.timeStr
216                             }</div> 
217                             <div>ratio:${
218                               // @ts-ignore
219                               obj.obj.ratio
220                             }%</div>
221                        </div>
222                `;
223      },
224      hoverHandler: (data): void => {
225        if (data) {
226          this.frequencyThreadTbl!.setCurrentHover(data);
227        } else {
228          this.frequencyThreadTbl!.mouseOut();
229        }
230      },
231      interactions: [
232        {
233          type: 'element-active',
234        },
235      ],
236    };
237  }
238
239  getPieChartData(res: unknown[]): unknown[] {
240    if (res.length > 20) {
241      let pieChartArr: unknown[] = [];
242      let other: unknown = {
243        cpu: '-',
244        freq: 'other',
245        time: 0,
246        ratio: '0',
247        totalDur: 0,
248      };
249      for (let i = 0; i < res.length; i++) {
250        if (i < 19) {
251          pieChartArr.push(res[i]);
252        } else {
253          //@ts-ignore
254          other.time += res[i].time; //@ts-ignore
255          other.timeStr = getProbablyTime(other.time); //@ts-ignore
256          other.totalDur = res[i].totalDur; //@ts-ignore
257          other.ratio = ((other.time / other.totalDur) * 100).toFixed(2);
258        }
259      }
260      pieChartArr.push(other);
261      return pieChartArr;
262    }
263    return res;
264  }
265
266  clearData(): void {
267    this.traceChange = true;
268    this.threadSelect!.innerHTML = '';
269    this.frequencyThreadPie!.dataSource = [];
270    this.frequencyThreadTbl!.recycleDataSource = [];
271  }
272
273  queryLogicWorker(option: string, log: string, handler: (res: unknown) => void): void {
274    let frequencyThreadTime = new Date().getTime();
275    procedurePool.submitWithName('logic0', option, { tid: this.currentTid }, undefined, handler);
276    let durTime = new Date().getTime() - frequencyThreadTime;
277    info(log, durTime);
278  }
279
280  initHtml(): string {
281    return Top20FrequencyThreadHtml;
282  }
283}
284