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 drawFlagLine, drawLines, 19 drawLoading, 20 drawSelection, 21 ns2x, 22 Render, 23 RequestMessage 24} from "./ProcedureWorkerCommon.js"; 25 26export class EnergyPowerRender extends Render { 27 render(req: RequestMessage, list: Array<any>, filter: Array<any>) { 28 if (req.lazyRefresh) { 29 power(list, filter, req.startNS, req.endNS, req.totalNS, req.frame, req.useCache 30 || !req.range.refresh, req.params.maxPowerName); 31 } else { 32 if (!req.useCache) { 33 power(list, filter, req.startNS, req.endNS, req.totalNS, req.frame, false, req.params.maxPowerName); 34 } 35 } 36 if (req.canvas) { 37 req.context.clearRect(0, 0, req.canvas.width, EnergyPowerStruct.rowHeight); 38 let arr = filter; 39 if (arr.length > 0 && !req.range.refresh && !req.useCache && req.lazyRefresh) { 40 drawLoading(req.context, req.startNS, req.endNS, req.totalNS, req.frame, arr[0].startNS, 41 arr[arr.length - 1].startNS + arr[arr.length - 1].dur) 42 } 43 drawLines(req.context, req.xs, req.frame.height, req.lineColor) 44 req.context.beginPath(); 45 EnergyPowerStruct.hoverEnergyPowerStruct = undefined; 46 if (req.isHover) { 47 for (let re of filter) { 48 if (re.frame && req.hoverX >= re.frame.x && req.hoverX <= re.frame.x + re.frame.width && req.hoverY >= re.frame.y && req.hoverY <= re.frame.y + re.frame.height) { 49 EnergyPowerStruct.hoverEnergyPowerStruct = re; 50 break; 51 } 52 } 53 } 54 EnergyPowerStruct.selectEnergyPowerStruct = req.params.selectEnergyPowerStruct; 55 for (let index = 0; index < filter.length; index++) { 56 EnergyPowerStruct.draw(req, index, filter[index]) 57 } 58 req.context.stroke(); 59 drawSelection(req.context, req.params); 60 req.context.closePath(); 61 if (EnergyPowerStruct.maxPower != 0) { 62 let s = EnergyPowerStruct.maxPower.toString() 63 let textMetrics = req.context.measureText(s); 64 req.context.globalAlpha = 1.0 65 req.context.fillStyle = "#f0f0f0" 66 req.context.fillRect(0, 5, textMetrics.width + 8, 18) 67 req.context.globalAlpha = 1 68 req.context.fillStyle = "#333" 69 req.context.textBaseline = "middle" 70 req.context.fillText(s, 4, 5 + 9) 71 } 72 drawLegend(req) 73 drawFlagLine(req.context, req.flagMoveInfo, req.flagSelectedInfo, req.startNS, req.endNS, req.totalNS, 74 req.frame, req.slicesTime); 75 } 76 // @ts-ignore 77 self.postMessage({ 78 id: req.id, 79 type: req.type, 80 results: req.canvas ? undefined : filter, 81 hover: EnergyPowerStruct.hoverEnergyPowerStruct 82 }); 83 } 84} 85 86export function drawLegend(req: RequestMessage){ 87 let textList = ["CPU", "LOCATION", "GPU", "DISPLAY", "CAMERA", "BLUETOOTH", "FLASHLIGHT", "AUDIO", "WIFISCAN"] 88 for (let index = 0; index < textList.length; index++) { 89 let text = req.context.measureText(textList[index]); 90 req.context.fillStyle = EnergyPowerStruct.getHistogramColor(textList[index]); 91 req.context.globalAlpha = 1 92 if(index == 0){ 93 req!.context.fillRect(req.canvas.width - (EnergyPowerStruct.powerItemNumber * 80), 12, 8, 8); 94 req.context.globalAlpha = 1 95 req.context.fillStyle = "#333" 96 req.context.textBaseline = "middle" 97 req.context.fillText(textList[index], req.canvas.width - (EnergyPowerStruct.powerItemNumber * 80) + 10, 18) 98 EnergyPowerStruct.currentTextWidth = req.canvas.width - (EnergyPowerStruct.powerItemNumber * 80) + 40 + text.width 99 } else { 100 req!.context.fillRect(EnergyPowerStruct.currentTextWidth, 12, 8, 8); 101 req.context.globalAlpha = 1 102 req.context.fillStyle = "#333" 103 req.context.textBaseline = "middle" 104 req!.context.fillText(textList[index], EnergyPowerStruct.currentTextWidth + 12, 18); 105 EnergyPowerStruct.currentTextWidth = EnergyPowerStruct.currentTextWidth + 40 + text.width 106 } 107 } 108} 109 110export function power(list: Array<any>, res: Array<any>, startNS: number, endNS: number, totalNS: number, 111 frame: any, use: boolean, appName: string) { 112 if (use && res.length > 0) { 113 for (let i = 0; i < res.length; i++) { 114 let item = res[i]; 115 let obj = item[appName] 116 if (obj != undefined) { 117 if ((obj.ts + 1000000000) > (startNS || 0) && (obj.ts || 0) < (endNS || 0)) { 118 EnergyPowerStruct.setPowerFrame(item, 5, startNS || 0, endNS || 0, 119 totalNS || 0, frame) 120 } else { 121 obj.frame = null; 122 } 123 } 124 } 125 return; 126 } 127 res.length = 0; 128 if (list) { 129 let firstList: Array<any> = [] 130 EnergyPowerStruct.maxPower = 0 131 for (let index = 0; index < list.length; index++) { 132 let item = list[index]; 133 let obj = item[appName] 134 if (obj != undefined) { 135 if ((obj.ts + 1000000000) > (startNS || 0) && (obj.ts || 0) < (endNS || 0)) { 136 firstList.push(obj) 137 } 138 } 139 } 140 141 let array = firstList.sort((a, b) => a.ts - b.ts); 142 for (let index = 0; index < array.length; index++) { 143 if (res.length == 0) { 144 res.push(array[index]) 145 } else { 146 let rightTime = array[index].ts + 500000000; 147 let leftTime = array[index].ts - 500000000; 148 let obj = res[res.length - 1]; 149 if (obj.ts >= leftTime && obj.ts <= rightTime) { 150 obj.cpu = obj.cpu == 0 ? array[index].cpu : obj.cpu 151 obj.location = obj.location == 0 ? array[index].location : obj.location 152 obj.gpu = obj.gpu == 0 ? array[index].gpu : obj.gpu 153 obj.display = obj.display == 0 ? array[index].display : obj.display 154 obj.camera = obj.camera == 0 ? array[index].camera : obj.camera 155 obj.bluetooth = obj.bluetooth == 0 ? array[index].bluetooth : obj.bluetooth 156 obj.flashlight = obj.flashlight == 0 ? array[index].flashlight : obj.flashlight 157 obj.audio = obj.audio ? array[index].audio : obj.audio 158 obj.wifiscan = obj.wifiscan == 0 ? array[index].wifiscan : obj.wifiscan 159 } else { 160 res.push(array[index]) 161 } 162 } 163 } 164 res.forEach(item => { 165 EnergyPowerStruct.setPowerFrame(item, 5, startNS || 0, endNS || 0, 166 totalNS || 0, frame) 167 let max = (item.cpu || 0) + (item.location || 0) + (item.gpu || 0) + (item.display || 0) + (item.camera 168 || 0) + (item.bluetooth || 0) + (item.flashlight || 0) + (item.audio || 0) + (item.wifiscan || 0) 169 if (max > EnergyPowerStruct.maxPower) { 170 EnergyPowerStruct.maxPower = max 171 } 172 }) 173 } 174} 175 176export class EnergyPowerStruct extends BaseStruct { 177 static maxPower: number = 0 178 static maxPowerName: string = "0" 179 static powerItemNumber: number = 9 180 static currentTextWidth: number = 0 181 static rowHeight: number = 200 182 static appName: string | undefined 183 static hoverEnergyPowerStruct: EnergyPowerStruct | undefined; 184 static selectEnergyPowerStruct: EnergyPowerStruct | undefined; 185 name: string | undefined 186 ts: number = 0 187 cpu: number = 0 188 location: number = 0 189 gpu: number = 0 190 display: number = 0 191 camera: number = 0 192 bluetooth: number = 0 193 flashlight: number = 0 194 audio: number = 0 195 wifiscan: number = 0 196 197 static draw(req: RequestMessage, index: number, data: EnergyPowerStruct) { 198 if (data.frame) { 199 let width = data.frame.width || 0; 200 req!.context.globalAlpha = 1.0; 201 req!.context.lineWidth = 1; 202 this.currentTextWidth = 0 203 let cpuHeight = this.drawHistogram(req, data, -1, data.cpu!, "CPU"); 204 let locationHeight = this.drawHistogram(req, data, cpuHeight, data.location!, "LOCATION"); 205 let gpuHeight = this.drawHistogram(req, data, cpuHeight - locationHeight, data.gpu!, 206 "GPU"); 207 let displayHeight = this.drawHistogram(req, data, 208 cpuHeight - locationHeight - gpuHeight, data.display!, "DISPLAY"); 209 let cameraHeight = this.drawHistogram(req, data, cpuHeight - locationHeight - gpuHeight - 210 displayHeight, data.camera!, "CAMERA"); 211 let bluetoothHeight = this.drawHistogram(req, data, cpuHeight - locationHeight - gpuHeight - 212 displayHeight - cameraHeight, data.bluetooth!, "BLUETOOTH"); 213 let flashlightHeight = this.drawHistogram(req, data, cpuHeight - locationHeight - gpuHeight - 214 displayHeight - cameraHeight - bluetoothHeight, data.flashlight!, "FLASHLIGHT"); 215 let audioHeight = this.drawHistogram(req, data, cpuHeight - locationHeight - gpuHeight - 216 displayHeight - cameraHeight - bluetoothHeight - flashlightHeight, data.audio!, 217 "AUDIO"); 218 let wifiHeight = this.drawHistogram(req, data, cpuHeight - locationHeight - gpuHeight - 219 displayHeight - cameraHeight - bluetoothHeight - flashlightHeight - audioHeight, data.wifiscan!, 220 "WIFISCAN"); 221 let maxPointY = this.drawPolyline(req, index, data) 222 if (data.ts === EnergyPowerStruct.hoverEnergyPowerStruct?.ts) { 223 req.context.globalAlpha = 1 224 req!.context.lineWidth = 2; 225 req.context.fillStyle = "#333" 226 req!.context.strokeRect(data.frame!.x, maxPointY, data.frame!.width, req.canvas.width - maxPointY); 227 } 228 } 229 req!.context.globalAlpha = 1.0; 230 req!.context.lineWidth = 1; 231 } 232 233 static drawHistogram(req: RequestMessage, data: EnergyPowerStruct, height: number, itemValue: number, 234 textItem: string): number { 235 let histogramColor = this.getHistogramColor(textItem); 236 req!.context.fillStyle = histogramColor; 237 req!.context.strokeStyle = histogramColor; 238 let dataHeight: number = Math.floor(((itemValue || 0) * (this.rowHeight - 40)) / 239 EnergyPowerStruct.maxPower); 240 let drawStartY = 0; 241 242 if (height == -1) { 243 drawStartY = data.frame!.y + this.rowHeight - dataHeight + 4; 244 req!.context.fillRect(data.frame!.x, drawStartY, data.frame!.width, dataHeight); 245 return drawStartY; 246 } else { 247 drawStartY = height - dataHeight; 248 req!.context.fillRect(data.frame!.x, drawStartY, data.frame!.width, dataHeight); 249 return dataHeight; 250 } 251 } 252 253 static drawPolyline(req: RequestMessage, index: number, data: EnergyPowerStruct) { 254 let pointX = ns2x(((data.ts || 0) + 500000000), req.startNS, req.endNS, req.totalNS, req.frame); 255 let maxHeight = (data.cpu || 0) + (data.location || 0) + (data.gpu || 0) + (data.display || 0) + (data.camera 256 || 0) + (data.bluetooth || 0) + (data.flashlight || 0) + (data.audio || 0) + (data.wifiscan || 0) 257 let drawHeight: number = Math.floor(((maxHeight || 0) * (this.rowHeight - 40)) / 258 EnergyPowerStruct.maxPower); 259 let drawY = data.frame!.y + this.rowHeight - drawHeight + 5 260 req!.context.fillStyle = "#ED6F21"; 261 req!.context.strokeStyle = "#ED6F21"; 262 263 if (index == 0) { 264 req.context.beginPath() 265 req.context.arc(pointX, drawY, 4, 0, 2 * Math.PI) 266 req.context.fill() 267 req.context.moveTo(pointX, drawY) 268 } else { 269 req.context.lineTo(pointX, drawY); 270 req.context.stroke(); 271 req.context.beginPath() 272 req.context.arc(pointX, drawY, 4, 0, 2 * Math.PI) 273 req.context.fill() 274 } 275 return drawY 276 } 277 278 static setPowerFrame(node: any, padding: number, startNS: number, endNS: number, totalNS: number, frame: any) { 279 let startPointX: number 280 let endPointX: number 281 if ((node.ts || 0) < startNS) { 282 startPointX = 0 283 } else { 284 startPointX = ns2x((node.ts || 0), startNS, endNS, totalNS, frame); 285 } 286 if ((node.ts + 1000000000) > endNS) { 287 endPointX = frame.width; 288 } else { 289 endPointX = ns2x((node.ts + 1000000000), startNS, endNS, totalNS, frame); 290 } 291 let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX; 292 if (!node.frame) { 293 node.frame = {}; 294 } 295 node.frame.x = Math.floor(startPointX); 296 node.frame.y = frame.y + padding; 297 node.frame.width = Math.ceil(frameWidth); 298 node.frame.height = Math.floor(frame.height - padding * 2); 299 } 300 301 static getHistogramColor(textItem: string): string { 302 switch (textItem) { 303 case 'CPU': 304 return "#92D6CC"; 305 case 'LOCATION': 306 return "#61CFBE" 307 case 'GPU': 308 return "#86C5E3" 309 case 'DISPLAY': 310 return "#46B1E3" 311 case 'CAMERA': 312 return "#C386F0" 313 case 'BLUETOOTH': 314 return "#8981F7" 315 case 'AUDIO': 316 return "#AC49F5" 317 case 'WIFISCAN': 318 return "#92C4BD" 319 default: 320 return "#564AF7" 321 } 322 } 323} 324