timing: Make scatter viewer update only once
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.timing.ui / src / org / eclipse / tracecompass / analysis / timing / ui / views / segmentstore / scatter / AbstractSegmentStoreScatterGraphViewer.java
CommitLineData
03c96217 1/******************************************************************************
edded5c1 2 * Copyright (c) 2015, 2016 Ericsson
03c96217
BH
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 * Contributors:
10 * France Lapointe Nguyen - Initial API and implementation
11 * Bernd Hufmann - Extracted abstract class from LatencyScatterGraphViewer
12 *******************************************************************************/
13
edded5c1 14package org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.scatter;
03c96217 15
21c917fe 16import java.text.Format;
03c96217
BH
17import java.util.ArrayList;
18import java.util.Collection;
19import java.util.Collections;
20import java.util.Iterator;
21import java.util.List;
e18d40d0 22import java.util.concurrent.atomic.AtomicInteger;
03c96217
BH
23
24import org.eclipse.core.runtime.IProgressMonitor;
25import org.eclipse.core.runtime.IStatus;
26import org.eclipse.core.runtime.Status;
27import org.eclipse.core.runtime.jobs.Job;
28import org.eclipse.jdt.annotation.Nullable;
03c96217
BH
29import org.eclipse.swt.widgets.Composite;
30import org.eclipse.swt.widgets.Display;
03c96217 31import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener;
0f769d2b 32import org.eclipse.tracecompass.analysis.timing.core.segmentstore.ISegmentStoreProvider;
21c917fe 33import org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.SubSecondTimeWithUnitFormat;
03c96217 34import org.eclipse.tracecompass.internal.analysis.timing.ui.Activator;
edded5c1 35import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.scatter.Messages;
21c917fe 36import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.scatter.SegmentStoreScatterGraphTooltipProvider;
03c96217
BH
37import org.eclipse.tracecompass.segmentstore.core.ISegment;
38import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
39import org.eclipse.tracecompass.segmentstore.core.SegmentComparators;
0f769d2b 40import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
03c96217 41import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
03c96217
BH
42import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
43import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
44import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
03c96217
BH
45import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
46import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
47import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
03c96217 48import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.linecharts.TmfCommonXLineChartViewer;
03c96217
BH
49import org.swtchart.ILineSeries;
50import org.swtchart.ILineSeries.PlotSymbolType;
51import org.swtchart.ISeries.SeriesType;
52import org.swtchart.ISeriesSet;
53import org.swtchart.LineStyle;
85ca1cfe
MK
54
55import com.google.common.primitives.Doubles;
03c96217
BH
56
57/**
0f769d2b 58 * Displays the segment store provider data in a scatter graph
03c96217
BH
59 *
60 * @author France Lapointe Nguyen
61 * @author Matthew Khouzam - reduced memory usage
62 * @since 2.0
63 */
64public abstract class AbstractSegmentStoreScatterGraphViewer extends TmfCommonXLineChartViewer {
03c96217 65
21c917fe
MK
66 private static final Format FORMAT = new SubSecondTimeWithUnitFormat();
67
e18d40d0
GB
68 private final AtomicInteger fDirty = new AtomicInteger();
69
03c96217
BH
70 private final class CompactingSegmentStoreQuery extends Job {
71 private static final long MAX_POINTS = 1000;
edb7c563
GB
72 private final long fStart;
73 private final long fEnd;
03c96217 74
edb7c563 75 private CompactingSegmentStoreQuery(long start, long end) {
03c96217 76 super(Messages.SegmentStoreScatterGraphViewer_compactTitle);
edb7c563
GB
77 fStart = start;
78 fEnd = end;
03c96217
BH
79 }
80
81 @Override
82 protected IStatus run(@Nullable IProgressMonitor monitor) {
83 final IProgressMonitor statusMonitor = monitor;
e18d40d0
GB
84 try {
85 if (statusMonitor == null) {
86 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Monitor is null"); //$NON-NLS-1$
87 }
03c96217 88
e18d40d0 89 ISegmentStoreProvider segmentProvider = getSegmentProvider();
edb7c563
GB
90 final long startTime = fStart;
91 final long endTime = fEnd;
e18d40d0 92 if (segmentProvider == null) {
edb7c563 93 redraw(statusMonitor, startTime, startTime, Collections.EMPTY_LIST);
e18d40d0
GB
94 return new Status(IStatus.WARNING, Activator.PLUGIN_ID, "segment provider not available"); //$NON-NLS-1$
95 }
03c96217 96
e18d40d0
GB
97 final ISegmentStore<ISegment> segStore = segmentProvider.getSegmentStore();
98 if (segStore == null) {
edb7c563 99 redraw(statusMonitor, startTime, startTime, Collections.EMPTY_LIST);
e18d40d0
GB
100 return new Status(IStatus.INFO, Activator.PLUGIN_ID, "Segment provider does not have segments"); //$NON-NLS-1$
101 }
03c96217 102
e18d40d0
GB
103 fPixelStart = startTime;
104 fPixelSize = Math.max(1, (endTime - startTime) / MAX_POINTS);
105 final Iterable<ISegment> intersectingElements = segStore.getIntersectingElements(startTime, endTime);
e18d40d0
GB
106 final List<ISegment> list = convertIterableToList(intersectingElements, statusMonitor);
107 final List<ISegment> displayData = (!list.isEmpty()) ? compactList(startTime, list, statusMonitor) : list;
03c96217 108
e18d40d0 109 redraw(statusMonitor, startTime, endTime, displayData);
03c96217 110
e18d40d0
GB
111 if (statusMonitor.isCanceled()) {
112 return Status.CANCEL_STATUS;
113 }
114 return Status.OK_STATUS;
115 } finally {
116 /*
117 * fDirty should have been incremented before creating a job, so
118 * we decrement it once the job is done
119 */
120 fDirty.decrementAndGet();
03c96217 121 }
03c96217
BH
122
123 }
124
125 private void redraw(final IProgressMonitor statusMonitor, final long startTime, final long endTime, final List<ISegment> displayData) {
126 fDisplayData = displayData;
e18d40d0
GB
127 /*
128 * Increment at every redraw, since the content of the view is not
129 * current
130 */
131 fDirty.incrementAndGet();
03c96217
BH
132 Display.getDefault().asyncExec(new Runnable() {
133
134 @Override
135 public void run() {
e18d40d0
GB
136 try {
137 updateData(startTime, endTime, displayData.size(), statusMonitor);
138 } finally {
139 /* Decrement once the redraw is done */
140 fDirty.decrementAndGet();
141 }
03c96217
BH
142 }
143 });
144 }
145
146 private List<ISegment> compactList(final long startTime, final List<ISegment> listToCompact, final IProgressMonitor statusMonitor) {
147 List<ISegment> displayData = new ArrayList<>();
148 ISegment last = listToCompact.get(0);
149 if (last.getStart() >= startTime) {
150 displayData.add(last);
151 }
152 for (ISegment next : listToCompact) {
153 if (next.getStart() < startTime) {
154 continue;
155 }
156 if (statusMonitor.isCanceled()) {
df2597e0 157 return Collections.EMPTY_LIST;
03c96217
BH
158 }
159 if (!overlaps(last, next)) {
160 displayData.add(next);
161 last = next;
162 }
163 }
164 return displayData;
165 }
166
167 private List<ISegment> convertIterableToList(final Iterable<ISegment> iterable, final IProgressMonitor statusMonitor) {
168 final List<ISegment> list = new ArrayList<>();
169 for (ISegment seg : iterable) {
170 if (statusMonitor.isCanceled()) {
df2597e0 171 return Collections.EMPTY_LIST;
03c96217
BH
172 }
173 list.add(seg);
174 }
175 Collections.sort(list, SegmentComparators.INTERVAL_START_COMPARATOR);
176 return list;
177 }
178
179 private boolean overlaps(ISegment last, ISegment next) {
180 long timePerPix = fPixelSize;
181 final long start = last.getStart();
182 final long pixelStart = fPixelStart;
183 final long pixelDuration = start - pixelStart;
184 long startPixBoundL = pixelDuration / timePerPix * timePerPix + pixelStart;
185 long startPixBoundR = startPixBoundL + timePerPix;
186 final long currentStart = next.getStart();
187 if (currentStart >= startPixBoundL && currentStart <= startPixBoundR) {
188 long length = last.getLength();
189 long lengthNext = next.getLength();
190 long lengthLow = length / timePerPix * timePerPix;
191 long lengthHigh = lengthLow + timePerPix;
192 return (lengthNext >= lengthLow && lengthNext <= lengthHigh);
193 }
194 return false;
195 }
196 }
197
198 // ------------------------------------------------------------------------
199 // Attributes
200 // ------------------------------------------------------------------------
201
202 /**
0f769d2b
MK
203 * Listener to update the model with the segment store provider results once
204 * its segment store is fully completed
03c96217 205 */
0f769d2b 206 private final class SegmentStoreProviderProgressListener implements IAnalysisProgressListener {
03c96217
BH
207
208 @Override
0f769d2b 209 public void onComplete(ISegmentStoreProvider segmentProvider, ISegmentStore<ISegment> segmentStore) {
03c96217 210 // Only update the model if trace that was analyzed is active trace
0f769d2b
MK
211 if (segmentProvider.equals(getSegmentProvider())) {
212 updateModel(segmentStore);
03c96217
BH
213 }
214 }
215 }
216
217 private long fPixelSize = -1;
218
219 private long fPixelStart = 0;
220 /**
221 * Data to display
222 */
df2597e0 223 private Collection<ISegment> fDisplayData = Collections.EMPTY_LIST;
03c96217
BH
224
225 /**
0f769d2b 226 * Provider completion listener
03c96217 227 */
0f769d2b 228 private SegmentStoreProviderProgressListener fListener;
03c96217
BH
229
230 /**
0f769d2b 231 * Current segment provider
03c96217 232 */
0f769d2b 233 private @Nullable ISegmentStoreProvider fSegmentProvider;
03c96217
BH
234
235 private @Nullable Job fCompactingJob;
236
237 // ------------------------------------------------------------------------
238 // Constructor
239 // ------------------------------------------------------------------------
240
241 /**
242 * Constructor
243 *
244 * @param parent
245 * parent composite
246 * @param title
247 * name of the graph
248 * @param xLabel
249 * name of the x axis
250 * @param yLabel
251 * name of the y axis
252 */
253 public AbstractSegmentStoreScatterGraphViewer(Composite parent, String title, String xLabel, String yLabel) {
254 super(parent, title, xLabel, yLabel);
a2de198a 255 setTooltipProvider(new SegmentStoreScatterGraphTooltipProvider(this));
0f769d2b 256 fListener = new SegmentStoreProviderProgressListener();
03c96217 257 ITmfTrace trace = TmfTraceManager.getInstance().getActiveTrace();
0f769d2b 258 initializeProvider(trace);
03c96217 259 getSwtChart().getLegend().setVisible(false);
21c917fe 260 getSwtChart().getAxisSet().getYAxis(0).getTick().setFormat(FORMAT);
03c96217
BH
261 }
262
0f769d2b 263 private final void initializeProvider(@Nullable ITmfTrace trace) {
03c96217 264 if (trace != null) {
0f769d2b
MK
265 final ISegmentStoreProvider segmentStoreProvider = getSegmentStoreProvider(trace);
266 if (segmentStoreProvider != null) {
267 segmentStoreProvider.addListener(fListener);
268 setData(segmentStoreProvider);
03c96217
BH
269 }
270 }
271 }
272
273 // ------------------------------------------------------------------------
274 // Operations
275 // ------------------------------------------------------------------------
276
277 /**
278 * Update the data in the graph
279 *
280 * @param dataInput
281 * new model
282 */
283 public void updateModel(@Nullable ISegmentStore<ISegment> dataInput) {
284 // Update new window range
285 TmfTimeRange currentRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
16801c72
MK
286 long currentStart = currentRange.getStartTime().toNanos();
287 long currentEnd = currentRange.getEndTime().toNanos();
03c96217
BH
288 if (dataInput == null) {
289 if (!getDisplay().isDisposed()) {
290 Display.getDefault().syncExec(new Runnable() {
291 @Override
292 public void run() {
293 clearContent();
294 }
295 });
296 }
df2597e0 297 fDisplayData = Collections.EMPTY_LIST;
03c96217
BH
298 } else {
299 Collection<ISegment> elements = (Collection<ISegment>) dataInput.getIntersectingElements(currentStart, currentEnd);
300 // getIntersectingElements can return an unsorted iterable, make
301 // sure our collection is sorted
302 ArrayList<ISegment> list = new ArrayList<>(elements);
303 Collections.sort(list, SegmentComparators.INTERVAL_START_COMPARATOR);
304 fDisplayData = list;
305 }
306 setWindowRange(currentStart, currentEnd);
edb7c563 307 updateContent();
03c96217
BH
308 }
309
310 @Override
311 protected void initializeDataSource() {
312 ITmfTrace trace = getTrace();
0f769d2b 313 initializeProvider(trace);
03c96217 314 if (trace != null) {
0f769d2b 315 setData(getSegmentStoreProvider(trace));
03c96217
BH
316 }
317 }
318
319 @Override
320 protected void updateData(final long start, final long end, int nb, @Nullable IProgressMonitor monitor) {
321 // Third parameter is not used by implementation
322 // Determine data that needs to be visible
323 Collection<ISegment> data = fDisplayData;
324
325 final int dataSize = (nb == 0) ? data.size() : nb;
44af9da9 326 if (end == start) {
03c96217
BH
327 return;
328 }
329
85ca1cfe
MK
330 List<Double> xSeries = new ArrayList<>(dataSize);
331 List<Double> ySeries = new ArrayList<>(dataSize);
03c96217
BH
332 // For each visible segments, add start time to x value and duration
333 // for y value
334 Iterator<ISegment> modelIter = data.iterator();
85ca1cfe
MK
335 while (modelIter.hasNext()) {
336 ISegment segment = modelIter.next();
337 xSeries.add((double) (segment.getStart() - start));
338 ySeries.add((double) segment.getLength());
03c96217 339 }
85ca1cfe 340 setXAxis(Doubles.toArray(xSeries));
44af9da9 341 setSeries(Messages.SegmentStoreScatterGraphViewer_legend, Doubles.toArray(ySeries));
85ca1cfe 342 updateDisplay();
03c96217
BH
343 }
344
345 @Override
346 protected void setWindowRange(final long windowStartTime, final long windowEndTime) {
347 super.setWindowRange(windowStartTime, windowEndTime);
348 }
349
350 @Override
351 protected ILineSeries addSeries(@Nullable String seriesName) {
352 ISeriesSet seriesSet = getSwtChart().getSeriesSet();
353 ILineSeries series = (ILineSeries) seriesSet.createSeries(SeriesType.LINE, seriesName);
354 series.setVisible(true);
355 series.enableArea(false);
356 series.setLineStyle(LineStyle.NONE);
357 series.setSymbolType(PlotSymbolType.DIAMOND);
358 return series;
359 }
360
361 /**
0f769d2b
MK
362 * Set the data into the viewer. If the provider is an analysis, it will
363 * update the model if the analysis is completed or run the analysis if not
364 * completed
03c96217 365 *
0f769d2b
MK
366 * @param provider
367 * Segment store provider
03c96217 368 */
0f769d2b
MK
369 public void setData(@Nullable ISegmentStoreProvider provider) {
370 if (provider == null) {
03c96217
BH
371 updateModel(null);
372 return;
373 }
0f769d2b
MK
374 ISegmentStore<ISegment> segStore = provider.getSegmentStore();
375 // If results are not null, then segment store is completed and model
376 // can be updated
73c74de7
JCK
377 if (segStore != null) {
378 updateModel(segStore);
0f769d2b 379 setSegmentProvider(provider);
03c96217
BH
380 return;
381 }
382 updateModel(null);
0f769d2b
MK
383 provider.addListener(fListener);
384 if (provider instanceof IAnalysisModule) {
385 ((IAnalysisModule) provider).schedule();
386 }
387 setSegmentProvider(provider);
03c96217
BH
388 }
389
390 /**
0f769d2b 391 * Returns the segment store provider
df2597e0 392 *
03c96217
BH
393 * @param trace
394 * The trace to consider
0f769d2b 395 * @return the segment store provider
03c96217 396 */
0f769d2b 397 protected @Nullable abstract ISegmentStoreProvider getSegmentStoreProvider(ITmfTrace trace);
03c96217 398
03c96217
BH
399 // ------------------------------------------------------------------------
400 // Signal handlers
401 // ------------------------------------------------------------------------
402
403 /**
404 * @param signal
405 * Signal received when a different trace is selected
406 */
407 @Override
408 @TmfSignalHandler
409 public void traceSelected(@Nullable TmfTraceSelectedSignal signal) {
410 super.traceSelected(signal);
411 if (signal == null) {
412 return;
413 }
414 ITmfTrace trace = signal.getTrace();
415 setTrace(trace);
416 if (trace != null) {
417 final TmfTimeRange timeRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
418 setWindowRange(
16801c72
MK
419 timeRange.getStartTime().toNanos(),
420 timeRange.getEndTime().toNanos());
0f769d2b 421 setData(getSegmentStoreProvider(trace));
edb7c563 422 updateContent();
03c96217
BH
423 }
424 }
425
426 /**
427 * @param signal
428 * Signal received when trace is opened
429 */
430 @Override
431 @TmfSignalHandler
432 public void traceOpened(@Nullable TmfTraceOpenedSignal signal) {
433 super.traceOpened(signal);
434 if (signal == null) {
435 return;
436 }
437 ITmfTrace trace = signal.getTrace();
438 setTrace(trace);
439 if (trace != null) {
440
0f769d2b 441 final ISegmentStoreProvider segmentStoreProvider = getSegmentStoreProvider(trace);
03c96217
BH
442 final TmfTimeRange timeRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
443 setWindowRange(
16801c72
MK
444 timeRange.getStartTime().toNanos(),
445 timeRange.getEndTime().toNanos());
0f769d2b 446 setData(segmentStoreProvider);
03c96217
BH
447 }
448
449 }
450
edb7c563
GB
451 @Override
452 protected void updateContent() {
e18d40d0 453 /*
edb7c563 454 * Update is requested, content is not up to date, fDirty will be
e18d40d0
GB
455 * decremented in the compacting job
456 */
457 fDirty.incrementAndGet();
03c96217
BH
458 Job compactingJob = fCompactingJob;
459 if (compactingJob != null && compactingJob.getState() == Job.RUNNING) {
460 compactingJob.cancel();
461 }
edb7c563 462 compactingJob = new CompactingSegmentStoreQuery(getWindowStartTime(), getWindowEndTime());
03c96217
BH
463 fCompactingJob = compactingJob;
464 compactingJob.schedule();
465 }
466
467 /**
468 * @param signal
469 * Signal received when last opened trace is closed
470 */
471 @Override
472 @TmfSignalHandler
473 public void traceClosed(@Nullable TmfTraceClosedSignal signal) {
474 super.traceClosed(signal);
475 if (signal != null) {
476 // Check if there is no more opened trace
477 if (TmfTraceManager.getInstance().getActiveTrace() == null) {
0f769d2b
MK
478 ISegmentStoreProvider provider = getSegmentProvider();
479 if (provider != null) {
480 provider.removeListener(fListener);
03c96217
BH
481 }
482 clearContent();
483 }
484 }
485 refresh();
486 }
487
0f769d2b
MK
488 private @Nullable ISegmentStoreProvider getSegmentProvider() {
489 return fSegmentProvider;
03c96217
BH
490 }
491
0f769d2b
MK
492 private void setSegmentProvider(ISegmentStoreProvider provider) {
493 fSegmentProvider = provider;
03c96217 494 }
e18d40d0
GB
495
496 @Override
497 public boolean isDirty() {
498 /* Check the parent's or this view's own dirtiness */
499 return super.isDirty() || (fDirty.get() != 0);
500 }
03c96217 501}
This page took 0.063177 seconds and 5 git commands to generate.