542a5dccfb8441752b26be925432781fac902240
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.timing.ui / src / org / eclipse / tracecompass / analysis / timing / ui / views / segmentstore / density / AbstractSegmentStoreDensityViewer.java
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
10 package org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.density;
11
12 import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
13
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Collections;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.concurrent.CompletableFuture;
20
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.graphics.Color;
24 import org.eclipse.swt.graphics.RGB;
25 import org.eclipse.swt.widgets.Composite;
26 import org.eclipse.swt.widgets.Display;
27 import org.eclipse.tracecompass.analysis.timing.core.segmentstore.AbstractSegmentStoreAnalysisModule;
28 import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener;
29 import org.eclipse.tracecompass.common.core.NonNullUtils;
30 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.DensityTimeFormat;
31 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.MouseDragZoomProvider;
32 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.MouseSelectionProvider;
33 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.SimpleTooltipProvider;
34 import org.eclipse.tracecompass.segmentstore.core.ISegment;
35 import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
36 import org.eclipse.tracecompass.segmentstore.core.SegmentComparators;
37 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
38 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
39 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
40 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
41 import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
42 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
43 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
44 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceContext;
45 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
46 import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
47 import org.swtchart.Chart;
48 import org.swtchart.IAxis;
49 import org.swtchart.IBarSeries;
50 import org.swtchart.ISeries;
51 import org.swtchart.ISeries.SeriesType;
52 import org.swtchart.ISeriesSet;
53 import org.swtchart.LineStyle;
54 import org.swtchart.Range;
55
56 import com.google.common.base.Predicate;
57 import com.google.common.collect.Iterators;
58 import 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 */
68 public 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 double timeWidth = (double) maxLength / (double) width;
145 for (int i = 0; i < width; i++) {
146 xOrigSeries[i] = i * timeWidth;
147 }
148 double maxY = Double.MIN_VALUE;
149 for (int i = 0; i < width; i++) {
150 maxY = Math.max(maxY, yOrigSeries[i]);
151 }
152 if (minX == maxLength) {
153 maxLength++;
154 minX--;
155 }
156 series.setYSeries(yOrigSeries);
157 series.setXSeries(xOrigSeries);
158 final IAxis xAxis = fChart.getAxisSet().getXAxis(0);
159 /*
160 * adjustrange appears to bring origin back since we pad the series with
161 * 0s, not interesting.
162 */
163 xAxis.adjustRange();
164 Range range = xAxis.getRange();
165 // fix for overly aggressive lower after an adjust range
166 range.lower = minX - range.upper + maxLength;
167 xAxis.setRange(range);
168 xAxis.getTick().setFormat(DENSITY_TIME_FORMATTER);
169 fChart.getAxisSet().getYAxis(0).setRange(new Range(1.0, maxY));
170 fChart.getAxisSet().getYAxis(0).enableLogScale(true);
171 fChart.redraw();
172 }
173
174 @Override
175 public Chart getControl() {
176 return fChart;
177 }
178
179 /**
180 * Select a range of latency durations in the viewer.
181 *
182 * @param durationRange
183 * a range of latency durations
184 */
185 public void select(Range durationRange) {
186 computeDataAsync(fCurrentTimeRange, durationRange).thenAccept((data) -> {
187 for (ISegmentStoreDensityViewerDataListener listener : fListeners) {
188 listener.dataSelectionChanged(data);
189 }
190 });
191 }
192
193 /**
194 * Zoom to a range of latency durations in the viewer.
195 *
196 * @param durationRange
197 * a range of latency durations
198 */
199 public void zoom(Range durationRange) {
200 computeDataAsync(fCurrentTimeRange, durationRange).thenAccept((data) -> applyData(data));
201 }
202
203 private CompletableFuture<List<ISegment>> computeDataAsync(final TmfTimeRange timeRange, final Range durationRange) {
204 return CompletableFuture.supplyAsync(() -> computeData(timeRange, durationRange));
205 }
206
207 private @Nullable List<ISegment> computeData(final TmfTimeRange timeRange, final Range durationRange) {
208 final AbstractSegmentStoreAnalysisModule analysisModule = fAnalysisModule;
209 if (analysisModule == null) {
210 return null;
211 }
212 final ISegmentStore<ISegment> results = analysisModule.getResults();
213 if (results == null) {
214 return null;
215 }
216
217 Iterator<ISegment> intersectingElements = results.getIntersectingElements(timeRange.getStartTime().getValue(), timeRange.getEndTime().getValue()).iterator();
218
219 if (durationRange.lower > Double.MIN_VALUE || durationRange.upper < Double.MAX_VALUE) {
220 Predicate<? super ISegment> predicate = new Predicate<ISegment>() {
221 @Override
222 public boolean apply(@Nullable ISegment input) {
223 return input != null && input.getLength() >= durationRange.lower && input.getLength() <= durationRange.upper;
224 }
225 };
226 intersectingElements = Iterators.<ISegment> filter(intersectingElements, predicate);
227 }
228
229 return Lists.newArrayList(intersectingElements);
230 }
231
232 private void applyData(final @Nullable List<ISegment> data) {
233 if (data != null) {
234 Collections.sort(data, SegmentComparators.INTERVAL_LENGTH_COMPARATOR);
235 Display.getDefault().asyncExec(() -> updateDisplay(data));
236 for (ISegmentStoreDensityViewerDataListener l : fListeners) {
237 l.dataChanged(data);
238 }
239 }
240 }
241
242 /**
243 * Signal handler for handling of the window range signal.
244 *
245 * @param signal
246 * The {@link TmfWindowRangeUpdatedSignal}
247 */
248 @TmfSignalHandler
249 public void windowRangeUpdated(@Nullable TmfWindowRangeUpdatedSignal signal) {
250 if (signal == null) {
251 return;
252 }
253 ITmfTrace trace = getTrace();
254 if (trace == null) {
255 return;
256 }
257 fAnalysisModule = getSegmentStoreAnalysisModule(trace);
258 fCurrentTimeRange = NonNullUtils.checkNotNull(signal.getCurrentRange());
259 updateWithRange(fCurrentTimeRange);
260 }
261
262 private void updateWithRange(final TmfTimeRange range) {
263 computeDataAsync(range, new Range(Double.MIN_VALUE, Double.MAX_VALUE)).thenAccept((data) -> applyData(data));
264 }
265
266 @Override
267 public void refresh() {
268 fChart.redraw();
269 }
270
271 @Override
272 public void dispose() {
273 if (fAnalysisModule != null && fListener != null) {
274 fAnalysisModule.removeListener(fListener);
275 }
276 fDragZoomProvider.deregister();
277 fTooltipProvider.deregister();
278 fDragProvider.deregister();
279 super.dispose();
280 }
281
282 /**
283 * Signal handler for handling of the trace opened signal.
284 *
285 * @param signal
286 * The trace opened signal {@link TmfTraceOpenedSignal}
287 */
288 @TmfSignalHandler
289 public void traceOpened(TmfTraceOpenedSignal signal) {
290 fTrace = signal.getTrace();
291 loadTrace(getTrace());
292 }
293
294 /**
295 * Signal handler for handling of the trace selected signal.
296 *
297 * @param signal
298 * The trace selected signal {@link TmfTraceSelectedSignal}
299 */
300 @TmfSignalHandler
301 public void traceSelected(TmfTraceSelectedSignal signal) {
302 if (fTrace != signal.getTrace()) {
303 fTrace = signal.getTrace();
304 loadTrace(getTrace());
305 }
306 }
307
308 /**
309 * Signal handler for handling of the trace closed signal.
310 *
311 * @param signal
312 * The trace closed signal {@link TmfTraceClosedSignal}
313 */
314 @TmfSignalHandler
315 public void traceClosed(TmfTraceClosedSignal signal) {
316
317 if (signal.getTrace() != fTrace) {
318 return;
319 }
320
321 fTrace = null;
322 clearContent();
323 }
324
325 /**
326 * A Method to load a trace into the viewer.
327 *
328 * @param trace
329 * A trace to apply in the viewer
330 */
331 protected void loadTrace(@Nullable ITmfTrace trace) {
332 clearContent();
333
334 fTrace = trace;
335 TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
336 TmfTimeRange windowRange = ctx.getWindowRange();
337 fCurrentTimeRange = windowRange;
338
339 if (trace != null) {
340 fAnalysisModule = getSegmentStoreAnalysisModule(trace);
341 final AbstractSegmentStoreAnalysisModule module = fAnalysisModule;
342 if (module != null) {
343 fListener = (activeAnalysis, data) -> updateWithRange(windowRange);
344 module.addListener(fListener);
345 module.schedule();
346 }
347 }
348 zoom(new Range(0, Long.MAX_VALUE));
349 }
350
351 /**
352 * Clears the view content.
353 */
354 private void clearContent() {
355 final Chart chart = fChart;
356 if (!chart.isDisposed()) {
357 ISeriesSet set = chart.getSeriesSet();
358 ISeries[] series = set.getSeries();
359 for (int i = 0; i < series.length; i++) {
360 set.deleteSeries(series[i].getId());
361 }
362 for (IAxis axis : chart.getAxisSet().getAxes()) {
363 axis.setRange(new Range(0, 1));
364 }
365 chart.redraw();
366 }
367 }
368
369 /**
370 * Add a data listener.
371 *
372 * @param dataListener
373 * the data listener to add
374 */
375 public void addDataListener(ISegmentStoreDensityViewerDataListener dataListener) {
376 fListeners.add(dataListener);
377 }
378
379 /**
380 * Remove a data listener.
381 *
382 * @param dataListener
383 * the data listener to remove
384 */
385 public void removeDataListener(ISegmentStoreDensityViewerDataListener dataListener) {
386 fListeners.remove(dataListener);
387 }
388 }
This page took 0.038693 seconds and 4 git commands to generate.