• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 
16 package ohos.devtools.views.layout.chartview.observer;
17 
18 import com.intellij.icons.AllIcons;
19 import com.intellij.openapi.util.IconLoader;
20 import ohos.devtools.datasources.utils.common.util.DateTimeUtil;
21 import ohos.devtools.datasources.utils.profilerlog.ProfilerLogManager;
22 import ohos.devtools.datasources.utils.quartzmanager.QuartzManager;
23 import ohos.devtools.datasources.utils.session.entity.SessionInfo;
24 import ohos.devtools.datasources.utils.session.service.SessionManager;
25 import ohos.devtools.views.charts.model.ChartDataRange;
26 import ohos.devtools.views.charts.model.ChartStandard;
27 import ohos.devtools.views.common.LayoutConstants;
28 import ohos.devtools.views.common.customcomp.CustomJButton;
29 import ohos.devtools.views.layout.chartview.CountingThread;
30 import ohos.devtools.views.layout.chartview.ProfilerChartsView;
31 import ohos.devtools.views.layout.chartview.event.IChartEventObserver;
32 import ohos.devtools.views.layout.chartview.event.IChartEventPublisher;
33 import org.apache.logging.log4j.LogManager;
34 import org.apache.logging.log4j.Logger;
35 
36 import javax.swing.SwingWorker;
37 import java.awt.event.ActionListener;
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.Locale;
41 import java.util.concurrent.ExecutionException;
42 import java.util.concurrent.TimeUnit;
43 
44 import static ohos.devtools.views.common.LayoutConstants.INITIAL_VALUE;
45 import static ohos.devtools.views.layout.chartview.utils.ChartViewConstants.CHART_START_DELAY;
46 import static ohos.devtools.views.layout.chartview.utils.ChartViewConstants.REFRESH_FREQ;
47 
48 /**
49  * 监控界面保存Chart的面板的事件发布者
50  *
51  * @since 2021/11/22
52  */
53 public class ProfilerChartsViewPublisher implements IChartEventPublisher {
54     private static final Logger LOGGER = LogManager.getLogger(ProfilerChartsViewPublisher.class);
55 
56     /**
57      * Chart监控界面的定时刷新线程的名称
58      */
59     public static final String RUN_NAME = "ProfilerChartsViewMonitorTimer";
60 
61     /**
62      * Chart监控界面的定时刷新线程的进度条名称
63      */
64     private static final String RUN_NAME_SCROLLBAR = "ScrollbarTimer";
65 
66     private static final int NUM_10 = 10;
67 
68     /**
69      * 监听的视图
70      */
71     private final ProfilerChartsView view;
72 
73     /**
74      * 是否为Trace文件静态导入模式
75      *
76      * @see "true表示静态导入,false表示动态实时跟踪"
77      */
78     private final boolean traceFile;
79 
80     /**
81      * 监听者的集合
82      */
83     private final List<IChartEventObserver> observers = new ArrayList<>();
84 
85     /**
86      * 绘图标准
87      */
88     private final ChartStandard standard;
89 
90     /**
91      * Chart刷新线程是否在运行
92      */
93     private boolean refreshing = false;
94 
95     /**
96      * 滚动条是否显示
97      */
98     private boolean displayScrollbar = false;
99 
100     /**
101      * 启动任务时,本机时间和数据流中的时间偏移量
102      */
103     private long startOffset = INITIAL_VALUE;
104 
105     /**
106      * 构造函数
107      *
108      * @param view 监听的视图
109      * @param traceFile 是否为Trace文件静态导入模式
110      */
ProfilerChartsViewPublisher(ProfilerChartsView view, boolean traceFile)111     public ProfilerChartsViewPublisher(ProfilerChartsView view, boolean traceFile) {
112         this.view = view;
113         this.traceFile = traceFile;
114         standard = new ChartStandard(view.getSessionId());
115     }
116 
117     /**
118      * 展示Trace文件分析结果
119      *
120      * @param firstTimestamp Trace文件中数据的开始时间
121      * @param lastTimestamp Trace文件中数据的结束时间
122      */
showTraceResult(long firstTimestamp, long lastTimestamp)123     public void showTraceResult(long firstTimestamp, long lastTimestamp) {
124         if (!traceFile) {
125             return;
126         }
127 
128         standard.setFirstTimestamp(firstTimestamp);
129         // 保存trace文件导入模式下最后一个数据的时间戳
130         standard.setLastTimestamp(lastTimestamp);
131         int end = (int) (lastTimestamp - firstTimestamp);
132         int start = 0;
133         if (end > standard.getMaxDisplayMillis()) {
134             start = end - standard.getMaxDisplayMillis();
135             // 这里需要异步初始化滚动条,否则会因为view没有渲染导致滚动条无法显示
136             new SwingWorker<>() {
137                 @Override
138                 protected Object doInBackground() {
139                     try {
140                         TimeUnit.MILLISECONDS.sleep(LayoutConstants.FIVE_HUNDRED);
141                     } catch (InterruptedException exception) {
142                         if (ProfilerLogManager.isErrorEnabled()) {
143                             LOGGER.error("Asynchronous initialization scrollbar failed!", exception);
144                         }
145                     }
146                     return new Object();
147                 }
148 
149                 @Override
150                 protected void done() {
151                     view.initScrollbar();
152                     view.getHorizontalBar().resizeAndReposition();
153                     displayScrollbar = true;
154                 }
155             }.execute();
156         }
157         notifyRefresh(start, end);
158     }
159 
160     /**
161      * 检查Loading结果:Loading完成后启动刷新
162      */
checkLoadingResult()163     public void checkLoadingResult() {
164         new SwingWorker<SessionInfo, Object>() {
165             @Override
166             protected SessionInfo doInBackground() throws InterruptedException {
167                 SessionInfo info = SessionManager.getInstance().getSessionInfo(standard.getSessionId());
168                 while (info == null || !info.isStartRefsh()) {
169                     try {
170                         TimeUnit.MILLISECONDS.sleep(NUM_10);
171                     } catch (InterruptedException exception) {
172                         if (ProfilerLogManager.isErrorEnabled()) {
173                             LOGGER.error("checkLoadingResult InterruptedException", exception);
174                         }
175                     }
176                     info = SessionManager.getInstance().getSessionInfo(standard.getSessionId());
177                 }
178                 // 等待一段时间再启动刷新Chart,否则会导致查询的数据还未入库完成
179                 TimeUnit.MILLISECONDS.sleep(CHART_START_DELAY);
180                 return info;
181             }
182 
183             @Override
184             protected void done() {
185                 try {
186                     SessionInfo info = get();
187                     long first = info.getStartTimestamp();
188                     view.hideLoading();
189                     startRefresh(first);
190                 } catch (InterruptedException | ExecutionException exception) {
191                     LOGGER.error(String.format(Locale.ENGLISH, "Error occur when loading done: %s", exception
192                         .toString()));
193                 }
194             }
195         }.execute();
196     }
197 
198     /**
199      * 开始刷新Chart
200      *
201      * @param firstTimestamp 本次Chart第一个数据的时间戳
202      */
startRefresh(long firstTimestamp)203     public void startRefresh(long firstTimestamp) {
204         if (traceFile) {
205             return;
206         }
207         view.setStop(false);
208         view.setPause(false);
209         standard.setFirstTimestamp(firstTimestamp);
210         startOffset = DateTimeUtil.getNowTimeLong() - standard.getFirstTimestamp();
211         // 启动Chart绘制定时器
212         startChartTimer();
213         refreshing = true;
214     }
215 
216     /**
217      * 启动Chart绘制定时器
218      */
startChartTimer()219     private void startChartTimer() {
220         // 启动绘制Chart线程
221         QuartzManager.getInstance().addExecutor(RUN_NAME, () -> {
222             try {
223                 // 保存LastTimestamp,为当前时间戳减去Chart启动延迟
224                 standard.setLastTimestamp(DateTimeUtil.getNowTimeLong() - startOffset - CHART_START_DELAY);
225                 int end = (int) (standard.getLastTimestamp() - standard.getFirstTimestamp());
226                 int start = end > standard.getMaxDisplayMillis() ? end - standard.getMaxDisplayMillis() : 0;
227                 notifyRefresh(start, end);
228             } catch (Exception exception) {
229                 if (ProfilerLogManager.isErrorEnabled()) {
230                     LOGGER.error("Exception", exception);
231                 }
232             }
233         });
234         // 刷新间隔暂定30ms
235         QuartzManager.getInstance().startExecutor(RUN_NAME, 0, REFRESH_FREQ);
236 
237         // 如果有线程在刷新,则不需要再另起一个轮询线程。
238         if (!refreshing) {
239             QuartzManager.getInstance().addExecutor(RUN_NAME_SCROLLBAR, () -> {
240                 // 保存LastTimestamp,为当前时间戳减去Chart启动延迟
241                 standard.setLastTimestamp(DateTimeUtil.getNowTimeLong() - startOffset - CHART_START_DELAY);
242                 int end = (int) (standard.getLastTimestamp() - standard.getFirstTimestamp());
243                 int start = end > standard.getMaxDisplayMillis() ? end - standard.getMaxDisplayMillis() : 0;
244                 // 当end大于最大展示时间时,且滚动条未显示时,初始化显示滚动条,并把isScrollbarShow置为true
245                 if (end > standard.getMaxDisplayMillis() && !displayScrollbar) {
246                     // isScrollbarShow判断必须保留,否则会导致Scrollbar重复初始化,频繁闪烁
247                     view.initScrollbar();
248                     displayScrollbar = true;
249                 }
250                 notifyRefreshScrollbar(start, end);
251             });
252 
253             // 刷新间隔暂定30ms
254             QuartzManager.getInstance().startExecutor(RUN_NAME_SCROLLBAR, 0, REFRESH_FREQ);
255         }
256     }
257 
258     /**
259      * 暂停刷新Chart
260      */
pauseRefresh()261     public void pauseRefresh() {
262         if (refreshing) {
263             QuartzManager.getInstance().deleteExecutor(RUN_NAME);
264             refreshing = false;
265             view.setPause(true);
266         }
267     }
268 
269     /**
270      * 停止刷新Chart
271      *
272      * @param isOffline 设备是否断连
273      */
stopRefresh(boolean isOffline)274     public void stopRefresh(boolean isOffline) {
275         QuartzManager.getInstance().deleteExecutor(RUN_NAME);
276         QuartzManager.getInstance().deleteExecutor(RUN_NAME_SCROLLBAR);
277         refreshing = false;
278         view.setStop(true);
279         view.setPause(true);
280         if (isOffline) {
281             CustomJButton buttonRun = view.getTaskScenePanelChart().getjButtonRun();
282             CustomJButton buttonStop = view.getTaskScenePanelChart().getjButtonStop();
283             buttonRun.setIcon(IconLoader.getIcon("/images/breakpoint_grey.png", getClass()));
284             buttonRun.setEnabled(true);
285             ActionListener[] actionListenersRun = buttonRun.getActionListeners();
286             for (ActionListener listener : actionListenersRun) {
287                 buttonRun.removeActionListener(listener);
288             }
289 
290             buttonStop.setIcon(AllIcons.Process.ProgressResumeHover);
291             buttonStop.setEnabled(true);
292             ActionListener[] actionListenersStop = buttonStop.getActionListeners();
293             for (ActionListener listener : actionListenersStop) {
294                 buttonStop.removeActionListener(listener);
295             }
296             CountingThread countingThread = view.getTaskScenePanelChart().getCounting();
297             countingThread.setStopFlag(true);
298         }
299     }
300 
301     /**
302      * 暂停后重新开始刷新Chart
303      */
restartRefresh()304     public void restartRefresh() {
305         if (traceFile) {
306             return;
307         }
308 
309         if (view.isStop()) {
310             // 如果是已停止状态,则返回
311             return;
312         }
313 
314         if (!refreshing) {
315             refreshing = true;
316             view.setPause(false);
317             startChartTimer();
318             // 重新开始时,也要移除框选状态(暂定)
319             standard.clearAllSelectedRanges();
320         }
321     }
322 
323     /**
324      * Add observer
325      *
326      * @param observer Observer of chart refreshes event
327      */
328     @Override
attach(IChartEventObserver observer)329     public void attach(IChartEventObserver observer) {
330         observers.add(observer);
331     }
332 
333     /**
334      * Remove observer
335      *
336      * @param observer Observer of chart refreshes event
337      */
338     @Override
detach(IChartEventObserver observer)339     public void detach(IChartEventObserver observer) {
340         observers.remove(observer);
341     }
342 
343     /**
344      * notify to refresh
345      *
346      * @param start Start time of chart
347      * @param end End time of chart
348      */
349     @Override
notifyRefresh(int start, int end)350     public void notifyRefresh(int start, int end) {
351         standard.updateDisplayTimeRange(start, end);
352         ChartDataRange range = standard.getDisplayRange();
353         observers.forEach(lis -> lis.refreshView(range, standard.getFirstTimestamp(), !traceFile));
354     }
355 
356     /**
357      * 通知刷新滚动条
358      *
359      * @param start 开始时间
360      * @param end 结束时间
361      */
notifyRefreshScrollbar(int start, int end)362     private void notifyRefreshScrollbar(int start, int end) {
363         // 当前时间超过最大展示时间,则调整滚动条长度和位置
364         if (end > standard.getMaxDisplayMillis()) {
365             if (view.getHorizontalBar() != null) {
366                 view.getHorizontalBar().resizeAndReposition();
367             }
368         }
369     }
370 
371     /**
372      * 时间线和char缩放
373      *
374      * @param startTime 缩放后的界面开始时间
375      * @param endTime 结束时间的界面开始时间
376      * @param maxDisplayTime 窗体上可以显示的最大毫秒数
377      */
charZoom(int startTime, int endTime, int maxDisplayTime)378     public void charZoom(int startTime, int endTime, int maxDisplayTime) {
379         // 修改char展示的时间范围
380         standard.setMaxDisplayMillis(maxDisplayTime);
381         standard.updateDisplayTimeRange(startTime, endTime);
382         observers.forEach(lis -> {
383             standard.setMaxDisplayMillis(maxDisplayTime);
384             lis.refreshStandard(startTime, endTime, maxDisplayTime, standard.getMinMarkInterval());
385             lis.refreshView(standard.getDisplayRange(), standard.getFirstTimestamp(), !traceFile);
386         });
387     }
388 
389     /**
390      * 界面毫秒数的时间刻度缩放
391      *
392      * @param maxDisplayTime 窗体上可以显示的最大毫秒数
393      * @param minMarkInterval 窗体上可以显示的时间刻度的单位
394      * @param newStartTime 新的开始时间
395      * @param newEndTime 新的结束时间
396      */
msTimeZoom(int maxDisplayTime, int minMarkInterval, int newStartTime, int newEndTime)397     public void msTimeZoom(int maxDisplayTime, int minMarkInterval, int newStartTime, int newEndTime) {
398         standard.setMaxDisplayMillis(maxDisplayTime);
399         standard.setMinMarkInterval(minMarkInterval);
400         standard.updateDisplayTimeRange(newStartTime, newEndTime);
401         observers.forEach(lis -> {
402             lis.refreshStandard(newStartTime, newEndTime, maxDisplayTime, minMarkInterval);
403             lis.refreshView(standard.getDisplayRange(), standard.getFirstTimestamp(), !traceFile);
404         });
405     }
406 
407     /**
408      * Getter
409      *
410      * @return ChartStandard
411      */
getStandard()412     public ChartStandard getStandard() {
413         return standard;
414     }
415 
416     /**
417      * Getter
418      *
419      * @return traceFile
420      */
isTraceFile()421     public boolean isTraceFile() {
422         return traceFile;
423     }
424 
getObservers()425     public List<IChartEventObserver> getObservers() {
426         return observers;
427     }
428 
isRefreshing()429     public boolean isRefreshing() {
430         return refreshing;
431     }
432 
isDisplayScrollbar()433     public boolean isDisplayScrollbar() {
434         return displayScrollbar;
435     }
436 
setDisplayScrollbar(boolean displayScrollbar)437     public void setDisplayScrollbar(boolean displayScrollbar) {
438         this.displayScrollbar = displayScrollbar;
439     }
440 }
441