Latency: introduce latency density view
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.timing.ui / src / org / eclipse / tracecompass / analysis / timing / ui / views / segmentstore / density / AbstractSegmentStoreDensityViewer.java
CommitLineData
b23cbbfc
MAL
1/******************************************************************************
2 * Copyright (c) 2015 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
9
10package org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.density;
11
12import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
13
14import java.util.ArrayList;
15import java.util.Arrays;
16import java.util.Collections;
17import java.util.Iterator;
18import java.util.List;
19import java.util.concurrent.CompletableFuture;
20
21import org.eclipse.jdt.annotation.Nullable;
22import org.eclipse.swt.SWT;
23import org.eclipse.swt.graphics.Color;
24import org.eclipse.swt.graphics.RGB;
25import org.eclipse.swt.widgets.Composite;
26import org.eclipse.swt.widgets.Display;
27import org.eclipse.tracecompass.analysis.timing.core.segmentstore.AbstractSegmentStoreAnalysisModule;
28import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener;
29import org.eclipse.tracecompass.common.core.NonNullUtils;
30import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.DensityTimeFormat;
31import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.MouseDragZoomProvider;
32import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.MouseSelectionProvider;
33import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.SimpleTooltipProvider;
34import org.eclipse.tracecompass.segmentstore.core.ISegment;
35import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
36import org.eclipse.tracecompass.segmentstore.core.SegmentComparators;
37import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
38import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
39import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
40import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
41import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
42import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
43import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
44import org.eclipse.tracecompass.tmf.core.trace.TmfTraceContext;
45import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
46import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
47import org.swtchart.Chart;
48import org.swtchart.IAxis;
49import org.swtchart.IBarSeries;
50import org.swtchart.ISeries;
51import org.swtchart.ISeries.SeriesType;
52import org.swtchart.ISeriesSet;
53import org.swtchart.LineStyle;
54import org.swtchart.Range;
55
56import com.google.common.base.Predicate;
57import com.google.common.collect.Iterators;
58import com.google.common.collect.Lists;
59
60/**
61 * Displays the segment store analysis data in a density chart.
62 *
63 * @author Matthew Khouzam
64 * @author Marc-Andre Laperle
65 *
66 * @since 2.0
67 */
68public abstract class AbstractSegmentStoreDensityViewer extends TmfViewer {
69
70 private static final DensityTimeFormat DENSITY_TIME_FORMATTER = new DensityTimeFormat();
71 private static final RGB BAR_COLOR = new RGB(0x42, 0x85, 0xf4);
72 private final Chart fChart;
73 private final MouseDragZoomProvider fDragZoomProvider;
74 private final MouseSelectionProvider fDragProvider;
75 private final SimpleTooltipProvider fTooltipProvider;
76
77 private @Nullable ITmfTrace fTrace;
78 private @Nullable IAnalysisProgressListener fListener;
79 private @Nullable AbstractSegmentStoreAnalysisModule fAnalysisModule;
80 private TmfTimeRange fCurrentTimeRange = TmfTimeRange.NULL_RANGE;
81 private List<ISegmentStoreDensityViewerDataListener> fListeners;
82
83 /**
84 * Constructs a new density viewer.
85 *
86 * @param parent
87 * the parent of the viewer
88 */
89 public AbstractSegmentStoreDensityViewer(Composite parent) {
90 super(parent);
91 fListeners = new ArrayList<>();
92 fChart = new Chart(parent, SWT.NONE);
93 fChart.getLegend().setVisible(false);
94 fChart.getTitle().setVisible(false);
95 fChart.getAxisSet().getXAxis(0).getTitle().setText(nullToEmptyString(Messages.AbstractSegmentStoreDensityViewer_TimeAxisLabel));
96 fChart.getAxisSet().getYAxis(0).getTitle().setText(nullToEmptyString(Messages.AbstractSegmentStoreDensityViewer_CountAxisLabel));
97 fChart.getAxisSet().getXAxis(0).getGrid().setStyle(LineStyle.NONE);
98 fChart.getAxisSet().getYAxis(0).getGrid().setStyle(LineStyle.NONE);
99
100 fDragZoomProvider = new MouseDragZoomProvider(this);
101 fDragZoomProvider.register();
102 fDragProvider = new MouseSelectionProvider(this);
103 fDragProvider.register();
104 fTooltipProvider = new SimpleTooltipProvider(this);
105 fTooltipProvider.register();
106 }
107
108 /**
109 * Returns the segment store analysis module
110 *
111 * @param trace
112 * The trace to consider
113 * @return the analysis module
114 */
115 protected @Nullable abstract AbstractSegmentStoreAnalysisModule getSegmentStoreAnalysisModule(ITmfTrace trace);
116
117 @Nullable
118 private static ITmfTrace getTrace() {
119 return TmfTraceManager.getInstance().getActiveTrace();
120 }
121
122 private void updateDisplay(List<ISegment> data) {
123 if (data.isEmpty()) {
124 return;
125 }
126 IBarSeries series = (IBarSeries) fChart.getSeriesSet().createSeries(SeriesType.BAR, Messages.AbstractSegmentStoreDensityViewer_SeriesLabel);
127 series.setVisible(true);
128 series.setBarPadding(0);
129
130 series.setBarColor(new Color(Display.getDefault(), BAR_COLOR));
131 int barWidth = 4;
132 final int width = fChart.getPlotArea().getBounds().width / barWidth;
133 double[] xOrigSeries = new double[width];
134 double[] yOrigSeries = new double[width];
135 Arrays.fill(yOrigSeries, 1.0);
136 long maxLength = data.get(data.size() - 1).getLength();
137 double maxFactor = 1.0 / (maxLength + 1.0);
138 long minX = Long.MAX_VALUE;
139 for (ISegment segment : data) {
140 double xBox = segment.getLength() * maxFactor * width;
141 yOrigSeries[(int) xBox]++;
142 minX = Math.min(minX, segment.getLength());
143 }
144 for (int i = 0; i < width; i++) {
145 xOrigSeries[i] = i * maxLength / width;
146 }
147 double maxY = Double.MIN_VALUE;
148 for (int i = 0; i < width; i++) {
149 maxY = Math.max(maxY, yOrigSeries[i]);
150 }
151 if (minX == maxLength) {
152 maxLength++;
153 minX--;
154 }
155 series.setYSeries(yOrigSeries);
156 series.setXSeries(xOrigSeries);
157 final IAxis xAxis = fChart.getAxisSet().getXAxis(0);
158 /*
159 * adjustrange appears to bring origin back since we pad the series with
160 * 0s, not interesting.
161 */
162 xAxis.adjustRange();
163 Range range = xAxis.getRange();
164 // fix for overly aggressive lower after an adjust range
165 range.lower = minX - range.upper + maxLength;
166 xAxis.setRange(range);
167 xAxis.getTick().setFormat(DENSITY_TIME_FORMATTER);
168 fChart.getAxisSet().getYAxis(0).setRange(new Range(1.0, maxY));
169 fChart.getAxisSet().getYAxis(0).enableLogScale(true);
170 fChart.redraw();
171 }
172
173 @Override
174 public Chart getControl() {
175 return fChart;
176 }
177
178 /**
179 * Select a range of latency durations in the viewer.
180 *
181 * @param durationRange
182 * a range of latency durations
183 */
184 public void select(Range durationRange) {
185 computeDataAsync(fCurrentTimeRange, durationRange).thenAccept((data) -> {
186 for (ISegmentStoreDensityViewerDataListener listener : fListeners) {
187 listener.dataSelectionChanged(data);
188 }
189 });
190 }
191
192 /**
193 * Zoom to a range of latency durations in the viewer.
194 *
195 * @param durationRange
196 * a range of latency durations
197 */
198 public void zoom(Range durationRange) {
199 computeDataAsync(fCurrentTimeRange, durationRange).thenAccept((data) -> applyData(data));
200 }
201
202 private CompletableFuture<List<ISegment>> computeDataAsync(final TmfTimeRange timeRange, final Range durationRange) {
203 return CompletableFuture.supplyAsync(() -> computeData(timeRange, durationRange));
204 }
205
206 private @Nullable ArrayList<ISegment> computeData(final TmfTimeRange timeRange, final Range durationRange) {
207 final AbstractSegmentStoreAnalysisModule analysisModule = fAnalysisModule;
208 if (analysisModule == null) {
209 return null;
210 }
211 final ISegmentStore<ISegment> results = analysisModule.getResults();
212 if (results == null) {
213 return null;
214 }
215
216 Iterator<ISegment> intersectingElements = results.getIntersectingElements(timeRange.getStartTime().getValue(), timeRange.getEndTime().getValue()).iterator();
217
218 if (durationRange.lower > Double.MIN_VALUE || durationRange.upper < Double.MAX_VALUE) {
219 Predicate<? super ISegment> predicate = new Predicate<ISegment>() {
220 @Override
221 public boolean apply(@Nullable ISegment input) {
222 return input != null && input.getLength() >= durationRange.lower && input.getLength() <= durationRange.upper;
223 }
224 };
225 intersectingElements = Iterators.<ISegment> filter(intersectingElements, predicate);
226 }
227
228 return Lists.newArrayList(intersectingElements);
229 }
230
231 private void applyData(final @Nullable List<ISegment> data) {
232 if (data != null) {
233 Collections.sort(data, SegmentComparators.INTERVAL_LENGTH_COMPARATOR);
234 Display.getDefault().asyncExec(() -> updateDisplay(data));
235 for (ISegmentStoreDensityViewerDataListener l : fListeners) {
236 l.dataChanged(data);
237 }
238 }
239 }
240
241 /**
242 * Signal handler for handling of the window range signal.
243 *
244 * @param signal
245 * The {@link TmfWindowRangeUpdatedSignal}
246 */
247 @TmfSignalHandler
248 public void windowRangeUpdated(@Nullable TmfWindowRangeUpdatedSignal signal) {
249 if (signal == null) {
250 return;
251 }
252 ITmfTrace trace = getTrace();
253 if (trace == null) {
254 return;
255 }
256 fAnalysisModule = getSegmentStoreAnalysisModule(trace);
257 fCurrentTimeRange = NonNullUtils.checkNotNull(signal.getCurrentRange());
258 updateWithRange(fCurrentTimeRange);
259 }
260
261 private void updateWithRange(final TmfTimeRange range) {
262 computeDataAsync(range, new Range(Double.MIN_VALUE, Double.MAX_VALUE)).thenAccept((data) -> applyData(data));
263 }
264
265 @Override
266 public void refresh() {
267 fChart.redraw();
268 }
269
270 @Override
271 public void dispose() {
272 if (fAnalysisModule != null && fListener != null) {
273 fAnalysisModule.removeListener(fListener);
274 }
275 fDragZoomProvider.deregister();
276 fTooltipProvider.deregister();
277 fDragProvider.deregister();
278 super.dispose();
279 }
280
281 /**
282 * Signal handler for handling of the trace opened signal.
283 *
284 * @param signal
285 * The trace opened signal {@link TmfTraceOpenedSignal}
286 */
287 @TmfSignalHandler
288 public void traceOpened(TmfTraceOpenedSignal signal) {
289 fTrace = signal.getTrace();
290 loadTrace(getTrace());
291 }
292
293 /**
294 * Signal handler for handling of the trace selected signal.
295 *
296 * @param signal
297 * The trace selected signal {@link TmfTraceSelectedSignal}
298 */
299 @TmfSignalHandler
300 public void traceSelected(TmfTraceSelectedSignal signal) {
301 if (fTrace != signal.getTrace()) {
302 fTrace = signal.getTrace();
303 loadTrace(getTrace());
304 }
305 }
306
307 /**
308 * Signal handler for handling of the trace closed signal.
309 *
310 * @param signal
311 * The trace closed signal {@link TmfTraceClosedSignal}
312 */
313 @TmfSignalHandler
314 public void traceClosed(TmfTraceClosedSignal signal) {
315
316 if (signal.getTrace() != fTrace) {
317 return;
318 }
319
320 fTrace = null;
321 clearContent();
322 }
323
324 /**
325 * A Method to load a trace into the viewer.
326 *
327 * @param trace
328 * A trace to apply in the viewer
329 */
330 protected void loadTrace(@Nullable ITmfTrace trace) {
331 clearContent();
332
333 fTrace = trace;
334 TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
335 TmfTimeRange windowRange = ctx.getWindowRange();
336 fCurrentTimeRange = windowRange;
337
338 if (trace != null) {
339 fAnalysisModule = getSegmentStoreAnalysisModule(trace);
340 final AbstractSegmentStoreAnalysisModule module = fAnalysisModule;
341 if (module != null) {
342 fListener = (activeAnalysis, data) -> updateWithRange(windowRange);
343 module.addListener(fListener);
344 module.schedule();
345 }
346 }
347 zoom(new Range(0, Long.MAX_VALUE));
348 }
349
350 /**
351 * Clears the view content.
352 */
353 private void clearContent() {
354 final Chart chart = fChart;
355 if (!chart.isDisposed()) {
356 ISeriesSet set = chart.getSeriesSet();
357 ISeries[] series = set.getSeries();
358 for (int i = 0; i < series.length; i++) {
359 set.deleteSeries(series[i].getId());
360 }
361 for (IAxis axis : chart.getAxisSet().getAxes()) {
362 axis.setRange(new Range(0, 1));
363 }
364 chart.redraw();
365 }
366 }
367
368 /**
369 * Add a data listener.
370 *
371 * @param dataListener
372 * the data listener to add
373 */
374 public void addDataListener(ISegmentStoreDensityViewerDataListener dataListener) {
375 fListeners.add(dataListener);
376 }
377
378 /**
379 * Remove a data listener.
380 *
381 * @param dataListener
382 * the data listener to remove
383 */
384 public void removeDataListener(ISegmentStoreDensityViewerDataListener dataListener) {
385 fListeners.remove(dataListener);
386 }
387}
This page took 0.038379 seconds and 5 git commands to generate.