diff --git a/org.eclipse.scanning.api/src/org/eclipse/scanning/api/annotation/scan/AnnotationManager.java b/org.eclipse.scanning.api/src/org/eclipse/scanning/api/annotation/scan/AnnotationManager.java index 1c22941f6..a6bd8087f 100644 --- a/org.eclipse.scanning.api/src/org/eclipse/scanning/api/annotation/scan/AnnotationManager.java +++ b/org.eclipse.scanning.api/src/org/eclipse/scanning/api/annotation/scan/AnnotationManager.java @@ -278,7 +278,13 @@ public void invoke(Object... objects) throws IllegalAccessException, IllegalArgu arguments[index] = context.get(i); } } - method.invoke(instance, arguments); + boolean accessible = method.isAccessible(); + try { + method.setAccessible(true); + method.invoke(instance, arguments); + } finally { + method.setAccessible(accessible); + } } else { method.invoke(instance); } diff --git a/org.eclipse.scanning.sequencer/src/org/eclipse/scanning/sequencer/AcquisitionDevice.java b/org.eclipse.scanning.sequencer/src/org/eclipse/scanning/sequencer/AcquisitionDevice.java index e0d42249e..5b220c51f 100644 --- a/org.eclipse.scanning.sequencer/src/org/eclipse/scanning/sequencer/AcquisitionDevice.java +++ b/org.eclipse.scanning.sequencer/src/org/eclipse/scanning/sequencer/AcquisitionDevice.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -46,11 +47,8 @@ import org.eclipse.scanning.api.event.scan.DeviceState; import org.eclipse.scanning.api.event.scan.ScanBean; import org.eclipse.scanning.api.event.status.Status; -import org.eclipse.scanning.api.points.GeneratorException; import org.eclipse.scanning.api.points.IDeviceDependentIterable; -import org.eclipse.scanning.api.points.IPointGenerator; import org.eclipse.scanning.api.points.IPosition; -import org.eclipse.scanning.api.points.models.CompoundModel; import org.eclipse.scanning.api.scan.IScanService; import org.eclipse.scanning.api.scan.PositionEvent; import org.eclipse.scanning.api.scan.ScanInformation; @@ -106,12 +104,16 @@ final class AcquisitionDevice extends AbstractRunnableDevice implemen private CountDownLatch latch; /** - * Variables used to monitor progress of inner scans + * Manages the positions we reach in the scan, including + * the outer scan location. */ - private int outerSize = 0; - private int outerCount = 0; - private int innerSize = 0; - private int totalSize = 0; + private LocationManager location; + + /** + * The current position iterator we are using + * to move over the CPU scan. + */ + private Iterator positionIterator; /** * Package private constructor, devices are created by the service. @@ -193,6 +195,8 @@ public void configure(ScanModel model) throws ScanningException { long after = System.currentTimeMillis(); setConfigureTime(after-before); + + location = new LocationManager(getBean(), model, manager); } @@ -204,18 +208,15 @@ public void run(IPosition parent) throws ScanningException, InterruptedException ScanModel model = getModel(); if (model.getPositionIterable()==null) throw new ScanningException("The model must contain some points to scan!"); - - CompoundModel cmodel = getBean().getScanRequest()!=null ? getBean().getScanRequest().getCompoundModel() : null; - SubscanModerator moderator = new SubscanModerator(model.getPositionIterable(), cmodel, model.getDetectors(), ServiceHolder.getGeneratorService()); - manager.addContext(moderator); - + manager.addContext(getBean()); manager.addContext(model); - manager.addContext(moderator); boolean errorFound = false; IPosition pos = null; try { + this.positionIterator = location.createPositionIterator(); + RunnableDeviceServiceImpl.setCurrentScanningDevice(this); if (latch!=null) latch.countDown(); this.latch = new CountDownLatch(1); @@ -225,12 +226,7 @@ public void run(IPosition parent) throws ScanningException, InterruptedException // Sometimes logic is needed to implement collision avoidance // Set the size and declare a count - final int size = getEstimatedSize(moderator.getOuterIterable()); - int count = 0; - outerSize = size; - innerSize = getEstimatedSize(moderator.getInnerIterable()); - totalSize = getEstimatedSize(model.getPositionIterable()); - fireStart(size); + fireStart(location.getOuterSize()); // We allow monitors which can block a position until a setpoint is // reached or add an extra record to the NeXus file. @@ -245,11 +241,10 @@ public void run(IPosition parent) throws ScanningException, InterruptedException // The scan loop pos = null; // We want the last point when we are done so don't use foreach boolean firedFirst = false; - for (IPosition position : moderator.getOuterIterable()) { - outerCount = count; + while (positionIterator.hasNext()) { - pos = position; - pos.setStepIndex(count); + pos = positionIterator.next(); + pos.setStepIndex(location.getStepNumber()); if (!firedFirst) { fireFirst(pos); @@ -272,8 +267,7 @@ public void run(IPosition parent) throws ScanningException, InterruptedException // Send an event about where we are in the scan manager.invoke(PointEnd.class, pos); - positionComplete(pos, count, size); - count+=Math.max(innerSize, 1); + positionComplete(pos); } // On the last iteration we must wait for the final readout. @@ -296,6 +290,10 @@ public void run(IPosition parent) throws ScanningException, InterruptedException RunnableDeviceServiceImpl.setCurrentScanningDevice(null); } } + + private void positionComplete(IPosition pos) throws EventException, ScanningException { + positionComplete(pos, location.getStepNumber(), location.getOuterSize()); + } private void fireFirst(IPosition firstPosition) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException, ScanningException, EventException { @@ -567,29 +565,18 @@ public void seek(int stepNumber) throws ScanningException, InterruptedException // This is the values of all motors at this global (including malcolm) scan // position. Therefore we do not need a subscan moderator but can run the iterator // to the point - IPosition pos = positionForStep(stepNumber); - if (pos == null) throw new ScanningException("Seek position is invalid "+stepNumber); - + if (stepNumber<0) throw new ScanningException("Seek position is invalid "+stepNumber); + if (stepNumber>location.getTotalSize()) throw new ScanningException("Seek position is invalid "+stepNumber); + + // We just changed the location from where we run the scan from and require a new location. + this.positionIterator = location.createPositionIterator(); + IPosition pos = location.seek(stepNumber, positionIterator); positioner.setPosition(pos); if (getModel().getDetectors()!=null) for (IRunnableDevice device : getModel().getDetectors()) { if (device instanceof IPausableDevice) ((IPausableDevice)device).seek(stepNumber); } } - private IPosition positionForStep(int stepNumber) { - /* - * IMPORTANT We do not keep the positions in memory because there can be millions. - * Running over them is fast however. - */ - int count=0; - for (IPosition pos : model.getPositionIterable()) { - pos.setStepIndex(count); - if (count == stepNumber) return pos; - count++; - } - return null; - } - @Override public void resume() throws ScanningException { @@ -622,9 +609,8 @@ public void resume() throws ScanningException { */ @Override public void positionPerformed(PositionEvent evt) throws ScanningException { - IPosition position = evt.getPosition(); - if (outerSize > 0 && innerSize > 0) { - innerPositionPercentComplete(position.getStepIndex()); + if (location.isInnerScan()) { + innerPositionPercentComplete(); } } @@ -633,23 +619,11 @@ public void positionPerformed(PositionEvent evt) throws ScanningException { * @param innerCount The count representing the progress of of the inner scan * @throws Exception */ - private void innerPositionPercentComplete(int innerCount) { - final ScanBean bean = getBean(); - int overallCount = (outerCount * innerSize) + innerCount + 1; - bean.setMessage("Point " + overallCount + " of " + totalSize); + private void innerPositionPercentComplete() { - double innerPercentComplete = 0; - if (innerCount > -1) { - innerPercentComplete = ((double) (innerCount + 1) / innerSize); - } - double outerPercentComplete = 0; - if (outerCount > -1) { - outerPercentComplete = ((double) (outerCount) / outerSize) * 100; - } - double innerPercentOfOuter = 100 / (double) outerSize; - innerPercentOfOuter *= innerPercentComplete; - outerPercentComplete += innerPercentOfOuter; - bean.setPercentComplete(outerPercentComplete); + final ScanBean bean = getBean(); + bean.setMessage("Point " + location.getOverallCount() + " of " + location.getTotalSize()); + bean.setPercentComplete(location.getOuterPercent()); if (getPublisher() != null) { try { @@ -658,23 +632,7 @@ private void innerPositionPercentComplete(int innerCount) { logger.warn("An error occurred publishing percent complete event ", e); } } - } - - private int getEstimatedSize(Iterable gen) throws GeneratorException { - - int size=0; - if (gen instanceof IDeviceDependentIterable) { - size = ((IDeviceDependentIterable)gen).size(); - - } else if (gen instanceof IPointGenerator) { - size = ((IPointGenerator)gen).size(); - - } else if (gen!=null) { - for (IPosition unused : gen) size++; // Fast even for large stuff providing they do not check hardware on the next() call. - } - return size; - } - + } private Collection> getScannables(ScanModel model) throws ScanningException { final Collection names = getScannableNames(model.getPositionIterable()); diff --git a/org.eclipse.scanning.sequencer/src/org/eclipse/scanning/sequencer/LocationManager.java b/org.eclipse.scanning.sequencer/src/org/eclipse/scanning/sequencer/LocationManager.java new file mode 100644 index 000000000..7aafbb247 --- /dev/null +++ b/org.eclipse.scanning.sequencer/src/org/eclipse/scanning/sequencer/LocationManager.java @@ -0,0 +1,184 @@ +package org.eclipse.scanning.sequencer; + +import java.util.Iterator; + +import org.eclipse.scanning.api.annotation.scan.AnnotationManager; +import org.eclipse.scanning.api.annotation.scan.PointEnd; +import org.eclipse.scanning.api.event.scan.ScanBean; +import org.eclipse.scanning.api.points.GeneratorException; +import org.eclipse.scanning.api.points.IDeviceDependentIterable; +import org.eclipse.scanning.api.points.IPointGenerator; +import org.eclipse.scanning.api.points.IPosition; +import org.eclipse.scanning.api.points.models.CompoundModel; +import org.eclipse.scanning.api.scan.ScanningException; +import org.eclipse.scanning.api.scan.models.ScanModel; + +/** + * + * This class manages the location of + * various parts of the scan as the scan runs. + * + * It maintains a count of levels and creates a + * + * @author Matthew Gerring + * + */ +public final class LocationManager { + + + /** + * Variables used to monitor progress of inner scans + */ + private int outerSize = 0; + private int outerCount = 0; + private int innerSize = 0; + private int totalSize = 0; + private int stepNumber = -1; + + // External data + private final ScanBean bean; + private final ScanModel model; + private final AnnotationManager manager; + + public LocationManager(ScanBean bean, ScanModel model, AnnotationManager manager) { + this.bean = bean; + this.model = model; + this.manager = manager; + manager.addDevices(this); + } + + public int getOuterSize() { + return outerSize; + } + public void setOuterSize(int outerSize) { + this.outerSize = outerSize; + } + public int getOuterCount() { + return outerCount; + } + public void setOuterCount(int outerCount) { + this.outerCount = outerCount; + } + public int getInnerSize() { + return innerSize; + } + public void setInnerSize(int innerSize) { + this.innerSize = innerSize; + } + public int getTotalSize() { + return totalSize; + } + public void setTotalSize(int totalSize) { + this.totalSize = totalSize; + } + public int getStepNumber() { + return stepNumber; + } + public void setStepNumber(int stepNumber) { + this.stepNumber = stepNumber; + } + + /** + * Called during the scan to increment counts. + */ + @PointEnd + public void increment() { + outerCount++; + stepNumber+=Math.max(innerSize, 1); + } + + /** + * Method used to generate an iterator for the scan. + * It sets counts which are incremented during the scan. + * + * @return + * @throws ScanningException + */ + public Iterator createPositionIterator() throws ScanningException { + + CompoundModel cmodel = bean.getScanRequest()!=null ? bean.getScanRequest().getCompoundModel() : null; + SubscanModerator moderator = new SubscanModerator(model.getPositionIterable(), cmodel, model.getDetectors(), ServiceHolder.getGeneratorService()); + manager.addContext(moderator); + + try { + stepNumber = 0; + outerSize = getEstimatedSize(moderator.getOuterIterable()); + innerSize = getEstimatedSize(moderator.getInnerIterable()); + totalSize = getEstimatedSize(model.getPositionIterable()); + } catch (GeneratorException se) { + throw new ScanningException("Cannot create the position iterator!", se); + } + + return moderator.getOuterIterable().iterator(); + } + + + private int getEstimatedSize(Iterable gen) throws GeneratorException { + + int size=0; + if (gen instanceof IDeviceDependentIterable) { + size = ((IDeviceDependentIterable)gen).size(); + + } else if (gen instanceof IPointGenerator) { + size = ((IPointGenerator)gen).size(); + + } else if (gen!=null) { + for (IPosition unused : gen) size++; // Fast even for large stuff providing they do not check hardware on the next() call. + } + return size; + } + + /** + * Seek within the iterator for the given location. + * @param location + * @param iterator + * @return null if position not found. + */ + public IPosition seek(int location, Iterator iterator) { + /* + * IMPORTANT We do not keep the positions in memory because there can be millions. + * Running over them is fast however. + */ + while(iterator.hasNext()) { + IPosition pos = iterator.next(); + pos.setStepIndex(stepNumber); + if (stepNumber == location) return pos; + stepNumber+=Math.max(innerSize, 1); + } + return null; + } + + public boolean isInnerScan() { + return outerSize > 0 && innerSize > 0; + } + + public int getOverallCount() { + return (outerCount * innerSize) + getStepNumber() + 1; + } + + /** + * TODO This code is copied from AcquisitionDevice but + * it is not clear if/how it works. The stepnumber was + * used however this is the global position in the scan + * so presumably the maths are wrong. + * + * @return + */ + public double getOuterPercent() { + + double innerPercentComplete = 0; + if (stepNumber > -1) { + innerPercentComplete = (double) (stepNumber + 1) / innerSize; + } + double outerPercentComplete = 0; + if (outerCount > -1) { + outerPercentComplete = ((double) (outerCount) / outerSize) * 100; + } + double innerPercentOfOuter = 100 / (double) outerSize; + innerPercentOfOuter *= innerPercentComplete; + outerPercentComplete += innerPercentOfOuter; + + return outerPercentComplete; + } + +} diff --git a/org.eclipse.scanning.test/src/org/eclipse/scanning/test/scan/SeekTest.java b/org.eclipse.scanning.test/src/org/eclipse/scanning/test/scan/SeekTest.java index 0665b95b4..458361bf6 100644 --- a/org.eclipse.scanning.test/src/org/eclipse/scanning/test/scan/SeekTest.java +++ b/org.eclipse.scanning.test/src/org/eclipse/scanning/test/scan/SeekTest.java @@ -19,7 +19,6 @@ import org.eclipse.scanning.api.scan.models.ScanModel; import org.eclipse.scanning.server.servlet.Services; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; public class SeekTest extends AbstractAcquisitionTest { @@ -55,7 +54,6 @@ public void seekFirst() throws Exception { } } - @Ignore("This MUST pass but requires a larger refactor so seeing that the initial changes do with the tests first.") @Test public void seekFirstRestartsInCorrectLocation() throws Exception { @@ -90,7 +88,7 @@ public void positionPerformed(PositionEvent evt) throws ScanningException { // The scan should restart from where it is seeked to. // Therefore the steps should be size (25) + stopped.getStepIndex() assertEquals("The scan should restart from where it is seeked to.", - 25+stopped.getStepIndex(), steps.size()); + 25+stopped.getStepIndex()+1, steps.size()); } finally { scanner.abort();