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}: </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