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