diff --git a/src/main/java/org/micromanager/lightsheetmanager/api/data/AcquisitionMode.java b/src/main/java/org/micromanager/lightsheetmanager/api/data/AcquisitionMode.java index 25fc535..4368aa5 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/api/data/AcquisitionMode.java +++ b/src/main/java/org/micromanager/lightsheetmanager/api/data/AcquisitionMode.java @@ -1,41 +1,70 @@ package org.micromanager.lightsheetmanager.api.data; -import java.util.Arrays; +import java.util.Collections; +import java.util.EnumMap; +import java.util.EnumSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; -// TODO: account for different naming on each geometry type (SLICE_SCAN_ONLY) +// To add a new AcquisitionMode: +// 1) Add a new constant to the enum. +// 2) Add the AcquisitionMode to the GeometryType in MODES_BY_GEOMETRY. + +// Note: The UI display order is determined by the order in the MODES_BY_GEOMETRY lists. +// Note: Each text string must be unique to support reliable lookup in fromString(). /** * Acquisition modes for all microscope geometry types. */ public enum AcquisitionMode { - NONE("None"), - PIEZO_SLICE_SCAN("Synchronous piezo/slice scan"), NO_SCAN("No scan (fixed sheet)"), + + // Stage scan modes STAGE_SCAN("Stage scan"), STAGE_SCAN_INTERLEAVED("Stage scan interleaved"), STAGE_SCAN_UNIDIRECTIONAL("Stage scan unidirectional"), - //SLICE_SCAN_ONLY("Slice scan only"), // for diSPIM - SLICE_SCAN_ONLY("Galvo scan"), // for SCAPE - PIEZO_SCAN_ONLY("Piezo scan only"); - - // Maps GeometryType to a set of valid AcquisitionModes - private static final Map> MODES_BY_GEOMETRY_TYPE = Map.of( - GeometryType.SCAPE, List.of(NO_SCAN, STAGE_SCAN, SLICE_SCAN_ONLY), - GeometryType.DISPIM, List.of(NO_SCAN, STAGE_SCAN, SLICE_SCAN_ONLY) // TODO: add all valid modes - ); - private static final List STAGE_SCAN_MODES = List.of( + // SCAPE + GALVO_SCAN("Galvo scan"), + + // diSPIM + SLICE_SCAN_ONLY("Slice scan only"), + PIEZO_SCAN_ONLY("Piezo scan only"), + PIEZO_SLICE_SCAN("Synchronous piezo/slice scan"); + + // Stage scan modes, conditionally included based on hardware availability + private static final EnumSet STAGE_SCAN_MODES = EnumSet.of( STAGE_SCAN, STAGE_SCAN_INTERLEAVED, STAGE_SCAN_UNIDIRECTIONAL ); + // Maps GeometryType to a List of valid AcquisitionMode constants + private static final Map> MODES_BY_GEOMETRY = + new EnumMap<>(GeometryType.class); + + // Add valid AcquisitionModes to GeometryTypes. + // The order of the constants determines the order in dropdown menus. + static { + MODES_BY_GEOMETRY.put(GeometryType.SCAPE, List.of( + NO_SCAN, STAGE_SCAN, GALVO_SCAN + )); + + MODES_BY_GEOMETRY.put(GeometryType.DISPIM, List.of( + NO_SCAN, STAGE_SCAN, STAGE_SCAN_INTERLEAVED, STAGE_SCAN_UNIDIRECTIONAL, + SLICE_SCAN_ONLY, PIEZO_SCAN_ONLY, PIEZO_SLICE_SCAN + )); + + // Example: Add future geometry types here + // MODES_BY_GEOMETRY.put(GeometryType.MESOSPIM, List.of(...)); + } + + // Display text used in the UI private final String text_; - private static final Map stringToEnum = - Stream.of(values()).collect(Collectors.toMap(Object::toString, e -> e)); + private static final Map STRING_TO_ENUM = + Stream.of(values()).collect(Collectors.toMap(Object::toString, e -> e)); AcquisitionMode(final String text) { text_ = text; @@ -46,27 +75,58 @@ public String toString() { return text_; } - public static AcquisitionMode fromString(final String symbol) { - return stringToEnum.getOrDefault(symbol, AcquisitionMode.NONE); + /** + * Returns {@code true} if the {@link AcquisitionMode} is a stage scan mode. + * + * @return {@code true} if the mode is stage scan mode + */ + public boolean isStageScanMode() { + return STAGE_SCAN_MODES.contains(this); } - public static String[] toArray() { - return Arrays.stream(values()) - .map(AcquisitionMode::toString) - .toArray(String[]::new); + /** + * Returns the available acquisition modes supported by a specific microscope geometry. + *

