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 { SpSystemTrace } from '../SpSystemTrace';
17import { TraceRow } from '../trace/base/TraceRow';
18import { renders } from '../../database/ui-worker/ProcedureWorker';
19import { info } from '../../../log/Log';
20import { XpowerRender, XpowerStruct } from '../../database/ui-worker/ProcedureWorkerXpower';
21import { ColorUtils } from '../trace/base/ColorUtils';
22import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU';
23import { xpowerDataSender } from '../../database/data-trafic/XpowerDataSender';
24import { queryXpowerData, queryXpowerMeasureData } from '../../database/sql/Xpower.sql';
25import { BaseStruct } from '../../bean/BaseStruct';
26
27export class SpXpowerChart {
28    private readonly trace: SpSystemTrace;
29    private rowFolder!: TraceRow<BaseStruct>;
30    private systemFolder!: TraceRow<BaseStruct>;
31
32    constructor(trace: SpSystemTrace) {
33        this.trace = trace;
34    }
35
36    async init(traceId?: string): Promise<void> {
37        let XpowerMeasureData = await queryXpowerMeasureData(traceId);
38        if (XpowerMeasureData.length <= 0) {
39            return;
40        }
41        let xpowerList = await queryXpowerData(traceId);
42        if (xpowerList.length <= 0) {
43            return;
44        }
45        await this.initXpowerFolder(traceId);
46        await this.initSystemFolder(traceId);
47        await this.initSystemData(this.systemFolder, xpowerList, traceId);
48    }
49
50    initXpowerFolder = async (traceId?: string): Promise<void> => {
51        let xpowerFolder = TraceRow.skeleton(traceId);
52        xpowerFolder.rowId = 'Xpowers';
53        xpowerFolder.index = 0;
54        xpowerFolder.rowType = TraceRow.ROW_TYPE_XPOWER;
55        xpowerFolder.rowParentId = '';
56        xpowerFolder.style.height = '40px';
57        xpowerFolder.folder = true;
58        xpowerFolder.name = 'Xpower';
59        xpowerFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler;
60        xpowerFolder.selectChangeHandler = this.trace.selectChangeHandler;
61        xpowerFolder.supplier = (): Promise<BaseStruct[]> => new Promise<Array<BaseStruct>>((resolve) => resolve([]));
62        xpowerFolder.onThreadHandler = (useCache): void => {
63            xpowerFolder.canvasSave(this.trace.canvasPanelCtx!);
64            if (xpowerFolder.expansion) {
65                this.trace.canvasPanelCtx?.clearRect(0, 0, xpowerFolder.frame.width, xpowerFolder.frame.height);
66            } else {
67                (renders.empty as EmptyRender).renderMainThread(
68                    {
69                        context: this.trace.canvasPanelCtx,
70                        useCache: useCache,
71                        type: '',
72                    },
73                    xpowerFolder
74                );
75            }
76            xpowerFolder.canvasRestore(this.trace.canvasPanelCtx!, this.trace);
77        };
78        this.rowFolder = xpowerFolder;
79        this.trace.rowsEL?.appendChild(xpowerFolder);
80    }
81
82    initSystemFolder = async (traceId?: string): Promise<void> => {
83        let systemFolder = TraceRow.skeleton(traceId);
84        systemFolder.rowId = 'system';
85        systemFolder.rowParentId = 'Xpowers';
86        systemFolder.rowHidden = !this.rowFolder.expansion;
87        systemFolder.rowType = TraceRow.ROW_TYPE_XPOWER_SYSTEM;
88        systemFolder.folder = true;
89        systemFolder.name = 'System';
90        systemFolder.folderPaddingLeft = 20;
91        systemFolder.style.height = '40px';
92        systemFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler;
93        systemFolder.selectChangeHandler = this.trace.selectChangeHandler;
94        systemFolder.supplier = (): Promise<BaseStruct[]> => new Promise<Array<BaseStruct>>((resolve) => resolve([]));
95        systemFolder.onThreadHandler = (useCache): void => {
96            systemFolder.canvasSave(this.trace.canvasPanelCtx!);
97            if (systemFolder.expansion) {
98                this.trace.canvasPanelCtx?.clearRect(0, 0, systemFolder.frame.width, systemFolder.frame.height);
99            } else {
100                (renders.empty as EmptyRender).renderMainThread(
101                    {
102                        context: this.trace.canvasPanelCtx,
103                        useCache: useCache,
104                        type: '',
105                    },
106                    systemFolder
107                );
108            }
109            systemFolder.canvasRestore(this.trace.canvasPanelCtx!, this.trace);
110        };
111        this.systemFolder = systemFolder;
112        this.rowFolder?.addChildTraceRow(systemFolder);
113    }
114
115    private xpowerSupplierFrame(
116        traceRow: TraceRow<XpowerStruct>,
117        it: {
118            name: string;
119            num: number;
120            maxValue?: number;
121        },
122    ): void {
123        traceRow.supplierFrame = (): Promise<XpowerStruct[]> => {
124            let promiseData = null;
125            // @ts-ignore
126            promiseData = xpowerDataSender(it.name, traceRow);
127            if (promiseData === null) {
128                // @ts-ignore
129                return new Promise<Array<unknown>>((resolve) => resolve([]));
130            } else {
131                // @ts-ignore
132                return promiseData.then((resultXpower: Array<unknown>) => {
133                    for (let j = 0; j < resultXpower.length; j++) {
134                        // @ts-ignore
135                        if ((resultXpower[j].value || 0) > it.maxValue!) {
136                            // @ts-ignore
137                            it.maxValue = resultXpower[j].value || 0;
138                        }
139                        if (j > 0) {
140                            // @ts-ignore
141                            resultXpower[j].delta = (resultXpower[j].value || 0) - (resultXpower[j - 1].value || 0);
142                        } else {
143                            // @ts-ignore
144                            resultXpower[j].delta = 0;
145                        }
146                    }
147                    return resultXpower;
148                });
149            }
150        };
151    }
152
153    private xpowerThreadHandler(
154        traceRow: TraceRow<XpowerStruct>,
155        it: {
156            name: string;
157            num: number;
158            maxValue?: number;
159        },
160        xpowerId: number
161    ): void {
162        traceRow.onThreadHandler = (useCache): void => {
163            let context: CanvasRenderingContext2D;
164            if (traceRow.currentContext) {
165                context = traceRow.currentContext;
166            } else {
167                context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
168            }
169            traceRow.canvasSave(context);
170            (renders.xpower as XpowerRender).renderMainThread(
171                {
172                    context: context,
173                    useCache: useCache,
174                    type: it.name,
175                    maxValue: it.maxValue === 0 ? 1 : it.maxValue!,
176                    index: xpowerId,
177                    maxName: it.maxValue!.toString()
178                },
179                traceRow
180            );
181            traceRow.canvasRestore(context, this.trace);
182        };
183    }
184
185    async initSystemData(folder: TraceRow<BaseStruct>, xpowerList: Array<{
186        name: string;
187        num: number;
188        maxValue?: number;
189    }>, traceId?: string): Promise<void> {
190        info('xpowerList data size is: ', xpowerList!.length);
191        XpowerStruct.maxValue = xpowerList.map((item) => item.num).reduce((a, b) => Math.max(a, b));
192        for (let i = 0; i < xpowerList.length; i++) {
193            const it = xpowerList[i];
194            it.maxValue = 0;
195            let traceRow = TraceRow.skeleton<XpowerStruct>(traceId);
196            traceRow.rowId = it.name;
197            traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_SYSTEM;
198            traceRow.rowParentId = folder.rowId;
199            traceRow.style.height = '40px';
200            traceRow.name = it.name;
201            traceRow.rowHidden = !folder.expansion;
202            traceRow.folderTextLeft = 40;
203            traceRow.xpowerRowTitle = this.convertTitle(it.name);
204            traceRow.setAttribute('children', '');
205            traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
206            traceRow.selectChangeHandler = this.trace.selectChangeHandler;
207            this.xpowerSupplierFrame(traceRow, it);
208            traceRow.getCacheData = (args: unknown): Promise<XpowerStruct[]> | undefined => {
209                let result: Promise<XpowerStruct[]> | undefined;
210                result = xpowerDataSender(it.name, traceRow, args);
211                return result;
212            };
213            traceRow.focusHandler = (ev): void => {
214                this.trace?.displayTip(
215                    traceRow,
216                    XpowerStruct.hoverXpowerStruct,
217                    `<span>${it.name === 'ThermalReport.ShellTemp' ? XpowerStruct.hoverXpowerStruct?.value! :
218                        ColorUtils.formatNumberComma(XpowerStruct.hoverXpowerStruct?.value!)}</span>`
219                );
220            };
221            traceRow.findHoverStruct = (): void => {
222                XpowerStruct.hoverXpowerStruct = traceRow.getHoverStruct();
223            };
224            this.xpowerThreadHandler(traceRow, it, i);
225            folder.addChildTraceRow(traceRow);
226        }
227    }
228
229    convertTitle(title: string): string {
230        switch (title) {
231            case 'Battery.Capacity':
232                return '电池容量(单位mAh)';
233            case 'Battery.Charge':
234                return '充电状态(充电1,非充电0)';
235            case 'Battery.GasGauge':
236                return '电池剩余电量(单位mAh)';
237            case 'Battery.Level':
238                return '电池百分比';
239            case 'Battery.RealCurrent':
240                return '实时电流(单位mAh,充电时为正数,耗电时为负数)';
241            case 'Battery.Screen':
242                return '屏幕状态(亮屏1,灭屏0)';
243            case 'ThermalReport.ShellTemp':
244                return '外壳温度(单位℃)';
245            case 'ThermalReport.ThermalLevel':
246                return '温度等级';
247            default:
248                return title;
249        }
250    }
251
252}
253