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 21export class XpowerWifiRender extends Render { 22 renderMainThread( 23 xpowerWifiReq: { 24 context: CanvasRenderingContext2D; 25 useCache: boolean; 26 name: string; 27 }, 28 row: TraceRow<XpowerWifiStruct> 29 ): void { 30 // offsetW控制图例的横向偏移量 确保图例不超过画布边界 因收藏和非收藏时泳道的宽度不一致 offsetW根据情况调整 31 let offsetW: number = row.collect ? 40 : 260; 32 let checkedType = row.rowSettingCheckedBoxList; 33 let xpowerWifiList = row.dataListCache.map((item) => ({ ...item })); 34 xpowerWifiList.forEach((item) => { 35 if (!checkedType![0]) { 36 item.tx = 0; 37 } 38 if (!checkedType![1]) { 39 item.rx = 0; 40 } 41 }); 42 setDataFrameAndHoverHtml(xpowerWifiList, row, xpowerWifiReq.name); 43 drawLoadingFrame(xpowerWifiReq.context, xpowerWifiList, row); 44 setMaxInfo(xpowerWifiReq.context, xpowerWifiList, xpowerWifiReq.name); 45 xpowerWifiReq.context.beginPath(); 46 let find = false; 47 for (let re of xpowerWifiList) { 48 XpowerWifiStruct.draw(xpowerWifiReq, re, row, isFrameContainPoint(re.frame!, row.hoverX, row.hoverY)); 49 if (row.isHover && re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) { 50 xpowerWifiReq.name === 'WIFIPackets' 51 ? (XpowerWifiStruct.hoverPacketsStruct = re) 52 : (XpowerWifiStruct.hoverBytesStruct = re); 53 find = true; 54 } 55 } 56 if (!find) { 57 xpowerWifiReq.name === 'WIFIPackets' 58 ? (XpowerWifiStruct.hoverPacketsStruct = undefined) 59 : (XpowerWifiStruct.hoverBytesStruct = undefined); 60 } 61 xpowerWifiReq.context.closePath(); 62 let spApplication = document.getElementsByTagName('sp-application')[0]; 63 let isDark = spApplication.hasAttribute('dark'); 64 drawLegend(xpowerWifiReq, checkedType!, offsetW, isDark); 65 } 66} 67 68function setDataFrameAndHoverHtml(filter: XpowerWifiStruct[], row: TraceRow<XpowerWifiStruct>, name: string): void { 69 filter.forEach((item) => { 70 XpowerWifiStruct.setXPowerStatisticFrame( 71 item, 72 5, 73 TraceRow.range?.startNS ?? 0, 74 TraceRow.range?.endNS ?? 0, 75 TraceRow.range?.totalNS ?? 0, 76 row.frame 77 ); 78 if ((name === 'WIFIPackets' && !item.hoverHtmlPackets) || (name === 'WIFIBytes' && !item.hoverHtmlBytes)) { 79 XpowerWifiStruct.setHoverHtml(item, name); 80 } 81 }); 82} 83 84export function drawLegend( 85 req: { context: CanvasRenderingContext2D; useCache: boolean; name: string }, 86 checkedType: boolean[], 87 offsetW: number, 88 isDark?: boolean 89): void { 90 let textList: string[] = []; 91 checkedType[0] && textList.push('tx'); 92 checkedType[1] && textList.push('rx'); 93 for (let index = 0; index < textList.length; index++) { 94 let text = req.context.measureText(textList[index]); 95 req.context.fillStyle = textList[index] === 'tx' ? ColorUtils.colorForTid(index) : ColorUtils.colorForTid(10); 96 req.context.globalAlpha = 1; 97 let canvasEndX = req.context.canvas.clientWidth - offsetW; 98 let textColor = isDark ? '#FFFFFF' : '#333'; 99 if (index === 0) { 100 req!.context.fillRect(canvasEndX - textList.length * 60, 12, 8, 8); 101 req.context.globalAlpha = 0.8; 102 req.context.fillStyle = textColor; 103 req.context.textBaseline = 'middle'; 104 req.context.fillText(textList[index], canvasEndX - textList.length * 60 + 10, 18); 105 XpowerWifiStruct.currentTextWidth = canvasEndX - textList.length * 60 + 40 + text.width; 106 } else { 107 req!.context.fillRect(XpowerWifiStruct.currentTextWidth, 12, 8, 8); 108 req.context.globalAlpha = 0.8; 109 req.context.fillStyle = textColor; 110 req.context.textBaseline = 'middle'; 111 req!.context.fillText(textList[index], XpowerWifiStruct.currentTextWidth + 12, 18); 112 XpowerWifiStruct.currentTextWidth = XpowerWifiStruct.currentTextWidth + 40 + text.width; 113 } 114 } 115 req.context.fillStyle = '#333'; 116} 117 118function setMaxInfo(context: CanvasRenderingContext2D, dataList: XpowerWifiStruct[], name: string): void { 119 let maxNumber = 0; 120 dataList.forEach((item) => { 121 item.total = item.rx + item.tx; 122 if (maxNumber < item.total) { 123 maxNumber = item.total; 124 } 125 }); 126 XpowerWifiStruct.max = maxNumber; 127 let s = name === 'WIFIBytes' ? XpowerWifiStruct.max + ' B' : XpowerWifiStruct.max.toString(); 128 let textMetrics = context.measureText(s); 129 context.globalAlpha = 0.8; 130 context.fillStyle = '#f0f0f0'; 131 context.fillRect(0, 5, textMetrics.width + 8, 18); 132 context.globalAlpha = 1; 133 context.fillStyle = '#333'; 134 context.textBaseline = 'middle'; 135 context.fillText(s, 4, 5 + 9); 136} 137 138export function XpowerWifiBytesStructOnClick( 139 clickRowType: string, 140 sp: SpSystemTrace, 141 entry?: XpowerWifiStruct 142): Promise<unknown> { 143 return new Promise((resolve, reject) => { 144 if (clickRowType === TraceRow.ROW_TYPE_XPOWER_WIFI_BYTES && (XpowerWifiStruct.hoverBytesStruct || entry)) { 145 XpowerWifiStruct.selectBytesXpowerStruct = entry || XpowerWifiStruct.hoverBytesStruct; 146 sp.traceSheetEL?.displayXpowerBytesWifiData(XpowerWifiStruct.selectBytesXpowerStruct!); 147 sp.timerShaftEL?.modifyFlagList(undefined); 148 reject(new Error()); 149 } else { 150 resolve(null); 151 } 152 }); 153} 154 155export function XpowerWifiPacketsStructOnClick( 156 clickRowType: string, 157 sp: SpSystemTrace, 158 entry?: XpowerWifiStruct 159): Promise<unknown> { 160 return new Promise((resolve, reject) => { 161 if (clickRowType === TraceRow.ROW_TYPE_XPOWER_WIFI_PACKETS && (XpowerWifiStruct.hoverPacketsStruct || entry)) { 162 XpowerWifiStruct.selectPacketsXpowerStruct = entry || XpowerWifiStruct.hoverPacketsStruct; 163 sp.traceSheetEL?.displayXpowerWifiPacketsData(XpowerWifiStruct.selectPacketsXpowerStruct!); 164 sp.timerShaftEL?.modifyFlagList(undefined); 165 reject(new Error()); 166 } else { 167 resolve(null); 168 } 169 }); 170} 171 172export class XpowerWifiStruct extends BaseStruct { 173 static rowHeight: number = 100; 174 static currentTextWidth: number = 0; 175 startTime: number = 0; 176 rx: number = 0; 177 tx: number = 0; 178 static hoverPacketsStruct: XpowerWifiStruct | undefined; 179 static hoverBytesStruct: XpowerWifiStruct | undefined; 180 static selectPacketsXpowerStruct: XpowerWifiStruct | undefined; 181 static selectBytesXpowerStruct: XpowerWifiStruct | undefined; 182 183 hoverHtmlPackets: string = ''; 184 hoverHtmlBytes: string = ''; 185 total: number = 0; 186 static max: number = 0; 187 188 static draw( 189 req: { context: CanvasRenderingContext2D; useCache: boolean; name: string }, 190 data: XpowerWifiStruct, 191 row: TraceRow<XpowerWifiStruct>, 192 ishover: boolean 193 ): void { 194 if (data.frame) { 195 req!.context.globalAlpha = 0.8; 196 req!.context.lineWidth = 1; 197 this.currentTextWidth = 0; 198 let txHeight = this.drawHistogram(req, data, -1, data.tx!, 'tx', row.frame); 199 let rxHeight = this.drawHistogram(req, data, txHeight, data.rx!, 'rx', row.frame); 200 let startNS = TraceRow.range!.startNS; 201 let endNS = TraceRow.range!.endNS; 202 let totalNS = TraceRow.range!.totalNS; 203 let hoverTime = 204 req.name === 'WIFIPackets' 205 ? XpowerWifiStruct.hoverPacketsStruct?.startTime 206 : XpowerWifiStruct.hoverBytesStruct?.startTime; 207 let selectTime = 208 req.name === 'WIFIPackets' 209 ? XpowerWifiStruct.selectPacketsXpowerStruct?.startTime 210 : XpowerWifiStruct.selectBytesXpowerStruct?.startTime; 211 if ((data.startTime === hoverTime && ishover) || data.startTime === selectTime) { 212 let endPointX = ns2x((data.startTime || 0) + 3000000000, startNS, endNS, totalNS, row.frame); 213 let startPointX = ns2x(data.startTime || 0, startNS, endNS, totalNS, row.frame); 214 let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX; 215 req.context.globalAlpha = 0.8; 216 req!.context.lineWidth = 2; 217 req.context.strokeStyle = '#9899a0'; 218 req!.context.strokeRect(startPointX, rxHeight, frameWidth, data.frame.height); 219 } 220 } 221 req!.context.globalAlpha = 0.8; 222 req!.context.lineWidth = 1; 223 } 224 225 static drawHistogram( 226 req: { context: CanvasRenderingContext2D; useCache: boolean }, 227 data: XpowerWifiStruct, 228 height: number, 229 itemValue: number, 230 type: string, 231 rowFrame: Rect 232 ): number { 233 let endPointX = ns2x( 234 data.startTime + 3000000000 || 0, 235 TraceRow.range!.startNS, 236 TraceRow.range!.endNS, 237 TraceRow.range!.totalNS, 238 rowFrame 239 ); 240 let startPointX = ns2x( 241 data.startTime || 0, 242 TraceRow.range!.startNS, 243 TraceRow.range!.endNS, 244 TraceRow.range!.totalNS, 245 rowFrame 246 ); 247 let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX; 248 let histogramColor = type === 'tx' ? ColorUtils.colorForTid(0) : ColorUtils.colorForTid(10); 249 req!.context.fillStyle = histogramColor; 250 let dataHeight: number = Math.floor(((itemValue || 0) * (this.rowHeight - 40)) / XpowerWifiStruct.max); 251 if (itemValue !== 0 && dataHeight < 10) { 252 dataHeight = 10; 253 } 254 let drawStartY = 0; 255 if (height === -1) { 256 drawStartY = data.frame!.y + this.rowHeight - dataHeight; 257 req!.context.fillRect(startPointX, drawStartY, frameWidth, dataHeight); 258 data.frame!.y = drawStartY; 259 data.frame!.height += dataHeight; 260 return drawStartY; 261 } else { 262 drawStartY = height - dataHeight; 263 req!.context.fillRect(startPointX, drawStartY, frameWidth, dataHeight); 264 if (type === 'rx') { 265 data.frame!.y = drawStartY; 266 data.frame!.height += dataHeight; 267 return drawStartY; 268 } 269 data.frame!.y = drawStartY; 270 data.frame!.height += dataHeight; 271 return dataHeight; 272 } 273 } 274 275 static setHoverHtml(node: XpowerWifiStruct, name: string): void { 276 let hoverHtml = ''; 277 if (name === 'WIFIPackets') { 278 let hoverTx = 279 node.tx !== 0 280 ? `<div style="text-align: left">tx_packets: </div> 281 <div style="text-align: left">${node.tx}</div>` 282 : ''; 283 let hoverRx = 284 node.rx !== 0 285 ? `<div style="text-align: left">rx_packets: </div> 286 <div style="text-align: left">${node.rx}</div>` 287 : ''; 288 hoverHtml = `<div style="display: grid; width:auto; grid-template-columns: 1fr 1fr;"> 289 ${hoverTx} 290 ${hoverRx} 291 </div>`; 292 node.hoverHtmlPackets = hoverHtml; 293 } else { 294 let hoverTx = 295 node.tx !== 0 296 ? `<div style="text-align: left">tx_bytes: </div> 297 <div style="text-align: left">${node.tx + ' B'}</div>` 298 : ''; 299 let hoverRx = 300 node.rx !== 0 301 ? `<div style="text-align: left">rx_bytes: </div> 302 <div style="text-align: left">${node.rx + ' B'}</div>` 303 : ''; 304 hoverHtml = `<div style="display: grid; width: auto; grid-template-columns: 1fr 1fr;"> 305 ${hoverTx} 306 ${hoverRx} 307 </div>`; 308 node.hoverHtmlBytes = hoverHtml; 309 } 310 } 311 312 static setXPowerStatisticFrame( 313 node: XpowerWifiStruct, 314 padding: number, 315 startNS: number, 316 endNS: number, 317 totalNS: number, 318 frame: Rect 319 ): void { 320 let startPointX: number; 321 let endPointX: number; 322 if ((node.startTime || 0) < startNS) { 323 startPointX = 0; 324 } else { 325 startPointX = ns2x(node.startTime, startNS, endNS, totalNS, frame); 326 } 327 if (node.startTime + 3000000000 > endNS) { 328 endPointX = frame.width; 329 } else { 330 endPointX = ns2x(node.startTime + 3000000000, startNS, endNS, totalNS, frame); 331 } 332 let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX; 333 if (!node.frame) { 334 node.frame = new Rect(0, 0, 0, 0); 335 } 336 node.frame.x = Math.floor(startPointX); 337 node.frame.y = frame.y + padding; 338 node.frame.width = Math.ceil(frameWidth); 339 node.frame.height = Math.floor(frame.height - padding * 2); 340 } 341} 342