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 {
17  BaseStruct,
18  drawLoadingFrame,
19  isFrameContainPoint,
20  ns2x,
21  Rect,
22  Render,
23  RequestMessage,
24} from './ProcedureWorkerCommon';
25import { TraceRow } from '../../component/trace/base/TraceRow';
26
27export class EnergySystemRender extends Render {
28  renderMainThread(
29    req: {
30      useCache: boolean;
31      context: CanvasRenderingContext2D;
32      type: string;
33    },
34    row: TraceRow<EnergySystemStruct>
35  ): void {
36    let systemList = row.dataList;
37    let systemFilter = row.dataListCache;
38    system(
39      systemList,
40      systemFilter,
41      TraceRow.range!.startNS || 0,
42      TraceRow.range!.endNS || 0,
43      TraceRow.range!.totalNS || 0,
44      row.frame,
45      req.useCache || !TraceRow.range!.refresh
46    );
47    drawLoadingFrame(req.context, row.dataListCache, row);
48    drawProcedureWorkerEnergy(req, systemFilter, row);
49  }
50}
51
52function drawProcedureWorkerEnergy(
53  req: {
54    useCache: boolean;
55    context: CanvasRenderingContext2D;
56    type: string;
57  },
58  systemFilter: Array<EnergySystemStruct>,
59  row: TraceRow<EnergySystemStruct>
60): void {
61  req.context.beginPath();
62  let find = false;
63  let energySystemData: unknown = {};
64  for (let i = 0; i < systemFilter.length; i++) {
65    let energySysStruct = systemFilter[i];
66    EnergySystemStruct.draw(req.context, energySysStruct);
67    if (row.isHover && energySysStruct.frame && isFrameContainPoint(energySysStruct.frame, row.hoverX, row.hoverY)) {
68      EnergySystemStruct.hoverEnergySystemStruct = energySysStruct;
69      if (energySysStruct.type === 0) {
70        if (energySysStruct.count !== undefined) {
71          // @ts-ignore
72          energySystemData.workScheduler = energySysStruct.count;
73        } else {
74          // @ts-ignore
75          energySystemData.workScheduler = '0';
76        }
77      }
78      if (energySysStruct.type === 1) {
79        if (energySysStruct.count !== undefined) {
80          // @ts-ignore
81          energySystemData.power = energySysStruct.count + '';
82        } else {
83          // @ts-ignore
84          energySystemData.power = '0';
85        }
86      }
87      if (energySysStruct.type === 2) {
88        if (energySysStruct.count !== undefined) {
89          // @ts-ignore
90          energySystemData.location = energySysStruct.count + '';
91        } else {
92          // @ts-ignore
93          energySystemData.location = '0';
94        }
95      }
96      find = true;
97    }
98  }
99  if (!find && row.isHover) {
100    EnergySystemStruct.hoverEnergySystemStruct = undefined;
101  }
102  if (EnergySystemStruct.hoverEnergySystemStruct) {
103    EnergySystemStruct.hoverEnergySystemStruct!.workScheduler =
104      // @ts-ignore
105      energySystemData.workScheduler === undefined ? '0' : energySystemData.workScheduler;
106    EnergySystemStruct.hoverEnergySystemStruct!.power =
107      // @ts-ignore
108      energySystemData.power === undefined ? '0' : energySystemData.power;
109    EnergySystemStruct.hoverEnergySystemStruct!.location =
110      // @ts-ignore
111      energySystemData.location === undefined ? '0' : energySystemData.location;
112  }
113  let spApplication = document.getElementsByTagName('sp-application')[0];
114  let isDark = spApplication.hasAttribute('dark');
115  drawLegend(req, isDark);
116  req.context.closePath();
117}
118
119export function drawLegend(
120  req: {
121    useCache: boolean;
122    context: CanvasRenderingContext2D;
123    type: string;
124  },
125  isDark?: boolean
126): void {
127  let textList = ['WORKSCHEDULER', 'POWER_RUNNINGLOCK', 'LOCATION'];
128  for (let index = 0; index < textList.length; index++) {
129    let text = req.context.measureText(textList[index]);
130    req.context.fillStyle = EnergySystemStruct.getColor(index);
131    let canvasEndX = req.context.canvas.clientWidth - EnergySystemStruct.OFFSET_WIDTH;
132    let textColor = isDark ? '#FFFFFF' : '#333';
133    if (textList[index] === 'WORKSCHEDULER') {
134      req.context.fillRect(canvasEndX - EnergySystemStruct.itemNumber * 120, 12, 8, 8);
135      req.context.globalAlpha = 1;
136      req.context.textBaseline = 'middle';
137      req.context.fillStyle = textColor;
138      req.context.fillText(textList[index], canvasEndX - EnergySystemStruct.itemNumber * 120 + 10, 18);
139      EnergySystemStruct.currentTextWidth = canvasEndX - EnergySystemStruct.itemNumber * 120 + 40 + text.width;
140    } else {
141      req.context.fillRect(EnergySystemStruct.currentTextWidth, 12, 8, 8);
142      req.context.globalAlpha = 1;
143      req.context.fillStyle = textColor;
144      req.context.textBaseline = 'middle';
145      req.context.fillText(textList[index], EnergySystemStruct.currentTextWidth + 12, 18);
146      EnergySystemStruct.currentTextWidth = EnergySystemStruct.currentTextWidth + 40 + text.width;
147    }
148  }
149  req.context.fillStyle = '#333';
150}
151
152export function systemData(
153  data: Array<EnergySystemStruct>,
154  startNS: number,
155  endNS: number,
156  totalNS: number,
157  frame: Rect
158): void {
159  for (let index = 0; index < data.length; index++) {
160    let systemItem = data[index];
161    if (index === data.length - 1) {
162      systemItem.dur = (endNS || 0) - (systemItem.startNs || 0);
163    } else {
164      systemItem.dur = (data[index + 1].startNs! || 0) - (systemItem.startNs! || 0);
165    }
166    if (systemItem.count === 0) {
167      systemItem.dur = 0;
168    }
169    if (
170      (systemItem.startNs || 0) + (systemItem.dur || 0) > (startNS || 0) &&
171      (systemItem.startNs || 0) < (endNS || 0)
172    ) {
173      EnergySystemStruct.setSystemFrame(systemItem, 10, startNS || 0, endNS || 0, totalNS || 0, frame);
174    }
175  }
176}
177
178export function system(
179  systemList: Array<EnergySystemStruct>,
180  res: Array<EnergySystemStruct>,
181  startNS: number,
182  endNS: number,
183  totalNS: number,
184  frame: Rect,
185  use: boolean
186): void {
187  if (use && res.length > 0) {
188    let lockData: EnergySystemStruct[] = [];
189    let locationData: EnergySystemStruct[] = [];
190    let workData: EnergySystemStruct[] = [];
191    res.forEach((item) => {
192      if (item.dataType === 1) {
193        lockData.push(item);
194      } else if (item.dataType === 2) {
195        locationData.push(item);
196      } else {
197        workData.push(item);
198      }
199    });
200    if (lockData.length > 0) {
201      systemData(lockData, startNS, endNS, totalNS, frame);
202    }
203    if (locationData.length > 0) {
204      systemData(locationData, startNS, endNS, totalNS, frame);
205    }
206    if (workData.length > 0) {
207      systemData(workData, startNS, endNS, totalNS, frame);
208    }
209    return;
210  }
211  res.length = 0;
212  setEnergySystemFilter(systemList, res, startNS, endNS, totalNS, frame);
213}
214function setEnergySystemFilter(
215  systemList: Array<unknown>,
216  res: Array<unknown>,
217  startNS: number,
218  endNS: number,
219  totalNS: number,
220  frame: Rect
221): void {
222  if (systemList) {
223    for (let i = 0; i < 3; i++) {
224      let arr = systemList[i];
225      if (arr) {
226        //@ts-ignore
227        for (let index = 0; index < arr.length; index++) {
228          //@ts-ignore
229          let item = arr[index];
230          //@ts-ignore
231          if (index === arr.length - 1) {
232            item.dur = endNS - (item.startNs || 0);
233          } else {
234            //@ts-ignore
235            item.dur = (arr[index + 1].startNs || 0) - (item.startNs || 0);
236          }
237          if (item.count === 0) {
238            item.dur = 0;
239          }
240          if ((item.startNs || 0) + (item.dur || 0) > startNS && (item.startNs || 0) < endNS) {
241            EnergySystemStruct.setSystemFrame(item, 10, startNS, endNS, totalNS, frame);
242            res.push(item);
243          }
244        }
245      }
246    }
247  }
248}
249
250export class EnergySystemStruct extends BaseStruct {
251  static hoverEnergySystemStruct: EnergySystemStruct | undefined;
252  static selectEnergySystemStruct: EnergySystemStruct | undefined;
253  static itemNumber: number = 3;
254  static currentTextWidth: number = 0;
255  static OFFSET_WIDTH: number = 266;
256  type: number | undefined;
257  startNs: number | undefined;
258  dur: number | undefined;
259  count: number | undefined;
260  token: number | undefined;
261  workScheduler: string | undefined;
262  power: string | undefined;
263  location: string | undefined;
264  id: number | undefined;
265  eventName: string | undefined;
266  eventValue: string | undefined;
267  appKey: string | undefined;
268  dataType: number | undefined;
269
270  static draw(energySystemContext: CanvasRenderingContext2D, data: EnergySystemStruct): void {
271    if (data.frame) {
272      let width = data.frame.width || 0;
273      energySystemContext.globalAlpha = 1.0;
274      energySystemContext.lineWidth = 1;
275      energySystemContext.fillStyle = this.getColor(data.type!);
276      energySystemContext.strokeStyle = this.getColor(data.type!);
277      energySystemContext.fillRect(data.frame.x, data.frame.y + 4, width, data.frame.height);
278    }
279    energySystemContext.globalAlpha = 1.0;
280    energySystemContext.lineWidth = 1;
281  }
282
283  static setSystemFrame(
284    systemNode: EnergySystemStruct,
285    padding: number,
286    startNS: number,
287    endNS: number,
288    totalNS: number,
289    frame: Rect
290  ): void {
291    let systemStartPointX: number;
292    let systemEndPointX: number;
293    if ((systemNode.startNs || 0) < startNS) {
294      systemStartPointX = 0;
295    } else {
296      systemStartPointX = ns2x(systemNode.startNs || 0, startNS, endNS, totalNS, frame);
297    }
298    if ((systemNode.startNs || 0) + (systemNode.dur || 0) > endNS) {
299      systemEndPointX = frame.width;
300    } else {
301      systemEndPointX = ns2x((systemNode.startNs || 0) + (systemNode.dur || 0), startNS, endNS, totalNS, frame);
302    }
303    let frameWidth: number = systemEndPointX - systemStartPointX <= 1 ? 1 : systemEndPointX - systemStartPointX;
304    if (!systemNode.frame) {
305      systemNode.frame = new Rect(0, 0, 0, 0);
306    }
307    systemNode.frame.x = Math.floor(systemStartPointX);
308    if (systemNode.type === 0) {
309      systemNode.frame.y = frame.y + padding * 2.5;
310    } else if (systemNode.type === 1) {
311      systemNode.frame.y = frame.y + padding * 4.5;
312    } else if (systemNode.type === 2) {
313      systemNode.frame.y = frame.y + padding * 6.5;
314    }
315    systemNode.frame.width = Math.ceil(frameWidth);
316    systemNode.frame.height = Math.floor(padding);
317  }
318
319  static getColor(textItem: number): string {
320    switch (textItem) {
321      case 0:
322        return '#E64566';
323      case 1:
324        return '#FFC880';
325      default:
326        return '#564AF7';
327    }
328  }
329}
330