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 {
18  LitTable,
19  RedrawTreeForm,
20} from '../../../../../base-ui/table/lit-table';
21import { SelectionParam } from '../../../../bean/BoxSelection';
22import '../../../StackBar';
23import { getTabRunningPercent } from '../../../../database/sql/ProcessThread.sql';
24import {
25  queryCpuFreqUsageData,
26  queryCpuFreqFilterId,
27} from '../../../../database/sql/Cpu.sql';
28import { Utils } from '../../base/Utils';
29import { resizeObserver } from '../SheetUtils';
30import { SpSegmentationChart } from '../../../chart/SpSegmentationChart';
31import {
32  type CpuFreqData,
33  type RunningFreqData,
34  type RunningData,
35  type CpuFreqTd,
36} from './TabPaneFreqUsageConfig';
37
38@element('tabpane-frequsage')
39export class TabPaneFreqUsage extends BaseElement {
40  private threadStatesTbl: LitTable | null | undefined;
41  private currentSelectionParam: SelectionParam | undefined;
42  private worker: Worker | undefined;
43  static element: TabPaneFreqUsage;
44
45  set data(threadStatesParam: SelectionParam) {
46    if (this.currentSelectionParam === threadStatesParam) {
47      return;
48    }
49    this.currentSelectionParam = threadStatesParam;
50    this.threadStatesTbl!.recycleDataSource = [];
51    // @ts-ignore
52    this.threadStatesTbl.value = [];
53    this.queryAllData(threadStatesParam);
54  }
55
56  static refresh(): void {
57    this.prototype.queryAllData(TabPaneFreqUsage.element.currentSelectionParam!);
58  }
59
60  async queryAllData(threadStatesParam: SelectionParam): Promise<void> {
61    TabPaneFreqUsage.element.threadStatesTbl!.loading = true;
62    let runningResult: Array<RunningData> = await getTabRunningPercent(
63      threadStatesParam.threadIds,
64      threadStatesParam.processIds,
65      threadStatesParam.leftNs,
66      threadStatesParam.rightNs
67    );
68    // 查询cpu及id信息
69    let cpuIdResult: Array<{ id: number; cpu: number }> =
70      await queryCpuFreqFilterId();
71    // 以键值对形式将cpu及id进行对应,后续会将频点数据与其对应cpu进行整合
72    let IdMap: Map<number, number> = new Map();
73    let queryId: Array<number> = [];
74    let cpuArray: Array<number> = [];
75    for (let i = 0; i < cpuIdResult.length; i++) {
76      queryId.push(cpuIdResult[i].id);
77      IdMap.set(cpuIdResult[i].id, cpuIdResult[i].cpu);
78      cpuArray.push(cpuIdResult[i].cpu);
79    }
80    // 通过id去查询频点数据
81    let cpuFreqResult: Array<CpuFreqTd> = await queryCpuFreqUsageData(queryId);
82    let cpuFreqData: Array<CpuFreqData> = [];
83    for (let i of cpuFreqResult) {
84      cpuFreqData.push({
85        ts: i.startNS + threadStatesParam.recordStartNs,
86        cpu: IdMap.get(i.filter_id)!,
87        value: i.value,
88        dur: i.dur,
89      });
90    }
91    const LEFT_TIME: number = threadStatesParam.leftNs + threadStatesParam.recordStartNs;
92    const RIGHT_TIME: number = threadStatesParam.rightNs + threadStatesParam.recordStartNs;
93    const comPower =
94      SpSegmentationChart.freqInfoMapData.size > 0
95        ? SpSegmentationChart.freqInfoMapData
96        : undefined;
97    const args = {
98      runData: runningResult,
99      cpuFreqData: cpuFreqData,
100      leftNs: LEFT_TIME,
101      rightNs: RIGHT_TIME,
102      cpuArray: cpuArray,
103      comPower: comPower,
104    };
105    TabPaneFreqUsage.element.worker!.postMessage(args);
106    TabPaneFreqUsage.element.worker!.onmessage = (event: MessageEvent): void => {
107      let resultArr: Array<RunningFreqData> = event.data;
108      TabPaneFreqUsage.element.fixedDeal(resultArr, threadStatesParam.traceId);
109      TabPaneFreqUsage.element.threadClick(resultArr);
110      TabPaneFreqUsage.element.threadStatesTbl!.recycleDataSource = resultArr;
111      TabPaneFreqUsage.element.threadStatesTbl!.loading = false;
112    };
113  }
114
115  /**
116   * 表头点击事件
117   */
118  private threadClick(data: Array<RunningFreqData>): void {
119    let labels = this.threadStatesTbl?.shadowRoot
120      ?.querySelector('.th > .td')!
121      .querySelectorAll('label');
122    if (labels) {
123      for (let i = 0; i < labels.length; i++) {
124        let label = labels[i].innerHTML;
125        labels[i].addEventListener('click', (e) => {
126          if (label.includes('Process') && i === 0) {
127            this.threadStatesTbl!.setStatus(data, false);
128            this.threadStatesTbl!.recycleDs =
129              this.threadStatesTbl!.meauseTreeRowElement(
130                data,
131                RedrawTreeForm.Retract
132              );
133          } else if (label.includes('Thread') && i === 1) {
134            for (let item of data) {
135              // @ts-ignore
136              item.status = true;
137              if (item.children !== undefined && item.children.length > 0) {
138                this.threadStatesTbl!.setStatus(item.children, false);
139              }
140            }
141            this.threadStatesTbl!.recycleDs =
142              this.threadStatesTbl!.meauseTreeRowElement(
143                data,
144                RedrawTreeForm.Retract
145              );
146          } else if (label.includes('CPU') && i === 2) {
147            this.threadStatesTbl!.setStatus(data, true);
148            this.threadStatesTbl!.recycleDs =
149              this.threadStatesTbl!.meauseTreeRowElement(
150                data,
151                RedrawTreeForm.Expand
152              );
153          }
154        });
155      }
156    }
157  }
158
159
160  initElements(): void {
161    this.threadStatesTbl = this.shadowRoot?.querySelector<LitTable>(
162      '#tb-running-percent'
163    );
164    //开启一个线程计算busyTime
165    this.worker = new Worker(
166      new URL('../../../../database/TabPaneFreqUsageWorker', import.meta.url)
167    );
168    TabPaneFreqUsage.element = this;
169  }
170
171  connectedCallback(): void {
172    super.connectedCallback();
173    resizeObserver(this.parentElement!, this.threadStatesTbl!, 20);
174  }
175
176  initHtml(): string {
177    return `
178        <style>
179        :host{
180            padding: 10px 10px;
181            display: flex;
182            flex-direction: column;
183        }
184        </style>
185        <lit-table id="tb-running-percent" style="height: auto; overflow-x:auto;" tree>
186          <lit-table-column class="running-percent-column" width="320px" title="Process/Thread/CPU" data-index="thread" key="thread" align="flex-start" retract>
187          </lit-table-column>
188          <lit-table-column class="running-percent-column" width="100px" title="CPU" data-index="cpu" key="cpu" align="flex-start">
189          </lit-table-column>
190          <lit-table-column class="running-percent-column" width="240px" title="Consume(MHz*ms)" data-index="consumption" key="consumption" align="flex-start">
191          </lit-table-column>
192          <lit-table-column class="running-percent-column" width="200px" title="Freq(MHz:Cap)" data-index="frequency" key="frequency" align="flex-start">
193          </lit-table-column>
194          <lit-table-column class="running-percent-column" width="240px" title="Consume(cap*ms)" data-index="consumpower" key="consumpower" align="flex-start">
195          </lit-table-column>
196          <lit-table-column class="running-percent-column" width="100px" title="TaskUtil(%)" data-index="cpuload" key="cpuload" align="flex-start">
197          </lit-table-column>
198          <lit-table-column class="running-percent-column" width="200px" title="Dur(ms)" data-index="dur" key="dur" align="flex-start">
199          </lit-table-column>
200          <lit-table-column class="running-percent-column" width="140px" title="Dur/All_Dur(%)" data-index="percent" key="percent" align="flex-start">
201          </lit-table-column>
202        </lit-table>
203        `;
204  }
205
206  /**
207   * 递归整理数据小数位
208   */
209  fixedDeal(arr: Array<RunningFreqData>, traceId?: string | null): void {
210    if (arr == undefined) {
211      return;
212    }
213    const TIME_MUTIPLE: number = 1000000;
214    // KHz->MHz  *  ns->ms
215    const CONS_MUTIPLE: number = 1000000000;
216    const MIN_PERCENT: number = 2;
217    const MIN_FREQ: number = 3;
218    const MIN_POWER: number = 6;
219    for (let i = 0; i < arr.length; i++) {
220      let trackId: number;
221      // 若存在空位元素则进行删除处理
222      if (arr[i] === undefined) {
223        arr.splice(i, 1);
224        i--;
225        continue;
226      }
227      if (arr[i].thread?.indexOf('P') !== -1) {
228        trackId = Number(arr[i].thread?.slice(1)!);
229        arr[i].thread = `${Utils.getInstance().getProcessMap(traceId).get(trackId) || 'Process'} ${trackId}`;
230      } else if (arr[i].thread === 'summary data') {
231      } else {
232        trackId = Number(arr[i].thread!.split('_')[1]);
233        arr[i].thread = `${Utils.getInstance().getThreadMap(traceId).get(trackId) || 'Thread'} ${trackId}`;
234      }
235      if (arr[i].cpu < 0) {
236        // @ts-ignore
237        arr[i].cpu = '';
238      }
239      // @ts-ignore
240      if (arr[i].frequency < 0) {
241        arr[i].frequency = '';
242      }
243      if (!arr[i].cpuload) {
244        // @ts-ignore
245        arr[i].cpuload = '0.000000';
246      } else {
247        // @ts-ignore
248        arr[i].cpuload = arr[i].cpuload.toFixed(MIN_POWER);
249      }
250      // @ts-ignore
251      arr[i].percent = arr[i].percent.toFixed(MIN_PERCENT);
252      // @ts-ignore
253      arr[i].dur = (arr[i].dur / TIME_MUTIPLE).toFixed(MIN_FREQ);
254      // @ts-ignore
255      arr[i].consumption = (arr[i].consumption / CONS_MUTIPLE).toFixed(MIN_FREQ);
256      // @ts-ignore
257      arr[i].consumpower = (arr[i].consumpower / TIME_MUTIPLE).toFixed(MIN_FREQ);
258      if (arr[i].frequency !== '') {
259        if (arr[i].frequency === 'unknown') {
260          arr[i].frequency = 'unknown';
261        } else {
262          arr[i].frequency = arr[i].frequency;
263        }
264      }
265      this.fixedDeal(arr[i].children!, traceId);
266    }
267  }
268}