• 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 { BaseStruct, drawLoadingFrame, isFrameContainPoint, Rect, Render, ns2x } from './ProcedureWorkerCommon';
17import { TraceRow } from '../../component/trace/base/TraceRow';
18import { ColorUtils } from '../../component/trace/base/ColorUtils';
19import { SpSystemTrace } from '../../component/SpSystemTrace';
20
21enum Type {
22  'C180HZ',
23  'C120HZ',
24  'C90HZ',
25  'C60HZ',
26  'C45HZ',
27  'C30HZ',
28  'C24HZ',
29  'C15HZ',
30  'C10HZ',
31  'C5HZ',
32  'C1HZ',
33}
34
35export class XpowerAppDetailRender extends Render {
36  renderMainThread(
37    xpowerAppDetailReq: {
38      context: CanvasRenderingContext2D;
39      useCache: boolean;
40    },
41    row: TraceRow<XpowerAppDetailStruct>
42  ): void {
43    let checkedType = row.rowSettingCheckedBoxList;
44    let checkedValue = row.rowSettingCheckBoxList;
45    // offsetW控制图例的横向偏移量 确保图例不超过画布边界 因收藏和非收藏时泳道的宽度不一致 offsetW根据情况调整
46    let offsetW: number = row.collect ? 60 : 300;
47    let xpowerAppDetailList: XpowerAppDetailStruct[] = row.dataListCache.map((item) => ({ ...item }));
48    xpowerAppDetailList.forEach((item) => {
49      checkedValue?.forEach((type, index) => {
50        if (!checkedType![index]) {
51          // @ts-ignore
52          item['c' + type] = 0;
53        }
54      });
55    });
56    setDataFrameAndHoverHtml(xpowerAppDetailList, row, checkedValue!);
57    drawLoadingFrame(xpowerAppDetailReq.context, xpowerAppDetailList, row);
58    setMaxEnergyInfo(xpowerAppDetailReq.context, xpowerAppDetailList);
59    xpowerAppDetailReq.context.beginPath();
60    let find = false;
61    for (let re of xpowerAppDetailList) {
62      XpowerAppDetailStruct.draw(xpowerAppDetailReq, re, row, isFrameContainPoint(re.frame!, row.hoverX, row.hoverY));
63      if (row.isHover && re.frame && isFrameContainPoint(re.frame!, row.hoverX, row.hoverY)) {
64        XpowerAppDetailStruct.hoverXpowerStruct = re;
65        find = true;
66      }
67    }
68    if (!find) {
69      XpowerAppDetailStruct.hoverXpowerStruct = undefined;
70    }
71    xpowerAppDetailReq.context.closePath();
72    let spApplication = document.getElementsByTagName('sp-application')[0];
73    let isDark = spApplication && spApplication.hasAttribute('dark');
74    drawLegend(xpowerAppDetailReq, checkedType!, checkedValue!, offsetW, isDark);
75  }
76}
77
78function setDataFrameAndHoverHtml(
79  filter: XpowerAppDetailStruct[],
80  row: TraceRow<XpowerAppDetailStruct>,
81  checkedValue: string[]
82): void {
83  filter.forEach((item) => {
84    XpowerAppDetailStruct.setXPowerAppDetailFrame(
85      item,
86      5,
87      TraceRow.range?.startNS ?? 0,
88      TraceRow.range?.endNS ?? 0,
89      TraceRow.range?.totalNS ?? 0,
90      row.frame
91    );
92    if (!item.hoverHtml) {
93      XpowerAppDetailStruct.setHoverHtml(item, checkedValue);
94    }
95  });
96}
97
98export function drawLegend(
99  req: { context: CanvasRenderingContext2D; useCache: boolean },
100  checked: boolean[],
101  checkedValue: string[],
102  offsetW: number,
103  isDark?: boolean
104): void {
105  let textList: string[] = [];
106  checkedValue.forEach((item, index) => {
107    if (checked[index]) {
108      textList.push(item);
109    }
110  });
111  for (let index = 0; index < textList.length; index++) {
112    let text = req.context.measureText(textList[index]);
113    req.context.fillStyle = ColorUtils.colorForTid(checkedValue.indexOf(textList[index]));
114    req.context.globalAlpha = 1;
115    let canvasEndX = req.context.canvas.clientWidth - offsetW;
116    let textColor = isDark ? '#FFFFFF' : '#333';
117    if (index === 0) {
118      let padding = 0;
119      if (textList.length === 1) {
120        padding = 20;
121      }
122      req!.context.fillRect(canvasEndX - textList.length * 60 - padding, 12, 8, 8);
123      req.context.globalAlpha = 0.8;
124      req.context.fillStyle = textColor;
125      req.context.textBaseline = 'middle';
126      req.context.fillText(textList[index], canvasEndX - textList.length * 60 + 10 - padding, 18);
127      XpowerAppDetailStruct.currentTextWidth = canvasEndX - textList.length * 60 + 40 + text.width;
128    } else {
129      req!.context.fillRect(XpowerAppDetailStruct.currentTextWidth, 12, 8, 8);
130      req.context.globalAlpha = 0.8;
131      req.context.fillStyle = textColor;
132      req.context.textBaseline = 'middle';
133      req!.context.fillText(textList[index], XpowerAppDetailStruct.currentTextWidth + 12, 18);
134      XpowerAppDetailStruct.currentTextWidth = XpowerAppDetailStruct.currentTextWidth + 40 + text.width;
135    }
136  }
137  req.context.fillStyle = '#333';
138}
139
140function setMaxEnergyInfo(context: CanvasRenderingContext2D, filter: XpowerAppDetailStruct[]): void {
141  filter.forEach((item) => {
142    item.total =
143      item.c1hz +
144      item.c5hz +
145      item.c10hz +
146      item.c15hz +
147      item.c24hz +
148      item.c30hz +
149      item.c45hz +
150      item.c60hz +
151      item.c90hz +
152      item.c120hz +
153      item.c180hz;
154  });
155  let max = 0;
156  filter.forEach((item) => {
157    if (max < item.total) {
158      max = item.total;
159    }
160  });
161  XpowerAppDetailStruct.maxEnergy = max;
162  let s = XpowerAppDetailStruct.maxEnergy + ' ms';
163  let textMetrics = context.measureText(s);
164  context.globalAlpha = 0.8;
165  context.fillStyle = '#f0f0f0';
166  context.fillRect(0, 5, textMetrics.width + 8, 18);
167  context.globalAlpha = 1;
168  context.fillStyle = '#333';
169  context.textBaseline = 'middle';
170  context.fillText(s, 4, 5 + 9);
171}
172
173export function XpowerAppDetailStructOnClick(
174  clickRowType: string,
175  sp: SpSystemTrace,
176  entry?: XpowerAppDetailStruct
177): Promise<unknown> {
178  return new Promise((resolve, reject) => {
179    if (
180      clickRowType === TraceRow.ROW_TYPE_XPOWER_APP_DETAIL_DISPLAY &&
181      (XpowerAppDetailStruct.hoverXpowerStruct || entry)
182    ) {
183      XpowerAppDetailStruct.selectXpowerStruct = entry || XpowerAppDetailStruct.hoverXpowerStruct;
184      sp.traceSheetEL?.displayXpowerDisplayData(XpowerAppDetailStruct.selectXpowerStruct!);
185      sp.timerShaftEL?.modifyFlagList(undefined);
186      reject(new Error());
187    } else {
188      resolve(null);
189    }
190  });
191}
192
193export class XpowerAppDetailStruct extends BaseStruct {
194  static rowHeight: number = 200;
195  static maxEnergy: number = 0;
196  static currentTextWidth: number = 0;
197
198  startTime: number = 0;
199  total: number = 0;
200  hoverHtml: string = '';
201  static hoverXpowerStruct: XpowerAppDetailStruct | undefined;
202  static selectXpowerStruct: XpowerAppDetailStruct | undefined;
203
204  c1hz: number = 0;
205  c5hz: number = 0;
206  c10hz: number = 0;
207  c15hz: number = 0;
208  c24hz: number = 0;
209  c30hz: number = 0;
210  c45hz: number = 0;
211  c60hz: number = 0;
212  c90hz: number = 0;
213  c120hz: number = 0;
214  c180hz: number = 0;
215
216  static draw(
217    req: { context: CanvasRenderingContext2D; useCache: boolean },
218    data: XpowerAppDetailStruct,
219    row: TraceRow<XpowerAppDetailStruct>,
220    ishover: boolean
221  ): void {
222    if (data.frame) {
223      req!.context.globalAlpha = 0.8;
224      req!.context.lineWidth = 1;
225      this.currentTextWidth = 0;
226      let c1Height = this.drawHistogram(req, data, -1, data.c1hz!, Type.C1HZ, row.frame);
227      let c5Height = this.drawHistogram(req, data, c1Height, data.c5hz!, Type.C5HZ, row.frame);
228      let c10Height = this.drawHistogram(req, data, c1Height - c5Height, data.c10hz!, Type.C10HZ, row.frame);
229      let c15h = c1Height - c5Height - c10Height;
230      let c15Height = this.drawHistogram(req, data, c15h, data.c15hz!, Type.C15HZ, row.frame);
231      let c24h = c15h - c15Height;
232      let c24Height = this.drawHistogram(req, data, c24h, data.c24hz!, Type.C24HZ, row.frame);
233      let c30h = c24h - c24Height;
234      let c30Height = this.drawHistogram(req, data, c30h, data.c30hz!, Type.C30HZ, row.frame);
235      let c45h = c30h - c30Height;
236      let c45Height = this.drawHistogram(req, data, c45h, data.c45hz!, Type.C45HZ, row.frame);
237      let c60h = c45h - c45Height;
238      let c60Height = this.drawHistogram(req, data, c60h, data.c60hz!, Type.C60HZ, row.frame);
239      let c90h = c60h - c60Height;
240      let c90Height = this.drawHistogram(req, data, c90h, data.c90hz!, Type.C90HZ, row.frame);
241      let c120h = c90h - c90Height;
242      let c120Height = this.drawHistogram(req, data, c120h, data.c120hz!, Type.C120HZ, row.frame);
243      let c180hz = c120h - c120Height;
244      let c180Height = this.drawHistogram(req, data, c180hz, data.c180hz!, Type.C180HZ, row.frame);
245      let startNS = TraceRow.range!.startNS;
246      let endNS = TraceRow.range!.endNS;
247      let totalNS = TraceRow.range!.totalNS;
248      if (
249        (data.startTime === XpowerAppDetailStruct.hoverXpowerStruct?.startTime && ishover) ||
250        (XpowerAppDetailStruct.selectXpowerStruct &&
251          data.startTime === XpowerAppDetailStruct.selectXpowerStruct.startTime)
252      ) {
253        let endPointX = ns2x((data.startTime || 0) + 3000000000, startNS, endNS, totalNS, row.frame);
254        let startPointX = ns2x(data.startTime || 0, startNS, endNS, totalNS, row.frame);
255        let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX;
256        req.context.globalAlpha = 0.8;
257        req!.context.lineWidth = 2;
258        req.context.strokeStyle = '#9899a0';
259        req!.context.strokeRect(startPointX, c180Height, frameWidth, data.frame.height);
260      }
261    }
262    req!.context.globalAlpha = 0.8;
263    req!.context.lineWidth = 1;
264  }
265
266  static drawHistogram(
267    req: { context: CanvasRenderingContext2D; useCache: boolean },
268    data: XpowerAppDetailStruct,
269    height: number,
270    itemValue: number,
271    type: number,
272    rowFrame: Rect
273  ): number {
274    let endPointX = ns2x(
275      data.startTime + 3000000000 || 0,
276      TraceRow.range!.startNS,
277      TraceRow.range!.endNS,
278      TraceRow.range!.totalNS,
279      rowFrame
280    );
281    let startPointX = ns2x(
282      data.startTime || 0,
283      TraceRow.range!.startNS,
284      TraceRow.range!.endNS,
285      TraceRow.range!.totalNS,
286      rowFrame
287    );
288    let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX;
289    let histogramColor = ColorUtils.colorForTid(type);
290    req!.context.fillStyle = histogramColor;
291    let dataHeight: number = Math.floor(((itemValue || 0) * (this.rowHeight - 40)) / XpowerAppDetailStruct.maxEnergy);
292    if (itemValue !== 0 && dataHeight < 15) {
293      dataHeight = 15;
294    }
295    let drawStartY = 0;
296    if (height === -1) {
297      drawStartY = data.frame!.y + this.rowHeight - dataHeight + 4;
298      req!.context.fillRect(startPointX, drawStartY, frameWidth, dataHeight);
299      data.frame!.y = drawStartY;
300      data.frame!.height += dataHeight;
301      return drawStartY;
302    } else {
303      drawStartY = height - dataHeight;
304      req!.context.fillRect(startPointX, drawStartY, frameWidth, dataHeight);
305      if (type === Type.C180HZ) {
306        data.frame!.y = drawStartY;
307        data.frame!.height += dataHeight;
308        return drawStartY;
309      }
310      data.frame!.y = drawStartY;
311      data.frame!.height += dataHeight;
312      return dataHeight;
313    }
314  }
315  static setHoverHtml(node: XpowerAppDetailStruct, checkedValue: string[]): void {
316    let hoverHtml = '';
317    for (let key of checkedValue) {
318      // @ts-ignore
319      let value = node['c' + key];
320      if (value !== 0) {
321        hoverHtml += `<div style="display: grid; width:auto; grid-template-columns: 1fr 1fr;">
322          <div style="text-align: left">${key}:&nbsp;&nbsp;</div>
323          <div style="text-align: left">${value + ' ms'}</div>
324        </div>`;
325      }
326    }
327    node.hoverHtml = hoverHtml;
328  }
329
330  static setXPowerAppDetailFrame(
331    node: XpowerAppDetailStruct,
332    padding: number,
333    startNS: number,
334    endNS: number,
335    totalNS: number,
336    frame: Rect
337  ): void {
338    let startPointX: number;
339    let endPointX: number;
340    if ((node.startTime || 0) <= startNS) {
341      startPointX = 0;
342    } else {
343      startPointX = ns2x(node.startTime, startNS, endNS, totalNS, frame);
344    }
345    if (node.startTime + 3000000000 > endNS) {
346      endPointX = frame.width;
347    } else {
348      endPointX = ns2x(node.startTime + 3000000000, startNS, endNS, totalNS, frame);
349    }
350    let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX;
351    if (!node.frame) {
352      node.frame = new Rect(0, 0, 0, 0);
353    }
354    node.frame.x = Math.floor(startPointX);
355    node.frame.y = frame.y + padding;
356    node.frame.width = Math.ceil(frameWidth);
357    node.frame.height = Math.floor(frame.height - padding * 2);
358  }
359}
360