1 /******************************************************************************
2 * Copyright (c) 2015 Ericsson
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 *******************************************************************************/
10 package org
.eclipse
.tracecompass
.analysis
.timing
.ui
.views
.segmentstore
.density
;
12 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.nullToEmptyString
;
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
;
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
;
56 import com
.google
.common
.base
.Predicate
;
57 import com
.google
.common
.collect
.Iterators
;
58 import com
.google
.common
.collect
.Lists
;
61 * Displays the segment store analysis data in a density chart.
63 * @author Matthew Khouzam
64 * @author Marc-Andre Laperle
68 public abstract class AbstractSegmentStoreDensityViewer
extends TmfViewer
{
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
;
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
;
84 * Constructs a new density viewer.
87 * the parent of the viewer
89 public AbstractSegmentStoreDensityViewer(Composite 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
);
100 fDragZoomProvider
= new MouseDragZoomProvider(this);
101 fDragZoomProvider
.register();
102 fDragProvider
= new MouseSelectionProvider(this);
103 fDragProvider
.register();
104 fTooltipProvider
= new SimpleTooltipProvider(this);
105 fTooltipProvider
.register();
109 * Returns the segment store analysis module
112 * The trace to consider
113 * @return the analysis module
115 protected @Nullable abstract AbstractSegmentStoreAnalysisModule
getSegmentStoreAnalysisModule(ITmfTrace trace
);
118 private static ITmfTrace
getTrace() {
119 return TmfTraceManager
.getInstance().getActiveTrace();
122 private void updateDisplay(List
<ISegment
> data
) {
123 if (data
.isEmpty()) {
126 IBarSeries series
= (IBarSeries
) fChart
.getSeriesSet().createSeries(SeriesType
.BAR
, Messages
.AbstractSegmentStoreDensityViewer_SeriesLabel
);
127 series
.setVisible(true);
128 series
.setBarPadding(0);
130 series
.setBarColor(new Color(Display
.getDefault(), BAR_COLOR
));
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());
144 double timeWidth
= (double) maxLength
/ (double) width
;
145 for (int i
= 0; i
< width
; i
++) {
146 xOrigSeries
[i
] = i
* timeWidth
;
148 double maxY
= Double
.MIN_VALUE
;
149 for (int i
= 0; i
< width
; i
++) {
150 maxY
= Math
.max(maxY
, yOrigSeries
[i
]);
152 if (minX
== maxLength
) {
156 series
.setYSeries(yOrigSeries
);
157 series
.setXSeries(xOrigSeries
);
158 final IAxis xAxis
= fChart
.getAxisSet().getXAxis(0);
160 * adjustrange appears to bring origin back since we pad the series with
161 * 0s, not interesting.
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);
175 public Chart
getControl() {
180 * Select a range of latency durations in the viewer.
182 * @param durationRange
183 * a range of latency durations
185 public void select(Range durationRange
) {
186 computeDataAsync(fCurrentTimeRange
, durationRange
).thenAccept((data
) -> {
187 for (ISegmentStoreDensityViewerDataListener listener
: fListeners
) {
188 listener
.dataSelectionChanged(data
);
194 * Zoom to a range of latency durations in the viewer.
196 * @param durationRange
197 * a range of latency durations
199 public void zoom(Range durationRange
) {
200 computeDataAsync(fCurrentTimeRange
, durationRange
).thenAccept((data
) -> applyData(data
));
203 private CompletableFuture
<List
<ISegment
>> computeDataAsync(final TmfTimeRange timeRange
, final Range durationRange
) {
204 return CompletableFuture
.supplyAsync(() -> computeData(timeRange
, durationRange
));
207 private @Nullable List
<ISegment
> computeData(final TmfTimeRange timeRange
, final Range durationRange
) {
208 final AbstractSegmentStoreAnalysisModule analysisModule
= fAnalysisModule
;
209 if (analysisModule
== null) {
212 final ISegmentStore
<ISegment
> results
= analysisModule
.getResults();
213 if (results
== null) {
217 Iterator
<ISegment
> intersectingElements
= results
.getIntersectingElements(timeRange
.getStartTime().getValue(), timeRange
.getEndTime().getValue()).iterator();
219 if (durationRange
.lower
> Double
.MIN_VALUE
|| durationRange
.upper
< Double
.MAX_VALUE
) {
220 Predicate
<?
super ISegment
> predicate
= new Predicate
<ISegment
>() {
222 public boolean apply(@Nullable ISegment input
) {
223 return input
!= null && input
.getLength() >= durationRange
.lower
&& input
.getLength() <= durationRange
.upper
;
226 intersectingElements
= Iterators
.<ISegment
> filter(intersectingElements
, predicate
);
229 return Lists
.newArrayList(intersectingElements
);
232 private void applyData(final @Nullable List
<ISegment
> data
) {
234 Collections
.sort(data
, SegmentComparators
.INTERVAL_LENGTH_COMPARATOR
);
235 Display
.getDefault().asyncExec(() -> updateDisplay(data
));
236 for (ISegmentStoreDensityViewerDataListener l
: fListeners
) {
243 * Signal handler for handling of the window range signal.
246 * The {@link TmfWindowRangeUpdatedSignal}
249 public void windowRangeUpdated(@Nullable TmfWindowRangeUpdatedSignal signal
) {
250 if (signal
== null) {
253 ITmfTrace trace
= getTrace();
257 fAnalysisModule
= getSegmentStoreAnalysisModule(trace
);
258 fCurrentTimeRange
= NonNullUtils
.checkNotNull(signal
.getCurrentRange());
259 updateWithRange(fCurrentTimeRange
);
262 private void updateWithRange(final TmfTimeRange range
) {
263 computeDataAsync(range
, new Range(Double
.MIN_VALUE
, Double
.MAX_VALUE
)).thenAccept((data
) -> applyData(data
));
267 public void refresh() {
272 public void dispose() {
273 if (fAnalysisModule
!= null && fListener
!= null) {
274 fAnalysisModule
.removeListener(fListener
);
276 fDragZoomProvider
.deregister();
277 fTooltipProvider
.deregister();
278 fDragProvider
.deregister();
283 * Signal handler for handling of the trace opened signal.
286 * The trace opened signal {@link TmfTraceOpenedSignal}
289 public void traceOpened(TmfTraceOpenedSignal signal
) {
290 fTrace
= signal
.getTrace();
291 loadTrace(getTrace());
295 * Signal handler for handling of the trace selected signal.
298 * The trace selected signal {@link TmfTraceSelectedSignal}
301 public void traceSelected(TmfTraceSelectedSignal signal
) {
302 if (fTrace
!= signal
.getTrace()) {
303 fTrace
= signal
.getTrace();
304 loadTrace(getTrace());
309 * Signal handler for handling of the trace closed signal.
312 * The trace closed signal {@link TmfTraceClosedSignal}
315 public void traceClosed(TmfTraceClosedSignal signal
) {
317 if (signal
.getTrace() != fTrace
) {
326 * A Method to load a trace into the viewer.
329 * A trace to apply in the viewer
331 protected void loadTrace(@Nullable ITmfTrace trace
) {
335 TmfTraceContext ctx
= TmfTraceManager
.getInstance().getCurrentTraceContext();
336 TmfTimeRange windowRange
= ctx
.getWindowRange();
337 fCurrentTimeRange
= windowRange
;
340 fAnalysisModule
= getSegmentStoreAnalysisModule(trace
);
341 final AbstractSegmentStoreAnalysisModule module
= fAnalysisModule
;
342 if (module
!= null) {
343 fListener
= (activeAnalysis
, data
) -> updateWithRange(windowRange
);
344 module
.addListener(fListener
);
348 zoom(new Range(0, Long
.MAX_VALUE
));
352 * Clears the view content.
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());
362 for (IAxis axis
: chart
.getAxisSet().getAxes()) {
363 axis
.setRange(new Range(0, 1));
370 * Add a data listener.
372 * @param dataListener
373 * the data listener to add
375 public void addDataListener(ISegmentStoreDensityViewerDataListener dataListener
) {
376 fListeners
.add(dataListener
);
380 * Remove a data listener.
382 * @param dataListener
383 * the data listener to remove
385 public void removeDataListener(ISegmentStoreDensityViewerDataListener dataListener
) {
386 fListeners
.remove(dataListener
);