From eff66d1afae0d167b7a9b0f8fdec66dfbec489bf Mon Sep 17 00:00:00 2001 From: Brandon Date: Thu, 26 Mar 2026 17:49:31 -0700 Subject: [PATCH 1/4] remove use simultaneous cameras logic --- .../model/DeviceManager.java | 35 ++++------------ .../acquisitions/AcquisitionEngineDispim.java | 40 ++++++++----------- .../acquisitions/AcquisitionEngineScape.java | 23 +++-------- 3 files changed, 30 insertions(+), 68 deletions(-) diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/DeviceManager.java b/src/main/java/org/micromanager/lightsheetmanager/model/DeviceManager.java index 3fc55e8c..4636fca1 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/DeviceManager.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/DeviceManager.java @@ -286,23 +286,7 @@ public T requiredDevice(final String name, final Class } public CameraBase firstImagingCamera() { - String deviceKey; - //if (model_.acquisitions().settings().isUsingSimultaneousCameras()) { - if (true) { - deviceKey = firstActiveCameraName(); - } else { - final DeviceAdapter adapter = model_.devices().adapter(); - if (adapter.numSimultaneousCameras() > 1 && adapter.numImagingPaths() == 1) { - deviceKey = "ImagingCamera1"; - } else if (adapter.numSimultaneousCameras() > 1) { - deviceKey = "Imaging1Camera1"; - } else if (adapter.numImagingPaths() > 1) { - deviceKey = "Imaging1Camera"; - } else { - deviceKey = "ImagingCamera"; - } - } - return (CameraBase) deviceMap_.get(deviceKey); + return (CameraBase) deviceMap_.get(firstActiveCameraName()); } // For simultaneous cameras @@ -352,19 +336,14 @@ public String[] imagingCameraNames() { public CameraBase[] imagingCameras() { String[] cameraNames; - //if (model_.acquisitions().settings().isUsingSimultaneousCameras()) { - if (true) { - ArrayList names = new ArrayList<>(); - final CameraData[] cameras = model_.acquisitions().settings().imagingCameraOrder(); - for (CameraData camera : cameras) { - if (camera.isActive()) { - names.add(camera.name()); - } + ArrayList names = new ArrayList<>(); + final CameraData[] cameras = model_.acquisitions().settings().imagingCameraOrder(); + for (CameraData camera : cameras) { + if (camera.isActive()) { + names.add(camera.name()); } - cameraNames = names.toArray(String[]::new); - } else { - cameraNames = imagingCameraNames(); } + cameraNames = names.toArray(String[]::new); return Arrays.stream(cameraNames) .map(name -> (CameraBase)deviceMap_.get(name)) .filter(Objects::nonNull) 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 24ee679b..8eda8767 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineDispim.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineDispim.java @@ -36,6 +36,8 @@ */ public class AcquisitionEngineDispim extends AcquisitionEngine { + private boolean isPolling_; // true if polling was enabled at the start of an acquisition + // private DefaultAcquisitionSettingsDISPIM.Builder asb_; // TODO: remove this when a more generic method is available and get from base class private DispimAcquisitionSettings acqSettings_; @@ -46,33 +48,23 @@ public AcquisitionEngineDispim(final LightSheetManager model) { acqSettings_ = DispimAcquisitionSettings.builder().build(); } -// @Override -// public DefaultAcquisitionSettingsDISPIM settings() { -// return acqSettings_; -// } -// -// @Override -// public DefaultAcquisitionSettingsDISPIM.Builder settingsBuilder() { -// return asb_; -// } - @Override boolean setup() { - return true; - } - - @Override - boolean run() { - - final boolean isPolling = model_.positions().isPolling(); - if (isPolling) { - studio_.logs().logMessage("stopped position polling"); + isPolling_ = model_.positions().isPolling(); + if (isPolling_) { model_.positions().stopPolling(); + studio_.logs().logMessage("stopped position polling"); } // make settings current updateAcquisitionSettings(); + return true; + } + + @Override + boolean run() { + final boolean isLiveModeOn = studio_.live().isLiveModeOn(); if (isLiveModeOn) { studio_.live().setLiveModeOn(false); @@ -497,20 +489,16 @@ public void close() { // } // } - - // No more instructions (i.e. AcquisitionEvents); tell the acquisition to initiate shutdown // once everything finishes currentAcquisition_.finish(); - currentAcquisition_.waitForCompletion(); // cleanup studio_.logs().logMessage("diSPIM plugin acquisition " + " took: " + (System.currentTimeMillis() - acqButtonStart) + "ms"); - // clean up controller settings after acquisition // want to do this, even with demo cameras, so we can test everything else // TODO: figure out if we really want to return piezos to 0 position (maybe center position, @@ -542,6 +530,12 @@ public void close() { @Override boolean finish() { + + // start polling for navigation panel + if (isPolling_) { + studio_.logs().logMessage("started position polling after acquisition"); + model_.positions().startPolling(); + } return true; } 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 e9dab8af..67b10748 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineScape.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineScape.java @@ -550,26 +550,15 @@ public void close() { } cameraNames = cameraDeviceNames.toArray(new String[0]); } else { - // TODO(Brandon): account for > 2 simultaneous cameras final DeviceAdapter adapter = model_.devices().adapter(); if (adapter.numSimultaneousCameras() > 1 && adapter.numImagingPaths() == 1) { - // multiple simultaneous cameras - //if (model_.acquisitions().settings().isUsingSimultaneousCameras()) { - if (true) { - // use 2 cameras - final ArrayList names = new ArrayList<>(); - final CameraBase[] cameraList = model_.devices().imagingCameras(); - for (CameraBase camera: cameraList) { - names.add(camera.getDeviceName()); - } - cameraNames = names.toArray(String[]::new); - } else { - // use 1 camera - final String camera = "ImagingCamera1"; // model_.acquisitions().settings().primaryCamera(); - cameraNames = new String[] { - model_.devices().device(camera).getDeviceName(), - }; + // multiple simultaneous cameras + final ArrayList names = new ArrayList<>(); + final CameraBase[] cameraList = model_.devices().imagingCameras(); + for (CameraBase camera: cameraList) { + names.add(camera.getDeviceName()); } + cameraNames = names.toArray(String[]::new); } else { // standard camera setup if (acqSettings_.volume().numViews() > 1) { From 04610e81666ca1b1619f94a16bc88013a686a101 Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 13 Apr 2026 17:01:53 -0700 Subject: [PATCH 2/4] write acquisition settings to save directory as json --- .../model/acquisitions/AcquisitionEngineScape.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 67b10748..487e4679 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineScape.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineScape.java @@ -40,6 +40,7 @@ import javax.swing.JLabel; import java.awt.geom.Point2D; +import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -230,11 +231,16 @@ boolean run() { updateAcquisitionSettings(); - studio_.logs().logMessage("Starting Acquisition with settings:\n" + acqSettings_.toPrettyJson()); + final String settingsJson = acqSettings_.toPrettyJson(); + studio_.logs().logMessage("Starting Acquisition with settings:\n" + settingsJson); String saveDir = acqSettings_.saveDirectory(); String saveName = acqSettings_.saveNamePrefix(); + // TODO: put this in AcquisitionEngine base class, between setup and run once structure is better + // save settings as JSON to the save directory + FileUtils.writeStringToFile(saveDir + File.separator + "acq_settings.json", settingsJson); + // This sets the preferred save mode for DefaultDatastore, this value // is used in the MMAcquisition constructor to set the Storage object. if (acqSettings_.saveMode() == DataStorage.SaveMode.ND_TIFF) { From 9eea1909f95b25166da0a03a9c5137e17b042cbb Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 13 Apr 2026 18:05:58 -0700 Subject: [PATCH 3/4] add load and save buttons for acq settings --- .../LightSheetManagerPlugin.java | 2 +- .../gui/tabs/acquisition/SavePanel.java | 54 ++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/micromanager/lightsheetmanager/LightSheetManagerPlugin.java b/src/main/java/org/micromanager/lightsheetmanager/LightSheetManagerPlugin.java index 5139b34f..eb50cb07 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/LightSheetManagerPlugin.java +++ b/src/main/java/org/micromanager/lightsheetmanager/LightSheetManagerPlugin.java @@ -12,7 +12,7 @@ public class LightSheetManagerPlugin implements MenuPlugin, SciJavaPlugin { public static final String copyright = "Applied Scientific Instrumentation (ASI), 2022-2026"; public static final String description = "A plugin to control various types of light sheet microscopes."; public static final String menuName = "Light Sheet Manager"; - public static final String version = "0.6.0"; + public static final String version = "0.6.1"; private Studio studio_; private LightSheetManager model_; diff --git a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/acquisition/SavePanel.java b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/acquisition/SavePanel.java index 637265b0..1ebdea2f 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/acquisition/SavePanel.java +++ b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/acquisition/SavePanel.java @@ -11,6 +11,7 @@ import org.micromanager.lightsheetmanager.gui.components.TextField; import org.micromanager.lightsheetmanager.gui.data.Icons; import org.micromanager.lightsheetmanager.model.DataStorage; +import org.micromanager.lightsheetmanager.model.utils.FileUtils; import javax.swing.JLabel; import java.awt.Color; @@ -30,7 +31,12 @@ public class SavePanel extends Panel { private ComboBox cbxSaveMode_; private CheckBox cbxSaveWhileAcquiring_; + private Button btnSaveSettings_; + private Button btnLoadSettings_; + private final FileDialogs.FileType directorySelect_; + private final FileDialogs.FileType jsonFileSave_; + private final FileDialogs.FileType jsonFileLoad_; private final LightSheetManager model_; private final LightSheetManagerFrame frame_; @@ -40,7 +46,6 @@ public SavePanel(final LightSheetManager model, final LightSheetManagerFrame fra model_ = Objects.requireNonNull(model); frame_ = Objects.requireNonNull(frame); - // file type filter directorySelect_ = new FileDialogs.FileType( "SAVE_DIRECTORY", "All Directories", @@ -49,6 +54,22 @@ public SavePanel(final LightSheetManager model, final LightSheetManagerFrame fra "" ); + jsonFileSave_ = new FileDialogs.FileType( + "SAVE_FILE", + "JSON Files", + "acq_settings.json", + false, + "json" + ); + + jsonFileLoad_ = new FileDialogs.FileType( + "OPEN_FILE", + "JSON Files", + "acq_settings.json", + false, + "json" + ); + createUserInterface(); createEventHandlers(); } @@ -86,6 +107,9 @@ public void createUserInterface() { cbxSaveWhileAcquiring_ = new CheckBox("Save images during acquisition", acqSettings.isSavingImagesDuringAcquisition()); + btnSaveSettings_ = new Button("Save", 60, 20); + btnLoadSettings_ = new Button("Load", 60, 20); + add(lblSaveDirectory, ""); add(txtSaveDirectory_, ""); add(btnBrowse_, ""); @@ -95,6 +119,9 @@ public void createUserInterface() { add(lblSaveMode, ""); add(cbxSaveMode_, "split 2, wrap"); add(cbxSaveWhileAcquiring_, "span 2, wrap"); + add(new JLabel("Acq Settings:"), ""); + add(btnSaveSettings_, "split 2"); + add(btnLoadSettings_, ""); } public void createEventHandlers() { @@ -123,8 +150,33 @@ public void createEventHandlers() { cbxSaveMode_.registerListener(e -> model_.acquisitions().settingsBuilder().saveMode(cbxSaveMode_.getSelected())); + btnSaveSettings_.registerListener(e -> { + final File file = FileDialogs.save(frame_, + "Save the acquisition settings to JSON...", jsonFileSave_ + ); + if (file != null) { + // TODO: prompt if file exists + FileUtils.writeStringToFile(file.toString(), model_.acquisitions().settings().toPrettyJson()); + model_.studio().logs().logMessage("Acquisition settings saved to: " + file); + } + }); + + btnLoadSettings_.registerListener(e -> { + final File file = FileDialogs.openFile(frame_, + "Load the acquisition settings from JSON...", jsonFileLoad_ + ); + if (file != null) { + // TODO: prompt to overwrite settings + final String json = FileUtils.readFileToString(file.toString()); + model_.acquisitions().setAcquisitionSettingsAndBuilder( + ScapeAcquisitionSettings.fromJson(json, ScapeAcquisitionSettings.class)); + model_.studio().logs().logMessage("Acquisition settings loaded from: " + file); + } + }); + } + // Opens the file explorer to the save directory private void openDirectory(final String path) { final File directory = new File(path); if (directory.exists()) { From fc37c524cb8da3c70f164d5b445d400575a25925 Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 13 Apr 2026 18:13:31 -0700 Subject: [PATCH 4/4] save position list during acq if there are positions --- .../model/acquisitions/AcquisitionEngineScape.java | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 487e4679..fc04145a 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineScape.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineScape.java @@ -240,6 +240,16 @@ boolean run() { // TODO: put this in AcquisitionEngine base class, between setup and run once structure is better // save settings as JSON to the save directory FileUtils.writeStringToFile(saveDir + File.separator + "acq_settings.json", settingsJson); + final PositionList positionList = model_.studio().positions().getPositionList(); + if (positionList.getNumberOfPositions() > 0) { + try { + final String path = saveDir + File.separator + "position_list.pos"; + positionList.save(path); + model_.studio().logs().logMessage("Position list saved to " + path); + } catch (Exception e) { + model_.studio().logs().logError(e, "Could not save position list."); + } + } // This sets the preferred save mode for DefaultDatastore, this value // is used in the MMAcquisition constructor to set the Storage object.