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 { ColorUtils } from '../../component/trace/base/ColorUtils';
17import { TraceRow } from '../../component/trace/base/TraceRow';
18import {
19  BaseStruct,
20  isFrameContainPoint,
21  ns2x,
22  Render,
23  RequestMessage,
24  drawString,
25  drawLoadingFrame,
26  Rect,
27} from './ProcedureWorkerCommon';
28import { SpSystemTrace } from '../../component/SpSystemTrace';
29
30export class SoRender extends Render {
31  renderMainThread(
32    req: {
33      useCache: boolean;
34      context: CanvasRenderingContext2D;
35      type: string;
36    },
37    row: TraceRow<SoStruct>
38  ): void {
39    let soList = row.dataList;
40    let soFilter = row.dataListCache;
41    soDataFilter(
42      soList,
43      soFilter,
44      TraceRow.range!.startNS,
45      TraceRow.range!.endNS,
46      TraceRow.range!.totalNS,
47      row.frame,
48      req.useCache || !TraceRow.range!.refresh
49    );
50
51    drawLoadingFrame(req.context, row.dataListCache, row);
52    req.context.beginPath();
53    let soFind = false;
54    for (let so of soFilter) {
55      SoStruct.draw(req.context, so);
56      if (row.isHover) {
57        if (so.dur === 0 || so.dur === null || so.dur === undefined) {
58          if (
59            so.frame &&
60            row.hoverX >= so.frame.x - 5 &&
61            row.hoverX <= so.frame.x + 5 &&
62            row.hoverY >= so.frame.y &&
63            row.hoverY <= so.frame.y + so.frame.height
64          ) {
65            SoStruct.hoverSoStruct = so;
66            soFind = true;
67          }
68        } else {
69          if (so.frame && isFrameContainPoint(so.frame, row.hoverX, row.hoverY)) {
70            SoStruct.hoverSoStruct = so;
71            soFind = true;
72          }
73        }
74      }
75    }
76    if (!soFind && row.isHover) {
77      SoStruct.hoverSoStruct = undefined;
78    }
79    req.context.closePath();
80  }
81
82  render(req: RequestMessage, list: Array<unknown>, filter: Array<unknown>): void {}
83}
84
85export function soDataFilter(
86  soList: Array<SoStruct>,
87  soFilter: Array<SoStruct>,
88  startNS: number,
89  endNS: number,
90  totalNS: number,
91  frame: Rect,
92  use: boolean
93): void {
94  if (use && soFilter.length > 0) {
95    for (let i = 0, len = soFilter.length; i < len; i++) {
96      if ((soFilter[i].startTs || 0) + (soFilter[i].dur || 0) >= startNS && (soFilter[i].startTs || 0) <= endNS) {
97        SoStruct.setSoFrame(soFilter[i], 0, startNS, endNS, totalNS, frame);
98      } else {
99        soFilter[i].frame = undefined;
100      }
101    }
102    return;
103  }
104  soFilter.length = 0;
105  if (soList) {
106    let groups = soList
107      .filter((it) => (it.startTs ?? 0) + (it.dur ?? 0) >= startNS && (it.startTs ?? 0) <= endNS)
108      .map((it) => {
109        SoStruct.setSoFrame(it, 0, startNS, endNS, totalNS, frame);
110        return it;
111      })
112      .reduce((pre: unknown, current, index, arr) => {
113        if (current.frame) {
114          //@ts-ignore
115          (pre[`${current.frame.x}-${current.depth}`] = pre[`${current.frame.x}-${current.depth}`] || []).push(current);
116        }
117        return pre;
118      }, {});
119    //@ts-ignore
120    Reflect.ownKeys(groups).map((kv) => {
121      //@ts-ignore
122      let arr = groups[kv].sort((a: unknown, b: unknown) => b.dur - a.dur);
123      soFilter.push(arr[0]);
124    });
125  }
126}
127export function SoStructOnClick(
128  clickRowType: string,
129  sp: SpSystemTrace,
130  scrollToFuncHandler: Function,
131  entry?: SoStruct,
132): Promise<unknown> {
133  return new Promise((resolve, reject) => {
134    if (clickRowType === TraceRow.ROW_TYPE_STATIC_INIT && (SoStruct.hoverSoStruct || entry)) {
135      SoStruct.selectSoStruct = entry || SoStruct.hoverSoStruct;
136      sp.traceSheetEL?.displayStaticInitData(SoStruct.selectSoStruct!, scrollToFuncHandler);
137      sp.timerShaftEL?.modifyFlagList(undefined);
138      reject(new Error());
139    } else {
140      resolve(null);
141    }
142  });
143}
144export class SoStruct extends BaseStruct {
145  static hoverSoStruct: SoStruct | undefined;
146  static selectSoStruct: SoStruct | undefined;
147  textMetricsWidth: number | undefined;
148  depth: number | undefined;
149  dur: number | undefined;
150  soName: string | undefined;
151  process: string | undefined;
152  startTs: number | undefined;
153  tid: number | undefined;
154  pid: number | undefined;
155  itid: number | undefined;
156  id: number | undefined;
157
158  static setSoFrame(
159    soNode: SoStruct,
160    padding: number,
161    startNS: number,
162    endNS: number,
163    totalNS: number,
164    frame: Rect
165  ): void {
166    let x1: number;
167    let x2: number;
168    if ((soNode.startTs || 0) > startNS && (soNode.startTs || 0) < endNS) {
169      x1 = ns2x(soNode.startTs || 0, startNS, endNS, totalNS, frame);
170    } else {
171      x1 = 0;
172    }
173    if ((soNode.startTs || 0) + (soNode.dur || 0) > startNS && (soNode.startTs || 0) + (soNode.dur || 0) < endNS) {
174      x2 = ns2x((soNode.startTs || 0) + (soNode.dur || 0), startNS, endNS, totalNS, frame);
175    } else {
176      x2 = frame.width;
177    }
178    if (!soNode.frame) {
179      soNode.frame = new Rect(0, 0, 0, 0);
180    }
181    let getV: number = x2 - x1 < 1 ? 1 : x2 - x1;
182    soNode.frame.x = Math.floor(x1);
183    soNode.frame.y = soNode.depth! * 20;
184    soNode.frame.width = Math.ceil(getV);
185    soNode.frame.height = 20;
186  }
187
188  static draw(ctx: CanvasRenderingContext2D, data: SoStruct): void {
189    if (data.frame) {
190      if (data.dur === undefined || data.dur === null) {
191      } else {
192        ctx.globalAlpha = 1;
193        ctx.fillStyle = ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.soName || '', 0, ColorUtils.FUNC_COLOR.length)];
194        let textColor = ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.soName || '', 0, ColorUtils.FUNC_COLOR.length)];
195        let miniHeight = 20;
196        if (SoStruct.hoverSoStruct && data.soName === SoStruct.hoverSoStruct.soName) {
197          ctx.globalAlpha = 0.7;
198        }
199        ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - padding * 2);
200        if (data.frame.width > 10) {
201          ctx.strokeStyle = '#fff';
202          ctx.lineWidth = 1;
203          ctx.strokeRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - padding * 2);
204          ctx.fillStyle = ColorUtils.funcTextColor(textColor);
205          drawString(ctx, `${data.soName || ''}`, 5, data.frame, data);
206        }
207        if (data === SoStruct.selectSoStruct) {
208          ctx.strokeStyle = '#000';
209          ctx.lineWidth = 2;
210          ctx.strokeRect(data.frame.x, data.frame.y + 1, data.frame.width, miniHeight - padding * 2 - 2);
211        }
212      }
213    }
214  }
215}
216
217const padding = 1;
218