From 63c0c39c85476605f5c97ac240613b0ef8194615 Mon Sep 17 00:00:00 2001 From: Will Rogers Date: Tue, 25 Sep 2018 09:26:17 +0100 Subject: [PATCH 1/7] Avoid invalid thread access exceptions. --- .../trends/databrowser2/editor/DataBrowserEditor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/editor/DataBrowserEditor.java b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/editor/DataBrowserEditor.java index 73aa18cc54d..fa9ac09a8fe 100644 --- a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/editor/DataBrowserEditor.java +++ b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/editor/DataBrowserEditor.java @@ -275,7 +275,9 @@ public void scrollEnabled(final boolean scroll_enabled) @Override public void changedAnnotations() - { setDirty(true); } + { + site.getShell().getDisplay().asyncExec(() -> setDirty(true)); + } }; model.addListener(model_listener); } From 9eb17c1215f90b2813589d6e8e53506fbf655ba4 Mon Sep 17 00:00:00 2001 From: Will Rogers Date: Tue, 25 Sep 2018 09:30:20 +0100 Subject: [PATCH 2/7] Use new generic getAdapter() method to avoid warnings. --- .../trends/databrowser2/editor/DataBrowserEditor.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/editor/DataBrowserEditor.java b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/editor/DataBrowserEditor.java index fa9ac09a8fe..50df3e1a3e2 100644 --- a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/editor/DataBrowserEditor.java +++ b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/editor/DataBrowserEditor.java @@ -283,12 +283,11 @@ public void changedAnnotations() } /** Provide custom property sheet for this editor */ - @SuppressWarnings("rawtypes") @Override - public Object getAdapter(final Class adapter) + public T getAdapter(final Class adapter) { if (adapter == IPropertySheetPage.class) - return new DataBrowserPropertySheetPage(model, plot.getPlot().getUndoableActionManager()); + return adapter.cast(new DataBrowserPropertySheetPage(model, plot.getPlot().getUndoableActionManager())); return super.getAdapter(adapter); } From 13d9c9b88161d2718e28c207e6590930969c2224 Mon Sep 17 00:00:00 2001 From: Will Rogers Date: Tue, 25 Sep 2018 12:03:13 +0100 Subject: [PATCH 3/7] Avoid null values for empty lists. Rename methods to better reflect multiple waveforms. --- .../waveformview/WaveformView.java | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java index 3f7140bb176..645786abef8 100644 --- a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java +++ b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java @@ -95,7 +95,7 @@ public class WaveformView extends DataBrowserAwareView private Model model; /** Annotation(s) in data browser plot that indicate waveform sample(s) */ - private List waveform_annotations; + private List waveform_annotations = new ArrayList<>(); private boolean changing_annotations = false; @@ -153,17 +153,16 @@ public void changedAnnotations() public void changedTimerange() { // Update selected sample to assert that it's one of the visible ones. - if (model_items != null && waveforms != null) - if (waveforms.size() > 0) - showSelectedSample(); + if (waveforms.size() > 0) + showSelectedSample(); } }; - /** Selected model item in model, or null */ - private List model_items = null; + /** Selected model items in model. */ + private List model_items = new ArrayList<>(); - /** Waveform for the currently selected sample */ - private List waveforms = null; + /** Waveforms for the currently selected samples */ + private List waveforms = new ArrayList<>(); /** {@inheritDoc} */ @Override @@ -175,7 +174,7 @@ protected void doCreatePartControl(final Composite parent) if (model != null) { model.removeListener(model_listener); - removeAnnotation(); + removeAnnotations(); } }); @@ -200,7 +199,7 @@ protected void doCreatePartControl(final Composite parent) pv_select.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { - selectPV(pv_select.getSelection()); + selectPVs(pv_select.getSelection()); } }); @@ -213,7 +212,7 @@ public void propertyChange(PropertyChangeEvent evt) { @Override public void widgetSelected(final SelectionEvent e) { - selectPV(pv_select.getSelection()); + selectPVs(pv_select.getSelection()); } }); @@ -304,7 +303,7 @@ protected void updateModel(final Model old_model, final Model model) { if (this.model == model) return; - removeAnnotation(); + removeAnnotations(); this.model = model; if (old_model != model) { @@ -332,7 +331,7 @@ private void update(final boolean model_changed) pv_select.setItems(getModelItems()); if (model == null) { pv_select.setEnabled(false); - selectPV(null); + selectPVs(null); return; } @@ -344,36 +343,39 @@ private void update(final boolean model_changed) newSelection.add(oldItem); } pv_select.setSelection(newSelection); - selectPV(oldSelection); + selectPVs(oldSelection); }); } - /** Select given PV item (or null). */ - private void selectPV(final List new_item) + /** Select given PV items. */ + private void selectPVs(final List new_items) { // Delete all existing traces for (Trace trace : plot.getTraces()) plot.removeTrace(trace); - model_items = new_item; + if (new_items == null) { + model_items.clear(); + } else { + model_items = new_items; + } - removeAnnotation(); + removeAnnotations(); - if (model_items == null || model_items.size() == 0) + if (model_items.isEmpty()) { sample_index.setEnabled(false); return; } // Prepare to show waveforms of model item in plot - + waveforms.clear(); // Create trace for waveform - waveforms = new ArrayList(); - for(int n=0; n modelAnnotations = new ArrayList(model.getAnnotations()); for (AnnotationInfo waveform_annotation : waveform_annotations) { @@ -513,13 +513,11 @@ private void removeAnnotation() } } } - waveform_annotations = null; + waveform_annotations.clear(); } private void updateAnnotation(final int annotation_index, final Instant time, final double value) { - if (waveform_annotations == null) - waveform_annotations = new ArrayList(); final List annotations = new ArrayList(model.getAnnotations()); // Initial annotation offset @@ -547,6 +545,8 @@ private void updateAnnotation(final int annotation_index, final Instant time, fi } i++; } + if (waveform_annotations.size() > annotation_index) + waveform_annotations.remove(annotation_index); waveform_annotations.add(annotation_index, new AnnotationInfo(true, item_index, time, value, offset, buildAnnotationText(annotation_index))); annotations.add(waveform_annotations.get(annotation_index)); changing_annotations = true; From 7fb7bb5b8a8db587499fda7ef22c604701889cf2 Mon Sep 17 00:00:00 2001 From: Will Rogers Date: Wed, 26 Sep 2018 16:05:24 +0100 Subject: [PATCH 4/7] Use merged PlotSampleArray for defining the slider in WaveformView. This gives better all-round behaviour. You can now drag any annotation and the others will update to the closest sample before the newly- selected timestamp. Some indexing errors are fixed, so that if you press the Refresh button you should get all the points currently visible. Use PlotSampleArray for timebase. This simplifies some code. --- .../swt/rtplot/data/TimeDataSearch.java | 2 +- .../waveformview/WaveformView.java | 107 ++++++++++++------ 2 files changed, 73 insertions(+), 36 deletions(-) diff --git a/applications/appunorganized/appunorganized-plugins/org.csstudio.swt.rtplot/src/org/csstudio/swt/rtplot/data/TimeDataSearch.java b/applications/appunorganized/appunorganized-plugins/org.csstudio.swt.rtplot/src/org/csstudio/swt/rtplot/data/TimeDataSearch.java index 4824e474b2b..c7cd5eb1ef9 100644 --- a/applications/appunorganized/appunorganized-plugins/org.csstudio.swt.rtplot/src/org/csstudio/swt/rtplot/data/TimeDataSearch.java +++ b/applications/appunorganized/appunorganized-plugins/org.csstudio.swt.rtplot/src/org/csstudio/swt/rtplot/data/TimeDataSearch.java @@ -15,7 +15,7 @@ */ public class TimeDataSearch extends PlotDataSearch { - /** Find a sample that's bigger or equal to given value + /** Find the sample closest to given value * @param data Data, must already be locked * @param time Time near which to look for sample. * @return Returns index of sample closest to time diff --git a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java index 645786abef8..3997910c014 100644 --- a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java +++ b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java @@ -11,6 +11,7 @@ import java.beans.PropertyChangeListener; import java.time.Instant; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -34,6 +35,7 @@ import org.csstudio.trends.databrowser2.model.ModelListener; import org.csstudio.trends.databrowser2.model.ModelListenerAdapter; import org.csstudio.trends.databrowser2.model.PlotSample; +import org.csstudio.trends.databrowser2.model.PlotSampleArray; import org.csstudio.trends.databrowser2.model.PlotSamples; import org.csstudio.ui.util.widgets.MultipleSelectionCombo; import org.diirt.vtype.VNumberArray; @@ -153,14 +155,59 @@ public void changedAnnotations() public void changedTimerange() { // Update selected sample to assert that it's one of the visible ones. - if (waveforms.size() > 0) + if (waveforms.size() > 0) { + Instant selectedInstant = Instant.MIN; + int selectedIndex = sample_index.getSelection(); + if (selectedIndex < waveform_samples.size()) + selectedInstant = waveform_samples.get(sample_index.getSelection()).getPosition(); + updateTimestamps(); + sample_index.setMaximum(waveform_samples.size()); + int newSelectedIndex = new TimeDataSearch().findClosestSample(waveform_samples, selectedInstant); + // If the timestamp wasn't found, it should be outside of the new timerange. + // Put the slider at the start or end as appropriate. + if (newSelectedIndex == -1) { + if (waveform_samples.size() == 0 || waveform_samples.get(0).getPosition().compareTo(selectedInstant) > 0) { + newSelectedIndex = 0; + } else if (waveform_samples.get(waveform_samples.size() - 1).getPosition().compareTo(selectedInstant) < 0) { + newSelectedIndex = waveform_samples.size() - 1; + } + } + sample_index.setSelection(newSelectedIndex); showSelectedSample(); + } } }; /** Selected model items in model. */ private List model_items = new ArrayList<>(); + /** Merged ordered list of all timestamps of all PlotSamples in all items. */ + private PlotSampleArray waveform_samples = new PlotSampleArray(); + + /** + * Take all the samples from all model items that are within the plot range, + * and sort them. The timestamps provide the index for the slider. + */ + private void updateTimestamps() { + List samples = new ArrayList<>(); + Instant plot_start = model.getStartTime(); + Instant plot_end = model.getEndTime(); + if (model_items != null) { + for (ModelItem item : model_items) { + for (int i = 0; i < item.getSamples().size(); i++) { + PlotSample sample = item.getSamples().get(i); + if (sample.getPosition().isAfter(plot_start) && sample.getPosition().isBefore(plot_end)) { + samples.add(sample); + } + } + } + } + Collections.sort(samples, (s1, s2) -> { + return s1.getPosition().compareTo(s2.getPosition()); + }); + waveform_samples.set(samples); + } + /** Waveforms for the currently selected samples */ private List waveforms = new ArrayList<>(); @@ -230,9 +277,12 @@ public void widgetSelected(final SelectionEvent e) // <<<<<< Slider >>>>>> sample_index = new Slider(parent, SWT.HORIZONTAL); sample_index.setToolTipText(Messages.WaveformTimeSelector); + // It's important for indexing that the slider 'thumb' takes up only one data point. + sample_index.setThumb(1); sample_index.setLayoutData(new GridData(SWT.FILL, 0, true, false, layout.numColumns, 1)); sample_index.addSelectionListener(new SelectionAdapter() { + @Override public void widgetSelected(SelectionEvent e) { @@ -361,6 +411,7 @@ private void selectPVs(final List new_items) } else { model_items = new_items; } + updateTimestamps(); removeAnnotations(); @@ -389,35 +440,26 @@ private void showSelectedSample() { final int numItems = model_items.size(); - final int idx = sample_index.getSelection(); - String timestampText = null; String statusText = null; + sample_index.setMaximum(waveform_samples.size()); - Instant firstWaveformSampleTime = null; + if (sample_index.getSelection() >= waveform_samples.size()) { + // Rapid update where there aren't enough timestamps for the current slider + return; + } + Instant sliderTime = waveform_samples.get(sample_index.getSelection()).getPosition(); for(int n=0; n { - sample_index.setSelection(idx); - showSelectedSample(); + if (! (idx == sample_index.getSelection())) { + sample_index.setSelection(idx); + showSelectedSample(); + } }); - return; } } } From 79e4283749a3bcc1575e8b0c5171ed6ae0e6f07c Mon Sep 17 00:00:00 2001 From: Will Rogers Date: Thu, 27 Sep 2018 14:29:56 +0100 Subject: [PATCH 5/7] Make variables final where possible. --- .../trends/databrowser2/waveformview/WaveformView.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java index 3997910c014..71408205056 100644 --- a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java +++ b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java @@ -97,11 +97,11 @@ public class WaveformView extends DataBrowserAwareView private Model model; /** Annotation(s) in data browser plot that indicate waveform sample(s) */ - private List waveform_annotations = new ArrayList<>(); + final private List waveform_annotations = new ArrayList<>(); private boolean changing_annotations = false; - private final ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor(); + final private ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor(); private ScheduledFuture pending_move = null; @@ -182,7 +182,7 @@ public void changedTimerange() private List model_items = new ArrayList<>(); /** Merged ordered list of all timestamps of all PlotSamples in all items. */ - private PlotSampleArray waveform_samples = new PlotSampleArray(); + final private PlotSampleArray waveform_samples = new PlotSampleArray(); /** * Take all the samples from all model items that are within the plot range, @@ -209,7 +209,7 @@ private void updateTimestamps() { } /** Waveforms for the currently selected samples */ - private List waveforms = new ArrayList<>(); + final private List waveforms = new ArrayList<>(); /** {@inheritDoc} */ @Override From 60acd703b9e456e199e8e90628a4b4270c8e13f8 Mon Sep 17 00:00:00 2001 From: Will Rogers Date: Thu, 27 Sep 2018 14:30:23 +0100 Subject: [PATCH 6/7] Simplify sample selection. --- .../trends/databrowser2/waveformview/WaveformView.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java index 71408205056..592a1c24a96 100644 --- a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java +++ b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java @@ -458,8 +458,9 @@ private void showSelectedSample() samples.getLock().lock(); try { + // Choose the sample with either the matching timestamp or the one after. final int idx = new TimeDataSearch().findSampleGreaterOrEqual(samples, sliderTime); - sample = samples.get(idx - 1); + sample = samples.get(idx); } finally { From 7c5fef70d6a2cc94ee78f82b73aca66840f1d038 Mon Sep 17 00:00:00 2001 From: Will Rogers Date: Thu, 27 Sep 2018 16:39:07 +0100 Subject: [PATCH 7/7] Tidy resetting the waveform view slider. --- .../databrowser2/waveformview/WaveformView.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java index 592a1c24a96..f2103d1e89d 100644 --- a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java +++ b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java @@ -161,7 +161,7 @@ public void changedTimerange() if (selectedIndex < waveform_samples.size()) selectedInstant = waveform_samples.get(sample_index.getSelection()).getPosition(); updateTimestamps(); - sample_index.setMaximum(waveform_samples.size()); + resetSlider(); int newSelectedIndex = new TimeDataSearch().findClosestSample(waveform_samples, selectedInstant); // If the timestamp wasn't found, it should be outside of the new timerange. // Put the slider at the start or end as appropriate. @@ -289,6 +289,7 @@ public void widgetSelected(SelectionEvent e) showSelectedSample(); } }); + resetSlider(); // Timestamp: __________ Sevr./Status: __________ l = new Label(parent, 0); @@ -325,6 +326,16 @@ public void menuAboutToShow(IMenuManager manager) { } + /** + * Ensure the slider has the correct number of options. + */ + private void resetSlider() { + // Setting the maximum to 0 has no effect. Setting the maximum to 1 means + // that there is one option in the slider - i.e. you can't move it. + // If there are no samples we want to set the maximum to 1. + sample_index.setMaximum(waveform_samples.size() == 0 ? 1 : waveform_samples.size()); + } + /** Return List of selected ModelItems * by finding Model Items from checked items in PV list * @param names list of all PV names available in list @@ -442,7 +453,7 @@ private void showSelectedSample() String timestampText = null; String statusText = null; - sample_index.setMaximum(waveform_samples.size()); + resetSlider(); if (sample_index.getSelection() >= waveform_samples.size()) { // Rapid update where there aren't enough timestamps for the current slider