+ * This list is filtered based on hardware capabilities: if {@code hasStageScanning} + * is {@code false}, all stage-scan related modes are excluded. + * + * @param geometry the {@link GeometryType} to query; if {@code null}, an empty list is returned + * @param hasStageScanning {@code true} if stage scan hardware is available + * @return a {@code List} of {@link AcquisitionMode} constants + */ + public static List getValidModes(final GeometryType geometry, final boolean hasStageScanning) { + return Optional.ofNullable(geometry) + .map(MODES_BY_GEOMETRY::get) // returns null if geometry is not in map + .orElse(Collections.emptyList()) + .stream() + .filter(mode -> hasStageScanning || !mode.isStageScanMode()) + .collect(Collectors.toList()); } /** - * Returns an array of valid acquisition modes as strings. + * Return an array of {@code String} labels for a dropdown menu. * - * @param isStageScanning {@code true} if stage scanning + * @param geometry the microscope {@link GeometryType} + * @param hasStageScanning {@code true} if stage scan hardware is available * @return an array of strings */ - public static String[] getValidKeys(final GeometryType geometry, final boolean isStageScanning) { - final List baseModes = MODES_BY_GEOMETRY_TYPE.getOrDefault(geometry, List.of()); - return baseModes.stream() - .filter(mode -> isStageScanning || !STAGE_SCAN_MODES.contains(mode)) - .map(AcquisitionMode::toString) - .toArray(String[]::new); + public static String[] getLabels(final GeometryType geometry, final boolean hasStageScanning) { + return getValidModes(geometry, hasStageScanning).stream() + .map(AcquisitionMode::toString) + .toArray(String[]::new); + } + + /** + * Returns the {@link AcquisitionMode} associated with the string label. + *

+ * If the provided string is {@code null} or does not match any known + * acquisition mode, an empty {@link Optional} is returned. + * + * @param str the string label to convert (Example: "Stage scan") + * @return an {@link Optional} containing the matching {@link AcquisitionMode}, + * or an empty {@code Optional} if no match is found. + */ + public static Optional fromString(final String str) { + return Optional.ofNullable(str).map(STRING_TO_ENUM::get); } } diff --git a/src/main/java/org/micromanager/lightsheetmanager/api/internal/DefaultAcquisitionSettingsDISPIM.java b/src/main/java/org/micromanager/lightsheetmanager/api/internal/DefaultAcquisitionSettingsDISPIM.java index aacc3e7..f0aff61 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/api/internal/DefaultAcquisitionSettingsDISPIM.java +++ b/src/main/java/org/micromanager/lightsheetmanager/api/internal/DefaultAcquisitionSettingsDISPIM.java @@ -16,7 +16,7 @@ public static class Builder extends DefaultAcquisitionSettings.Builder private DefaultScanSettings.Builder scsb_ = new DefaultScanSettings.Builder(); private DefaultSheetCalibration.Builder[] shcb_ = new DefaultSheetCalibration.Builder[2]; private DefaultSliceCalibration.Builder[] slcb_ = new DefaultSliceCalibration.Builder[2]; - private AcquisitionMode acquisitionMode_ = AcquisitionMode.NONE; + private AcquisitionMode acquisitionMode_ = AcquisitionMode.NO_SCAN; private MultiChannelMode channelMode_ = MultiChannelMode.NONE; private CameraMode cameraMode_ = CameraMode.EDGE; diff --git a/src/main/java/org/micromanager/lightsheetmanager/api/internal/DefaultAcquisitionSettingsSCAPE.java b/src/main/java/org/micromanager/lightsheetmanager/api/internal/DefaultAcquisitionSettingsSCAPE.java index ce82adc..03ade96 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/api/internal/DefaultAcquisitionSettingsSCAPE.java +++ b/src/main/java/org/micromanager/lightsheetmanager/api/internal/DefaultAcquisitionSettingsSCAPE.java @@ -16,7 +16,7 @@ public static class Builder extends DefaultAcquisitionSettings.Builder private DefaultScanSettings.Builder scsb_ = new DefaultScanSettings.Builder(); private DefaultSheetCalibration.Builder[] shcb_ = new DefaultSheetCalibration.Builder[1]; private DefaultSliceCalibration.Builder[] slcb_ = new DefaultSliceCalibration.Builder[1]; - private AcquisitionMode acquisitionMode_ = AcquisitionMode.NONE; + private AcquisitionMode acquisitionMode_ = AcquisitionMode.NO_SCAN; private CameraMode cameraMode_ = CameraMode.EDGE; private boolean useChannels_ = false; diff --git a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/AcquisitionTab.java b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/AcquisitionTab.java index 0688633..859fad4 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/AcquisitionTab.java +++ b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/AcquisitionTab.java @@ -163,7 +163,7 @@ private void createUserInterface() { // acquisition mode combo box final boolean isUsingScanSettings = model_.devices().isUsingStageScanning(); final GeometryType geometryType = model_.devices().getDeviceAdapter().getMicroscopeGeometry(); - cmbAcquisitionModes_ = new ComboBox(AcquisitionMode.getValidKeys(geometryType, isUsingScanSettings), + cmbAcquisitionModes_ = new ComboBox(AcquisitionMode.getLabels(geometryType, isUsingScanSettings), acqSettings.acquisitionMode().toString(), 180, 24); @@ -266,8 +266,9 @@ private void createEventHandlers() { // select the acquisition mode cmbAcquisitionModes_.registerListener(e -> { final String selected = cmbAcquisitionModes_.getSelected(); - model_.acquisitions().settingsBuilder() - .acquisitionMode(AcquisitionMode.fromString(selected)); + AcquisitionMode.fromString(selected).ifPresent(mode -> { + model_.acquisitions().settingsBuilder().acquisitionMode(mode); + }); }); // TODO: should timing recalc be part of setting use advanced timing value in model? diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/PLogicSCAPE.java b/src/main/java/org/micromanager/lightsheetmanager/model/PLogicSCAPE.java index 84c6df4..6399f2d 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/PLogicSCAPE.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/PLogicSCAPE.java @@ -497,13 +497,6 @@ public boolean prepareControllerForAcquisitionSide( double sliceAmplitude = piezoAmplitude / sliceRate; double sliceCenter = (piezoCenter - sliceOffset) / sliceRate; - if (settings.acquisitionMode() == AcquisitionMode.PIEZO_SCAN_ONLY) { - if (cameraMode == CameraMode.OVERLAP) { - double actualPiezoCenter = piezoCenter - piezoAmplitude / (2 * (numSlicesHW - 1)); - sliceCenter = (actualPiezoCenter - sliceOffset) / sliceRate; - } - sliceAmplitude = 0.0; - } // round to nearest 0.0001 degrees, which is approximately the DAC resolution sliceAmplitude = NumberUtils.roundToPlace(sliceAmplitude, 4); sliceCenter = NumberUtils.roundToPlace(sliceCenter, 4); @@ -532,9 +525,9 @@ public boolean prepareControllerForAcquisitionSide( // get the piezo card ready // need to do this for stage scanning too, which makes sure the piezo amplitude is 0 - // if mode SLICE_SCAN_ONLY we have computed slice movement as if we + // if mode GALVO_SCAN we have computed slice movement as if we // were moving the piezo but now make piezo stay still - if (settings.acquisitionMode() == AcquisitionMode.SLICE_SCAN_ONLY) { + if (settings.acquisitionMode() == AcquisitionMode.GALVO_SCAN) { // if we artificially shifted centers due to extra trigger and only moving piezo // then move galvo center back to where it would have been if (settings.cameraMode() == CameraMode.OVERLAP) { @@ -846,9 +839,7 @@ public boolean triggerControllerStartAcquisition(final AcquisitionMode acqMode, scanner_.setSPIMState(ASIScanner.SPIMState.ARMED); xyStage_.setScanState(ASIXYStage.ScanState.RUNNING); break; - case PIEZO_SLICE_SCAN: - case SLICE_SCAN_ONLY: - case PIEZO_SCAN_ONLY: + case GALVO_SCAN: case NO_SCAN: // in actuality only matters which device we trigger if there are // two micro-mirror cards, which hasn't ever been done in practice yet diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngine.java b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngine.java index 637ef39..3b3a847 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngine.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngine.java @@ -133,12 +133,6 @@ public Future requestRun(boolean speedTest) { } } else { studio_.logs().logMessage("Preparing Acquisition: plugin version " + LightSheetManagerPlugin.version); - // TODO: put this here? generic setup tasks? put in own method? - System.out.println("acqSettings_.acquisitionMode(): " + acqSettings_.acquisitionMode()); - if (acqSettings_.acquisitionMode() == AcquisitionMode.NONE) { - studio_.logs().showError("please select a valid acquisition mode!"); - return; // early exit - } // run abstract methods implemented by acquisition engine geometry types if (!setup()) { studio_.logs().logError("error during setup!");