diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/DeviceManager.java b/src/main/java/org/micromanager/lightsheetmanager/model/DeviceManager.java index a0f72b2..f88ccc7 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/DeviceManager.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/DeviceManager.java @@ -21,6 +21,7 @@ import org.micromanager.lightsheetmanager.model.devices.cameras.HamamatsuCamera; import org.micromanager.lightsheetmanager.model.devices.cameras.PcoCamera; import org.micromanager.lightsheetmanager.model.devices.cameras.PvCamera; +import org.micromanager.lightsheetmanager.model.devices.cameras.UnknownCamera; import org.micromanager.lightsheetmanager.model.devices.vendor.ASIPLogic; import org.micromanager.lightsheetmanager.model.devices.vendor.ASIPiezo; import org.micromanager.lightsheetmanager.model.devices.vendor.ASIScanner; @@ -222,10 +223,10 @@ private void createCameraDevice(final String propertyName, final String deviceNa addDevice(propertyName, deviceName, demoCamera); break; default: - CameraBase camera = new CameraBase(studio_, deviceName); + UnknownCamera camera = new UnknownCamera(studio_, deviceName); addDevice(propertyName, deviceName, camera); - studio_.logs().logError( - "Camera device library \"" + cameraLibrary + "\" not supported, using basic camera."); + studio_.logs().logError("Camera device library \"" + cameraLibrary + + "\" not supported; exposure and ROI will work but timing values are unknown."); break; } } diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineDispim.java b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineDispim.java index 01c43d0..5562165 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineDispim.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineDispim.java @@ -15,6 +15,7 @@ import org.micromanager.data.internal.DefaultSummaryMetadata; import org.micromanager.data.internal.ndtiff.NDTiffAdapter; import org.micromanager.internal.MMStudio; +import org.micromanager.lightsheetmanager.api.data.CameraLibrary; import org.micromanager.lightsheetmanager.api.data.CameraMode; import org.micromanager.lightsheetmanager.api.data.GeometryType; import org.micromanager.lightsheetmanager.api.data.ChannelMode; @@ -640,7 +641,8 @@ private boolean doHardwareCalculations(PLogicDispim plc) { // TODO: code that doubles nrSlicesSoftware if (twoSided && acqBothCameras) missing CameraBase camera = model_.devices().device("Imaging1Camera"); - CameraMode camMode = camera.getTriggerMode(); + // settings are the source of truth for camera mode + CameraMode camMode = acqSettings_.cameraMode(); final double cameraReadoutTime = camera.getReadoutTime(camMode); final double exposureTime = acqSettings_.timing().cameraExposure(); @@ -766,9 +768,8 @@ public DefaultTimingSettings.Builder getTimingFromPeriodAndLightExposure() { // TODO: do this in ui? camera.setTriggerMode(acqSettings_.cameraMode()); - //System.out.println(camera.getDeviceName()); - CameraMode camMode = camera.getTriggerMode(); - //System.out.println(camMode); + // settings are the source of truth for camera mode + CameraMode camMode = acqSettings_.cameraMode(); DefaultTimingSettings.Builder tsb = DefaultTimingSettings.builder(); @@ -849,9 +850,20 @@ public DefaultTimingSettings.Builder getTimingFromPeriodAndLightExposure() { break; case PSEUDO_OVERLAP:// PCO or Photometrics, enforce 0.25ms between end exposure and start of next exposure by triggering camera 0.25ms into the slice cameraDuration = 1; // doesn't really matter, 1ms should be plenty fast yet easy to see for debugging - // TODO: not dealing with PVCAM (maybe throw error on unknown cam lib) - sliceDuration = getSliceDuration(delayBeforeScan, scanDuration, scansPerSlice, delayBeforeLaser, laserDuration, delayBeforeCamera, cameraDuration); - cameraExposure = sliceDuration - delayBeforeCamera; // s.cameraDelay should be 0.25ms for PCO + switch (CameraLibrary.fromString(camera.getDeviceLibrary())) { + case PVCAM: + // leave cameraExposure alone + break; + case PCOCAMERA: + sliceDuration = getSliceDuration(delayBeforeScan, scanDuration, scansPerSlice, + delayBeforeLaser, laserDuration, delayBeforeCamera, cameraDuration); + cameraExposure = sliceDuration - delayBeforeCamera; // delayBeforeCamera should be 0.25ms for PCO + break; + default: + studio_.logs().showError("Unknown camera library for pseudo-overlap " + + "calculations: " + camera.getDeviceLibrary()); + break; + } if (cameraReadoutMax < 0.24) { studio_.logs().showError("Camera delay should be at least 0.25ms for pseudo-overlap mode."); } diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineScape.java b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineScape.java index 9656bde..8259b04 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineScape.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineScape.java @@ -179,6 +179,9 @@ boolean run() { "stage scanning previously. Do you want to set it to 1 mm/s now?"); if (result) { xyStage.setSpeedX(1.0); + // origSpeedX_ is the value finish() restores, so it has to track the change we + // just made; otherwise we put the small speed straight back at the end of the run + origSpeedX_ = 1.0; } } // TODO: add more checks from original plugin here... Z speed? @@ -1244,10 +1247,8 @@ public DefaultTimingSettings.Builder getTimingFromPeriodAndLightExposure() { cam.setTriggerMode(acqSettings_.cameraMode()); } - // TODO: camera.getTriggerMode(); does not match up with actual selected trigger mode for PVCAM (pseudo overlap reads as edge trigger) - //System.out.println(camera.getDeviceName()); - CameraMode camMode = acqSettings_.cameraMode(); // camera.getTriggerMode(); - //System.out.println(camMode); + // settings are the source of truth for camera mode + CameraMode camMode = acqSettings_.cameraMode(); final double scanLaserBufferTime = NumberUtils.roundToQuarterMs(0.25); // below assumed to be multiple of 0.25ms @@ -1349,9 +1350,17 @@ public DefaultTimingSettings.Builder getTimingFromPeriodAndLightExposure() { break; case PSEUDO_OVERLAP:// PCO or Photometrics, enforce 0.25ms between end exposure and start of next exposure by triggering camera 0.25ms into the slice // cameraTriggerDuration: doesn't really matter, 1ms should be plenty fast yet easy to see for debugging - // leave cameraExposure alone if using PVCAM device library - if (!camera.getDeviceLibrary().equals("PVCAM")) { - cameraExposure = tsb.sliceDuration() - delayBeforeCamera; // delayBeforeCamera should be 0.25ms for PCO + switch (CameraLibrary.fromString(camera.getDeviceLibrary())) { + case PVCAM: + // leave cameraExposure alone + break; + case PCOCAMERA: + cameraExposure = tsb.sliceDuration() - delayBeforeCamera; // delayBeforeCamera should be 0.25ms for PCO + break; + default: + studio_.logs().showError("Unknown camera library for pseudo-overlap " + + "calculations: " + camera.getDeviceLibrary()); + break; } if (cameraReadoutMax < 0.24) { studio_.logs().showError("Camera delay should be at least 0.25ms for pseudo-overlap mode."); diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/AndorCamera.java b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/AndorCamera.java index cde8069..a5a5702 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/AndorCamera.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/AndorCamera.java @@ -1,7 +1,6 @@ package org.micromanager.lightsheetmanager.model.devices.cameras; import org.micromanager.Studio; -import org.micromanager.lightsheetmanager.api.LightSheetCamera; import org.micromanager.lightsheetmanager.api.data.CameraMode; import java.awt.Rectangle; @@ -11,7 +10,7 @@ *
Device Adapter: AndorSDK3 *
Camera Support: Andor Zyla 4.2, Andor Zyla 5.5 */ -public class AndorCamera extends CameraBase implements LightSheetCamera { +public class AndorCamera extends CameraBase { public static class Models { public static final String ZYLA55 = "Zyla 5.5"; diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/CameraBase.java b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/CameraBase.java index 5be8c5e..5945637 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/CameraBase.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/CameraBase.java @@ -7,12 +7,14 @@ import java.awt.Rectangle; -// TODO: consider removing implements LightSheetCamera so no default methods for subclasses - /** * This is the base camera class. + * + *
Methods that need per-vendor knowledge are abstract on purpose: a camera class that + * forgets one fails to compile. Cameras whose device library resolves to + * {@code CameraLibrary.UNKNOWN} use {@link UnknownCamera}. */ -public class CameraBase extends DeviceBase implements LightSheetCamera { +public abstract class CameraBase extends DeviceBase implements LightSheetCamera { protected CameraMode mode_; @@ -96,32 +98,20 @@ public CameraMode getTriggerMode() { } @Override - public void setBinning() { - - } + public abstract void setBinning(); @Override - public int getBinning() { - return 0; - } + public abstract int getBinning(); @Override - public Rectangle getResolution() { - return new Rectangle(); - } + public abstract Rectangle getResolution(); @Override - public double getRowReadoutTime() { - return 0; - } + public abstract double getRowReadoutTime(); @Override - public double getReadoutTime(CameraMode cameraMode) { - return 0; - } + public abstract double getReadoutTime(CameraMode cameraMode); @Override - public double getResetTime(CameraMode cameraMode) { - return 0; - } + public abstract double getResetTime(CameraMode cameraMode); } diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/DemoCamera.java b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/DemoCamera.java index 74046a0..ac899b1 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/DemoCamera.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/DemoCamera.java @@ -1,7 +1,6 @@ package org.micromanager.lightsheetmanager.model.devices.cameras; import org.micromanager.Studio; -import org.micromanager.lightsheetmanager.api.LightSheetCamera; import org.micromanager.lightsheetmanager.api.data.CameraMode; import java.awt.Rectangle; @@ -12,7 +11,7 @@ *
Device Adapter: DHub *
Camera Support: DemoCamera */ -public class DemoCamera extends CameraBase implements LightSheetCamera { +public class DemoCamera extends CameraBase { public static class Properties { public static final String BINNING = "Binning"; diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/HamamatsuCamera.java b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/HamamatsuCamera.java index fca3367..8a7104b 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/HamamatsuCamera.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/HamamatsuCamera.java @@ -1,7 +1,6 @@ package org.micromanager.lightsheetmanager.model.devices.cameras; import org.micromanager.Studio; -import org.micromanager.lightsheetmanager.api.LightSheetCamera; import org.micromanager.lightsheetmanager.api.data.CameraMode; import java.awt.Rectangle; @@ -11,7 +10,7 @@ *
Device Adapter: HamamatsuHam *
Camera Support: ORCA-Flash4, ORCA-Fusion, ORCA-Fusion BT */ -public class HamamatsuCamera extends CameraBase implements LightSheetCamera { +public class HamamatsuCamera extends CameraBase { public static class Models { public static final String FUSION_BT = "C15440"; public static final String FUSION = "C14440"; diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/PcoCamera.java b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/PcoCamera.java index 3ac7243..1e45c41 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/PcoCamera.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/PcoCamera.java @@ -1,7 +1,6 @@ package org.micromanager.lightsheetmanager.model.devices.cameras; import org.micromanager.Studio; -import org.micromanager.lightsheetmanager.api.LightSheetCamera; import org.micromanager.lightsheetmanager.api.data.CameraMode; import java.awt.Rectangle; @@ -12,7 +11,7 @@ *
Device Adapter: PCO_Camera *
Camera Support: Edge 5.5, Panda */ -public class PcoCamera extends CameraBase implements LightSheetCamera { +public class PcoCamera extends CameraBase { public static class Models { public static final String EDGE55 =" 5.5"; diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/PvCamera.java b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/PvCamera.java index 5370c09..bfac4b4 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/PvCamera.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/PvCamera.java @@ -1,7 +1,6 @@ package org.micromanager.lightsheetmanager.model.devices.cameras; import org.micromanager.Studio; -import org.micromanager.lightsheetmanager.api.LightSheetCamera; import org.micromanager.lightsheetmanager.api.data.CameraMode; import java.awt.Rectangle; @@ -11,7 +10,7 @@ *
Device Adapter: PVCAM *
Camera Support: Kinetix, Prime 95B, Prime */ -public class PvCamera extends CameraBase implements LightSheetCamera { +public class PvCamera extends CameraBase { public static class Models { public static final String PRIME = "CIS2020F"; diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/UnknownCamera.java b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/UnknownCamera.java new file mode 100644 index 0000000..90c32f8 --- /dev/null +++ b/src/main/java/org/micromanager/lightsheetmanager/model/devices/cameras/UnknownCamera.java @@ -0,0 +1,58 @@ +package org.micromanager.lightsheetmanager.model.devices.cameras; + +import org.micromanager.Studio; +import org.micromanager.lightsheetmanager.api.data.CameraMode; + +import java.awt.Rectangle; + +/** + * Stand-in for a camera whose device adapter the plugin does not have a class for. + * + *
Device setup still completes so the plugin opens and the rest of the configuration stays + * usable, and the generic operations inherited from {@link CameraBase} (exposure, ROI) work + * through the Core for any camera. Only the values that need vendor knowledge fail, and they + * fail loudly: returning a zero for readout or reset time would flow straight into the + * slice-timing math and produce a plausible-looking but wrong acquisition. + */ +public class UnknownCamera extends CameraBase { + + public UnknownCamera(final Studio studio, final String deviceName) { + super(studio, deviceName); + } + + private UnsupportedOperationException unsupported(final String what) { + return new UnsupportedOperationException("Camera \"" + deviceName_ + + "\" uses a device adapter the plugin does not support, so " + what + + " is unknown. Supported: AndorSDK3, HamamatsuHam, PCO_Camera, PVCAM, DemoCamera."); + } + + @Override + public void setBinning() { + throw unsupported("binning"); + } + + @Override + public int getBinning() { + throw unsupported("binning"); + } + + @Override + public Rectangle getResolution() { + throw unsupported("sensor resolution"); + } + + @Override + public double getRowReadoutTime() { + throw unsupported("row readout time"); + } + + @Override + public double getReadoutTime(final CameraMode cameraMode) { + throw unsupported("readout time"); + } + + @Override + public double getResetTime(final CameraMode cameraMode) { + throw unsupported("reset time"); + } +}