gda.rcp.views.scan.AbstractCachedScanPlotView.java Source code

Java tutorial

Introduction

Here is the source code for gda.rcp.views.scan.AbstractCachedScanPlotView.java

Source

/*-
 * Copyright  2013 Diamond Light Source Ltd.
 *
 * This file is part of GDA.
 *
 * GDA is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License version 3 as published by the Free
 * Software Foundation.
 *
 * GDA is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along
 * with GDA. If not, see <http://www.gnu.org/licenses/>.
 */

package gda.rcp.views.scan;

import gda.rcp.util.ScanDataPointEvent;
import gda.scan.IScanDataPoint;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;
import org.eclipse.dawnsci.analysis.dataset.impl.Dataset;
import org.eclipse.dawnsci.analysis.dataset.impl.DoubleDataset;
import org.eclipse.ui.PlatformUI;

import uk.ac.diamond.scisoft.analysis.rcp.views.plot.DataSetPlotData;
import uk.ac.diamond.scisoft.analysis.rcp.views.plot.IPlotData;

/**
 * Extend to deal with plots which are simple functions.
 */
public abstract class AbstractCachedScanPlotView extends AbstractScanPlotView implements Runnable {

    protected List<Double> cachedX, cachedY;
    protected String xAxisTitle;

    // attributes to perform calculations in a separate thread
    private ScanDataPointEvent latestEvent;
    private ScanDataPointEvent lastEventUsedInACalculation;
    private Thread performCalculationsThread = null;
    private volatile boolean continueCalculations = true;
    private String currentScanUniqueName = "";
    private volatile boolean waitingForRefresh = false;

    @Override
    public void scanStopped() {
        //do nothing as this will interfere with the last points coming through
    }

    @Override
    public void scanStarted() {
        super.scanStarted();
        if (cachedX == null)
            cachedX = new ArrayList<Double>(89);
        if (cachedY == null)
            cachedY = new ArrayList<Double>(89);
    }

    @Override
    public void scanDataPointChanged(ScanDataPointEvent e) {

        if (cachedX == null)
            cachedX = new ArrayList<Double>(89);
        if (cachedY == null)
            cachedY = new ArrayList<Double>(89);
        latestEvent = e;
        checkLoopRunning();
    }

    protected void checkLoopRunning() {
        if (performCalculationsThread == null || !performCalculationsThread.isAlive()) {
            performCalculationsThread = new Thread(this, this.getClass().getSimpleName());
            continueCalculations = true;
            performCalculationsThread.start();
        }
    }

    @Override
    public void run() {
        while (continueCalculations) {
            if (updateCachedValues() && !waitingForRefresh) {
                y = getY((IScanDataPoint[]) null);
                x = getX((IScanDataPoint[]) null);
                if (y != null && x != null) {
                    waitingForRefresh = true;
                    PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
                        @Override
                        public void run() {
                            showPlotter();
                            rebuildPlot(); // uses x and y
                            waitingForRefresh = false;
                        }
                    });
                } else if (y == null) {
                    stack.topControl = lblNoDataMessage;
                }
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                break;
            }
        }
    }

    @Override
    public void dispose() {
        continueCalculations = false;
        super.dispose();
    }

    /**
     * Using the latestEvent attribute, update the cachedY and cachedX values.
     * <p>
     * Implementations of this should be aware that the latestEvent attribute will be updated by a different thread so
     * should operate with a copy of the attribute.
     * 
     * @return boolean - true if the cache was updated
     */
    private boolean updateCachedValues() {

        if (lastEventUsedInACalculation != null && latestEvent == lastEventUsedInACalculation) {
            return false;
        }

        // take copies of what we are going to use
        IScanDataPoint sdp = latestEvent.getCurrentPoint();
        ArrayList<IScanDataPoint> allSDPs = (ArrayList<IScanDataPoint>) latestEvent.getDataPoints();

        if (currentScanUniqueName == null || currentScanUniqueName.isEmpty()) {
            currentScanUniqueName = sdp.getUniqueName();
            scanNumber = sdp.getScanIdentifier();
        }

        if (currentScanUniqueName != sdp.getUniqueName()) {
            // only clear at this point - when points from a new scan are coming in
            system.clear();
            cachedX.clear();
            cachedY.clear();
            currentScanUniqueName = sdp.getUniqueName();
            scanNumber = sdp.getScanIdentifier();
        }

        if (!scanning) {
            // somehow missed the scan started event
            scanStarted();
        }
        lastEventUsedInACalculation = latestEvent;

        // assume a new entry to Y for every SDP
        int previousCacheSize = cachedY.size();
        int newCacheSize = allSDPs.size();

        if (previousCacheSize == newCacheSize) {
            // nothing new
            return false;
        }

        int startIndex = 0;
        if (previousCacheSize > 0) {
            startIndex = previousCacheSize - 1;
        }

        if (startIndex == newCacheSize - 1) {
            return false;
        }

        updateCache(allSDPs, startIndex);
        return true;
    }

    /**
     * Using the latest data points, update a local cache of data.
     * <p>
     * It is then expected that the next calls to getX and getY will reflect the latest cache contents.
     * 
     * @param collection
     */
    protected abstract void updateCache(ArrayList<IScanDataPoint> collection, int startIndex);

    @Override
    protected IPlotData getX(IScanDataPoint... points) {

        if (cachedX == null)
            cachedX = new ArrayList<Double>(89);

        Double[] values = cachedX.toArray(new Double[] {});
        double[] primitiveValues = ArrayUtils.toPrimitive(values, values.length);
        Dataset xValues = new DoubleDataset(primitiveValues, primitiveValues.length);
        xValues.setName(getXAxisName());
        return new DataSetPlotData(getXAxisName(), xValues);
    }

    @Override
    protected String getXAxisName() {
        return xAxisTitle;
    }

    @Override
    protected void plotPointsFromService() throws Exception {
        if (cachedX == null)
            cachedX = new ArrayList<Double>(89);
        if (cachedY == null)
            cachedY = new ArrayList<Double>(89);
        super.plotPointsFromService();
    }

    public List<Double> testGetCachedX() {
        return this.cachedX;
    }

    public List<Double> testGetCachedY() {
        return this.cachedY;
    }
}