From 409d53f79ed91339baf2766a39f5d8d5632d2e62 Mon Sep 17 00:00:00 2001 From: Brandon Date: Fri, 17 Apr 2026 14:54:16 -0700 Subject: [PATCH] add settings adapter to convert old settings files --- .../api/AcquisitionSettings.java | 2 +- .../api/internal/BaseAcquisitionSettings.java | 2 +- .../gui/tabs/AutofocusTab.java | 12 +- .../gui/tabs/acquisition/SavePanel.java | 19 +- .../model/SettingsAdapter.java | 277 ++++++++++++++++++ .../lightsheetmanager/model/UserSettings.java | 2 +- .../model/autofocus/AutofocusAdapter.java | 2 +- 7 files changed, 304 insertions(+), 12 deletions(-) create mode 100644 src/main/java/org/micromanager/lightsheetmanager/model/SettingsAdapter.java diff --git a/src/main/java/org/micromanager/lightsheetmanager/api/AcquisitionSettings.java b/src/main/java/org/micromanager/lightsheetmanager/api/AcquisitionSettings.java index 8d960ee9..0ed60470 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/api/AcquisitionSettings.java +++ b/src/main/java/org/micromanager/lightsheetmanager/api/AcquisitionSettings.java @@ -50,7 +50,7 @@ interface Builder> { * * @return the autofocus settings builder */ - DefaultAutofocusSettings.Builder autofocusSettingsBuilder(); + DefaultAutofocusSettings.Builder autofocusBuilder(); // TODO: document this /** diff --git a/src/main/java/org/micromanager/lightsheetmanager/api/internal/BaseAcquisitionSettings.java b/src/main/java/org/micromanager/lightsheetmanager/api/internal/BaseAcquisitionSettings.java index 1dba4361..8cd0446a 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/api/internal/BaseAcquisitionSettings.java +++ b/src/main/java/org/micromanager/lightsheetmanager/api/internal/BaseAcquisitionSettings.java @@ -96,7 +96,7 @@ public T saveMode(final DataStorage.SaveMode saveMode) { } @Override - public DefaultAutofocusSettings.Builder autofocusSettingsBuilder() { + public DefaultAutofocusSettings.Builder autofocusBuilder() { return afBuilder_; } diff --git a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/AutofocusTab.java b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/AutofocusTab.java index 4868cd17..b83f553b 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/AutofocusTab.java +++ b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/AutofocusTab.java @@ -194,25 +194,25 @@ private void createEventHandlers() { // general autofocus settings cbxShowImages_.registerListener(() -> model_.acquisitions().settingsBuilder() - .autofocusSettingsBuilder().showImages(cbxShowImages_.isSelected())); + .autofocusBuilder().showImages(cbxShowImages_.isSelected())); cbxShowGraph_.registerListener(() -> model_.acquisitions().settingsBuilder() - .autofocusSettingsBuilder().showGraph(cbxShowGraph_.isSelected())); + .autofocusBuilder().showGraph(cbxShowGraph_.isSelected())); spnNumImages_.registerListener(() -> model_.acquisitions().settingsBuilder() - .autofocusSettingsBuilder().numImages(spnNumImages_.getInt())); + .autofocusBuilder().numImages(spnNumImages_.getInt())); spnStepSize_.registerListener(() -> model_.acquisitions().settingsBuilder() - .autofocusSettingsBuilder().stepSizeUm(spnStepSize_.getDouble())); + .autofocusBuilder().stepSizeUm(spnStepSize_.getDouble())); // spnToleranceUm_.registerListener(() -> model_.acquisitions().settingsBuilder() // .autofocusSettingsBuilder().toleranceUm(spnToleranceUm_.getDouble())); cmbAutofocusMode_.registerListener(() -> model_.acquisitions().settingsBuilder() - .autofocusSettingsBuilder().mode(cmbAutofocusMode_.getSelected())); + .autofocusBuilder().mode(cmbAutofocusMode_.getSelected())); cmbScoringMethod_.registerListener(() -> model_.acquisitions().settingsBuilder() - .autofocusSettingsBuilder().scoringMethod(cmbScoringMethod_.getSelected())); + .autofocusBuilder().scoringMethod(cmbScoringMethod_.getSelected())); btnRunAutofocus_.registerListener(() -> model_.acquisitions().autofocus().run()); 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 b67c00e4..038cedc4 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 @@ -14,6 +14,7 @@ import org.micromanager.lightsheetmanager.gui.data.Icons; import org.micromanager.lightsheetmanager.gui.utils.DialogUtils; import org.micromanager.lightsheetmanager.model.DataStorage; +import org.micromanager.lightsheetmanager.model.SettingsAdapter; import org.micromanager.lightsheetmanager.model.utils.FileUtils; import javax.swing.JLabel; @@ -36,6 +37,7 @@ public class SavePanel extends Panel implements SettingsListener { private Button btnSaveSettings_; private Button btnLoadSettings_; + private Button btnConvertSettings_; private final FileDialogs.FileType directorySelect_; private final FileDialogs.FileType jsonFileSave_; @@ -114,19 +116,21 @@ public void createUserInterface() { btnSaveSettings_ = new Button("Save", 60, 20); btnLoadSettings_ = new Button("Load", 60, 20); + btnConvertSettings_ = new Button("Convert", 72, 20); add(lblSaveDirectory, ""); add(txtSaveDirectory_, ""); add(btnBrowse_, ""); add(btnOpen_, "wrap"); add(lblSaveFileName, ""); - add(txtSaveFileName_, "wrap"); + add(txtSaveFileName_, "span 3, wrap"); add(lblSaveMode, ""); add(cbxSaveMode_, "split 2, wrap"); add(cbxSaveWhileAcquiring_, "span 2, wrap"); add(new JLabel("Acq Settings:"), ""); - add(btnSaveSettings_, "split 2"); + add(btnSaveSettings_, "split 3, span 3"); add(btnLoadSettings_, ""); + add(btnConvertSettings_, ""); } public void createEventHandlers() { @@ -187,6 +191,17 @@ public void createEventHandlers() { } }); + btnConvertSettings_.registerListener(() -> { + final File file = FileDialogs.openFile(frame_, + "Load the acquisition settings from JSON...", jsonFileLoad_ + ); + if (file != null) { + final String json = FileUtils.readFileToString(file.toString()); + SettingsAdapter adapter = new SettingsAdapter(model_); + adapter.convert(json); + //System.out.println(json); + } + }); } // Opens the file explorer to the save directory diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/SettingsAdapter.java b/src/main/java/org/micromanager/lightsheetmanager/model/SettingsAdapter.java new file mode 100644 index 00000000..59a7d9d5 --- /dev/null +++ b/src/main/java/org/micromanager/lightsheetmanager/model/SettingsAdapter.java @@ -0,0 +1,277 @@ +package org.micromanager.lightsheetmanager.model; + +import mmcorej.org.json.JSONException; +import mmcorej.org.json.JSONObject; +import org.micromanager.lightsheetmanager.LightSheetManager; +import org.micromanager.lightsheetmanager.api.data.AcquisitionMode; +import org.micromanager.lightsheetmanager.api.data.CameraMode; +import org.micromanager.lightsheetmanager.api.data.ChannelMode; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; + +// TODO: add better validation and error handling + +/** + * An adapter that translates Micro-Manager 1.4 acquisition settings from + * the SCOPE plugin to Light Sheet Manager settings for Micro-Manager 2.0. + */ +public class SettingsAdapter { + + private final LightSheetManager model_; + + // maps old settings to conversion methods + private final Map> registry = new HashMap<>(); + + private static final Map ACQUISITION_MODE_MAP = Map.of( + "NO_SCAN", AcquisitionMode.NO_SCAN, + "STAGE_SCAN", AcquisitionMode.STAGE_SCAN, + "SLICE_SCAN_ONLY", AcquisitionMode.GALVO_SCAN + ); + + private static final Map CHANNEL_MODE_MAP = Map.of( + "VOLUME", ChannelMode.VOLUME, + "VOLUME_HW", ChannelMode.VOLUME_HW, + "SLICE_HW", ChannelMode.SLICE_HW + ); + + private static final Map CAMERA_MODE_MAP = Map.of( + "INTERNAL", CameraMode.INTERNAL, + "EDGE", CameraMode.EDGE, + "OVERLAP", CameraMode.OVERLAP, + "LEVEL", CameraMode.LEVEL, + "PSEUDO_OVERLAP", CameraMode.PSEUDO_OVERLAP, + "LIGHT_SHEET", CameraMode.VIRTUAL_SLIT + ); + + public SettingsAdapter(final LightSheetManager model) { + model_ = Objects.requireNonNull(model); + + registry.put("spimMode", + value -> model_.acquisitions().settingsBuilder() + .acquisitionMode(convertAcquisitionMode((String) value))); + + registry.put("isStageScanning", + value -> model_.acquisitions().settingsBuilder() + .stageScanBuilder().enabled((boolean) value)); + + // isStageStepping - not used, no longer support this kind of stage + + registry.put("useTimepoints", + value -> model_.acquisitions().settingsBuilder() + .useTimePoints((boolean) value)); + + registry.put("numTimepoints", + value -> model_.acquisitions().settingsBuilder() + .numTimePoints((int) value)); + + registry.put("timepointInterval", + value -> model_.acquisitions().settingsBuilder() + .timePointInterval((double) value)); + + registry.put("useMultiPositions", + value -> model_.acquisitions().settingsBuilder() + .useMultiplePositions((boolean) value)); + + registry.put("useChannels", + value -> model_.acquisitions().settingsBuilder() + .channelBuilder().enabled((boolean) value)); + + registry.put("channelMode", + value -> model_.acquisitions().settingsBuilder() + .channelBuilder().mode(convertChannelMode((String) value))); + + // numChannels - not used, computed by the plugin based on the size of the array + + // TODO: error handling, what happens if the group does not exist, etc? +// registry.put("channels", value -> { +// if (value instanceof JSONArray) { +// var channels = new ArrayList(); +// final JSONArray channelsJson = (JSONArray) value; +// for (int i = 0; i < channelsJson.length(); i++) { +// try { +// final JSONObject channelObj = channelsJson.getJSONObject(i); +// +// final boolean use = channelObj.getBoolean("useChannel_"); +// final String group = channelObj.getString("group_"); +// final String config = channelObj.getString("config_"); +// final double offset = channelObj.getDouble("offset_"); +// +// channels.add(new ChannelSpec(use, group, config, offset)); +// +// } catch (JSONException e) { +// model_.studio().logs().logError("Error parsing channel at index " + i); +// } +// } +// +// // add channels to +// model_.acquisitions().settingsBuilder() +// .channelBuilder().data(channels.toArray(new ChannelSpec[0])); +// } +// }); + + registry.put("channelGroup", + value -> model_.acquisitions().settingsBuilder() + .channelBuilder().group((String) value)); + + registry.put("useAutofocus", + value -> model_.acquisitions().settingsBuilder() + .autofocusBuilder().enabled((boolean) value)); + + // useMovementCorrection - not used, old planar correction feature + // acquireBothCamerasSimultaneously - not used, uses camera order to specify which cameras are active + // numSides - not used, SCAPE microscopes only have a single view + // firstSideIsA - not used, SCAPE microscopes only have a single view + + registry.put("delayBeforeSide", + value -> model_.acquisitions().settingsBuilder() + .volumeBuilder().delayBeforeView((double) value)); + + registry.put("numSlices", + value -> model_.acquisitions().settingsBuilder() + .volumeBuilder().slicesPerView((int) value)); + + registry.put("stepSizeUm", + value -> model_.acquisitions().settingsBuilder() + .volumeBuilder().sliceStepSize((double) value)); + + registry.put("minimizeSlicePeriod", + value -> model_.acquisitions().settingsBuilder() + .sliceBuilder().periodMinimized((boolean) value)); + + registry.put("desiredSlicePeriod", + value -> model_.acquisitions().settingsBuilder() + .sliceBuilder().period((double) value)); + + registry.put("desiredLightExposure", + value -> model_.acquisitions().settingsBuilder() + .sliceBuilder().sampleExposure((double) value)); + + // centerAtCurrentZ - not used + + registry.put("sliceTiming", value -> { + final JSONObject timing = (JSONObject) value; + final Iterator keys = timing.keys(); + + while (keys.hasNext()) { + final String key = keys.next(); + try { + final Object val = timing.get(key); + switch (key) { + case "scanDelay": + model_.acquisitions().settingsBuilder() + .timingBuilder().delayBeforeScan((double) val); + break; + case "scanNum": + model_.acquisitions().settingsBuilder() + .timingBuilder().scansPerSlice((int) val); + break; + case "scanPeriod": + model_.acquisitions().settingsBuilder() + .timingBuilder().scanDuration((double) val); + break; + case "laserDelay": + model_.acquisitions().settingsBuilder() + .timingBuilder().delayBeforeLaser((double) val); + break; + case "laserDuration": + model_.acquisitions().settingsBuilder() + .timingBuilder().laserTriggerDuration((double) val); + break; + case "cameraDelay": + model_.acquisitions().settingsBuilder() + .timingBuilder().delayBeforeCamera((double) val); + break; + case "cameraDuration": + model_.acquisitions().settingsBuilder() + .timingBuilder().cameraTriggerDuration((double) val); + break; + case "cameraExposure": + model_.acquisitions().settingsBuilder() + .timingBuilder().cameraExposure((double) val); + break; + default: + break; // skip key + } + } catch (JSONException e) { + model_.studio().logs().showError( + "Error parsing the timing settings!"); + } + } + }); + + registry.put("cameraMode", + value -> model_.acquisitions().settingsBuilder() + .cameraMode(convertCameraMode((String) value))); + + // hardwareTimepoints - computed by the acquisition setup + // separateTimepoints - not used + // usePathPresets - not used + + registry.put("useAdvancedSliceTiming", + value -> model_.acquisitions().settingsBuilder() + .useAdvancedTiming((boolean) value)); + + // numSimultCameras - this is set as pre-init property in the LSM device adapter + + registry.put("saveDirectoryRoot", + value -> model_.acquisitions().settingsBuilder() + .saveDirectory((String) value)); + + registry.put("saveNamePrefix", + value -> model_.acquisitions().settingsBuilder() + .saveNamePrefix((String) value)); + + // durationSliceMs - compute after we load the settings + // durationVolumeMs - compute after we load the settings + // durationTotalSec - compute after we load the settings + // pluginVersion - not used + } + + public void convert(final String json) { + // convert to JSON object + JSONObject obj; + try { + obj = new JSONObject(json); + } catch (JSONException e) { + model_.studio().logs().showError(e, + "Failed to convert old settings to JSON!"); + return; // early exit => no json to convert! + } + + // update settings from the old file + final Iterator keys = obj.keys(); + while (keys.hasNext()) { + final String key = keys.next(); + if (registry.containsKey(key)) { + try { + final Object value = obj.get(key); + registry.get(key).accept(value); + } catch (Exception e) { + model_.studio().logs().logError( + "Could not migrate key: " + key + " - " + e.getMessage()); + } + } + } + + // build the settings from builder and update the ui + model_.acquisitions().updateAcquisitionSettings(); + model_.userSettings().notifyListeners(model_.acquisitions().settings()); + } + + private AcquisitionMode convertAcquisitionMode(final String mode) { + return ACQUISITION_MODE_MAP.getOrDefault(mode, AcquisitionMode.NO_SCAN); + } + + private ChannelMode convertChannelMode(final String mode) { + return CHANNEL_MODE_MAP.getOrDefault(mode, ChannelMode.VOLUME); + } + + private CameraMode convertCameraMode(final String mode) { + return CAMERA_MODE_MAP.getOrDefault(mode, CameraMode.EDGE); + } + +} diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/UserSettings.java b/src/main/java/org/micromanager/lightsheetmanager/model/UserSettings.java index c6bcf870..4dc4f43d 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/UserSettings.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/UserSettings.java @@ -254,7 +254,7 @@ public void addChangeListener(final SettingsListener listener) { * * @param settings the acquisition settings */ - private void notifyListeners(final AcquisitionSettings settings) { + public void notifyListeners(final AcquisitionSettings settings) { // always update listeners on the EDT regardless of where we are called from if (SwingUtilities.isEventDispatchThread()) { for (SettingsListener listener : listeners) { diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/autofocus/AutofocusAdapter.java b/src/main/java/org/micromanager/lightsheetmanager/model/autofocus/AutofocusAdapter.java index 71de8f85..cb9758ba 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/autofocus/AutofocusAdapter.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/autofocus/AutofocusAdapter.java @@ -26,7 +26,7 @@ public void run() { // .acquisitions().settings().autofocusSettings(); final DefaultAutofocusSettings afSettings = model_ - .acquisitions().settingsBuilder().autofocusSettingsBuilder().build(); + .acquisitions().settingsBuilder().autofocusBuilder().build(); // save the original plugin settings final AutofocusManager manager = model_.studio().getAutofocusManager();