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 { 18 BaseStruct, 19 drawLoadingFrame, 20 isFrameContainPoint, 21 PerfRender, 22 Rect, 23 RequestMessage, 24} from './ProcedureWorkerCommon'; 25import { TraceRow } from '../../component/trace/base/TraceRow'; 26 27export class EnergyAnomalyRender extends PerfRender { 28 renderMainThread( 29 req: { 30 useCache: boolean; 31 context: CanvasRenderingContext2D; 32 type: string; 33 appName: string; 34 canvasWidth: number; 35 }, 36 row: TraceRow<EnergyAnomalyStruct> 37 ): void { 38 let list = row.dataList; 39 let filter = row.dataListCache; 40 anomaly( 41 list, 42 filter, 43 TraceRow.range!.startNS, 44 TraceRow.range!.endNS, 45 TraceRow.range!.totalNS, 46 row.frame, 47 req.appName, 48 req.useCache || !TraceRow.range!.refresh 49 ); 50 if (list.length > 0) { 51 filter.length = 0; 52 list.forEach((item) => { 53 filter.push(item); 54 }); 55 } 56 drawLoadingFrame(req.context, row.dataListCache, row); 57 req.context.beginPath(); 58 let find = false; 59 let spApplication = document.getElementsByTagName('sp-application')[0]; 60 let isDark = spApplication.hasAttribute('dark'); 61 drawLegend(req, isDark); 62 for (let re of filter) { 63 EnergyAnomalyStruct.draw(req.context, re); 64 if (row.isHover && re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) { 65 EnergyAnomalyStruct.hoverEnergyAnomalyStruct = re; 66 find = true; 67 } 68 } 69 if (!find && row.isHover) { 70 EnergyAnomalyStruct.hoverEnergyAnomalyStruct = undefined; 71 } 72 req.context.fillStyle = ColorUtils.FUNC_COLOR[0]; 73 req.context.strokeStyle = ColorUtils.FUNC_COLOR[0]; 74 req.context.closePath(); 75 } 76 77 render( 78 energyAnomalyRequest: RequestMessage, 79 list: Array<EnergyAnomalyStruct>, 80 filter: Array<EnergyAnomalyStruct>, 81 dataList2: Array<EnergyAnomalyStruct> 82 ): void {} 83} 84 85export function drawLegend( 86 req: { 87 useCache: boolean; 88 context: CanvasRenderingContext2D; 89 type: string; 90 appName: string; 91 canvasWidth: number; 92 }, 93 isDark?: boolean 94): void { 95 req.context.font = '12px Arial'; 96 let text = req.context.measureText('System Abnormality'); 97 req.context.fillStyle = '#E64566'; 98 req.context.strokeStyle = '#E64566'; 99 let textColor = isDark ? '#FFFFFF' : '#333'; 100 let canvasEndX = req.context.canvas.clientWidth - EnergyAnomalyStruct.OFFSET_WIDTH; 101 let rectPadding: number; 102 let textPadding: number; 103 let textMargin: number; 104 let currentTextWidth: number; 105 let lastTextMargin: number; 106 rectPadding = 280; 107 textPadding = 270; 108 textMargin = 250; 109 currentTextWidth = canvasEndX - textMargin + text.width; 110 lastTextMargin = currentTextWidth + 12; 111 req!.context.fillRect(canvasEndX - rectPadding, 12, 8, 8); 112 req.context.globalAlpha = 1; 113 req.context.fillStyle = textColor; 114 req.context.textBaseline = 'middle'; 115 req.context.fillText('System Abnormality', canvasEndX - textPadding, 18); 116 req.context.fillStyle = '#FFC880'; 117 req.context.strokeStyle = '#FFC880'; 118 req.context.fillRect(currentTextWidth, 12, 8, 8); 119 req.context.globalAlpha = 1; 120 req.context.fillStyle = textColor; 121 req.context.textBaseline = 'middle'; 122 req.context.fillText('Application Abnormality', lastTextMargin, 18); 123 req.context.fillStyle = '#333'; 124} 125 126export function anomaly( 127 arr: Array<EnergyAnomalyStruct>, 128 res: Array<EnergyAnomalyStruct>, 129 startNS: number, 130 endNS: number, 131 totalNS: number, 132 frame: Rect, 133 appName: string, 134 use: boolean 135): void { 136 arr.length = 0; 137 if (use && res.length > 0) { 138 let pns = (endNS - startNS) / frame.width; 139 let y = frame.y; 140 for (let i = 0; i < res.length; i++) { 141 let it = res[i]; 142 if ((it.startNS || 0) > startNS && (it.startNS || 0) < endNS) { 143 if (!it.frame) { 144 it.frame = new Rect(0, 0, 0, 0); 145 it.frame.y = y; 146 } 147 it.frame.height = 20 + radius * 2; 148 if (it.startNS! + 50000 > (startNS || 0) && (it.startNS || 0) < (endNS || 0)) { 149 EnergyAnomalyStruct.setAnomalyFrame(it, pns, startNS || 0, endNS || 0, frame); 150 if (it.appKey === 'APPNAME' && it.eventValue!.split(',').indexOf(appName!) >= 0) { 151 arr.push(it); 152 } 153 if (it.appKey !== 'APPNAME') { 154 arr.push(it); 155 } 156 } 157 } else { 158 it.frame = undefined; 159 } 160 } 161 return; 162 } 163} 164 165export class EnergyAnomalyStruct extends BaseStruct { 166 static hoverEnergyAnomalyStruct: EnergyAnomalyStruct | undefined; 167 static selectEnergyAnomalyStruct: EnergyAnomalyStruct | undefined; 168 static SYSTEM_EXCEPTION = new Set([ 169 'ANOMALY_SCREEN_OFF_ENERGY', 170 'ANOMALY_ALARM_WAKEUP', 171 'ANOMALY_KERNEL_WAKELOCK', 172 'ANOMALY_CPU_HIGH_FREQUENCY', 173 'ANOMALY_WAKEUP', 174 ]); 175 static OFFSET_WIDTH: number = 266; 176 id: number | undefined; 177 type: number | undefined; 178 startNS: number | undefined; 179 height: number | undefined; 180 eventName: string | undefined; 181 appKey: string | undefined; 182 eventValue: string | undefined; 183 184 static draw(ctx: CanvasRenderingContext2D, data: EnergyAnomalyStruct): void { 185 if (data.frame) { 186 EnergyAnomalyStruct.drawRoundRectPath(ctx, data.frame.x - 7, 20 - 7, radius, data); 187 } 188 } 189 190 static drawRoundRectPath( 191 ctx: CanvasRenderingContext2D, 192 x: number, 193 y: number, 194 radius: number, 195 data: EnergyAnomalyStruct 196 ): void { 197 ctx.beginPath(); 198 ctx.arc(x + 7, y + 22, radius, 0, Math.PI * 2); 199 ctx.closePath(); 200 let color = ''; 201 if (EnergyAnomalyStruct.SYSTEM_EXCEPTION.has(<string>data.eventName)) { 202 color = '#E64566'; 203 } else { 204 color = '#FFC880'; 205 } 206 // 填充背景颜色 207 ctx.fillStyle = color; 208 ctx.fill(); 209 ctx.stroke(); 210 // 填充文字颜色 211 ctx.font = '12px Arial'; 212 ctx.fillStyle = ColorUtils.GREY_COLOR; 213 ctx.textAlign = 'center'; 214 ctx.fillText('E', x + 7, y + 23); 215 } 216 217 static setAnomalyFrame(node: EnergyAnomalyStruct, pns: number, startNS: number, endNS: number, frame: Rect): void { 218 if (!node.frame) { 219 node.frame = new Rect(0, 0, 0, 0); 220 } 221 if ((node.startNS || 0) < startNS) { 222 node.frame.x = 0; 223 } else { 224 node.frame.x = Math.floor(((node.startNS || 0) - startNS) / pns); 225 } 226 if ((node.startNS || 0) > endNS) { 227 node.frame.width = frame.width - node.frame.x; 228 } else { 229 node.frame.width = Math.ceil(((node.startNS || 0) - startNS) / pns - node.frame.x); 230 } 231 if (node.frame.width < 1) { 232 node.frame.width = 1; 233 } 234 } 235} 236let radius = 12; 237