diff --git a/src/main/java/org/micromanager/lightsheetmanager/LightSheetManager.java b/src/main/java/org/micromanager/lightsheetmanager/LightSheetManager.java index 5e53761d..925c1069 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/LightSheetManager.java +++ b/src/main/java/org/micromanager/lightsheetmanager/LightSheetManager.java @@ -81,12 +81,17 @@ public boolean setup() { final String message = "AcquisitionEngine not implemented for " + geometryType; studio_.logs().logError(message); errorText_ = message; - return false; // early exit => error + return false; // early exit => show error ui } // load settings userSettings_.load(); + // validate settings + if (!devices().validateCameras()) { + return false; // early exit => show error ui + } + // TODO: put this somewhere better, need to put this value into LightSheetEventAdapter for now LightSheetEventAdapter.isUsingMultipleCameras = deviceManager_.adapter().numSimultaneousCameras() > 1; diff --git a/src/main/java/org/micromanager/lightsheetmanager/api/data/CameraMode.java b/src/main/java/org/micromanager/lightsheetmanager/api/data/CameraMode.java index 9b86ea97..3e717e54 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/api/data/CameraMode.java +++ b/src/main/java/org/micromanager/lightsheetmanager/api/data/CameraMode.java @@ -77,7 +77,8 @@ public static boolean isCameraValid(final CameraLibrary camLib) { return camLib != CameraLibrary.UNKNOWN; } - public static CameraMode[] modesByDeviceLibrary(final CameraLibrary cameraLibrary) { + public static CameraMode[] modesByDeviceLibrary( + final CameraLibrary cameraLibrary, final GeometryType geometry) { ArrayList modes = new ArrayList<>(); if (isCameraValid(cameraLibrary)) { modes.add(CameraMode.EDGE); @@ -90,7 +91,7 @@ public static CameraMode[] modesByDeviceLibrary(final CameraLibrary cameraLibrar if (hasPseudoOverlapTrigger(cameraLibrary)) { modes.add(CameraMode.PSEUDO_OVERLAP); } - if (hasLightSheetTrigger(cameraLibrary)) { + if (geometry != GeometryType.SCAPE && hasLightSheetTrigger(cameraLibrary)) { modes.add(CameraMode.VIRTUAL_SLIT); } } diff --git a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/acquisition/CameraPanel.java b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/acquisition/CameraPanel.java index 541325dd..ec8d7d67 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/acquisition/CameraPanel.java +++ b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/acquisition/CameraPanel.java @@ -50,7 +50,7 @@ private void createUserInterface() { final CameraBase camera = model_.devices().firstImagingCamera(); if (camera != null) { final CameraLibrary camLib = CameraLibrary.fromString(camera.getDeviceLibrary()); - modes = CameraMode.modesByDeviceLibrary(camLib); + modes = CameraMode.modesByDeviceLibrary(camLib, model_.devices().adapter().geometry()); } cmbCameraMode_ = new ComboBox<>(modes, diff --git a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/channels/ChannelTablePanel.java b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/channels/ChannelTablePanel.java index a687f878..cee3bbc8 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/channels/ChannelTablePanel.java +++ b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/channels/ChannelTablePanel.java @@ -9,9 +9,10 @@ import org.micromanager.lightsheetmanager.LightSheetManager; import org.micromanager.lightsheetmanager.api.data.ChannelMode; import org.micromanager.lightsheetmanager.gui.components.SettingsListener; +import org.micromanager.lightsheetmanager.gui.utils.DialogUtils; import org.micromanager.lightsheetmanager.model.channels.ChannelSpec; -import javax.swing.JLabel; +import javax.swing.*; import java.util.Objects; /** @@ -60,7 +61,7 @@ private void createUserInterface() { cmbChannelMode_ = new ComboBox<>(ChannelMode.values(), model_.acquisitions().settings().channels().mode(), - 120, 22); + 140, 22); add(lblChannelGroup_, "split 2"); add(cmbChannelGroup_, "wrap"); @@ -125,8 +126,14 @@ private void createEventHandlers() { // select channel mode cmbChannelMode_.registerListener(() -> { - model_.acquisitions().settingsBuilder().channelBuilder() - .mode(cmbChannelMode_.getSelected()); + final ChannelMode selected = cmbChannelMode_.getSelected(); + model_.acquisitions().settingsBuilder().channelBuilder().mode(selected); + if (selected == ChannelMode.VOLUME_HW) { + SwingUtilities.invokeLater(() -> { + DialogUtils.showErrorMessage(cmbChannelMode_, "Not Implemented", + "Not implemented in SCAPE, please contact ASI to request this feature."); + }); + } }); } diff --git a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/setup/CameraPanel.java b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/setup/CameraPanel.java index a87f8fa3..027cba21 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/setup/CameraPanel.java +++ b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/setup/CameraPanel.java @@ -106,6 +106,10 @@ private void createEventHandlers() { break; case SCAPE: btnInvertedPath_.registerListener(() -> { + if (!btnInvertedPath_.isSelected()) { + model_.studio().live().setLiveModeOn(false); + return; + } closeLiveModeWindow(); final CameraBase camera = model_.devices().device("PreviewCamera"); if (camera != null) { @@ -127,6 +131,10 @@ private void createEventHandlers() { // live mode btnLiveMode_.registerListener(() -> { + if (!btnLiveMode_.isSelected()) { + model_.studio().live().setLiveModeOn(false); + return; + } closeLiveModeWindow(); final CameraBase camera = model_.devices().firstImagingCamera(); if (camera != null) { diff --git a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/setup/JoystickPanel.java b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/setup/JoystickPanel.java index cc9a8a6e..73353bf4 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/setup/JoystickPanel.java +++ b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/setup/JoystickPanel.java @@ -15,7 +15,7 @@ import java.util.Objects; /** - * Control an ASI joystick. + * Control an ASI joystick, left wheel, and right wheel. */ public class JoystickPanel extends Panel { @@ -26,6 +26,7 @@ public class JoystickPanel extends Panel { private String previousJoystick_; private String previousLeftWheel_; private String previousRightWheel_; + private Map methods_; private final LightSheetManager model_; @@ -38,18 +39,22 @@ public JoystickPanel(final LightSheetManager model) { } createUserInterface(); createEventHandlers(); + if (model_.devices().isUsingPLogic()) { + initHardware(); + } } private void createMap() { final ASIXYStage xyStage = model_.devices().device("SampleXY"); final ASIZStage zStage = model_.devices().device("SampleZ"); + final ASIZStage stage = model_.devices().device("IllumAngle"); final ASIScanner scanner = model_.devices().device("IllumSlice"); - final ASIPiezo piezo = model_.devices().device("ImagingFocus"); + final ASIPiezo piezo = model_.devices().device("ImagingFocus"); // set to the default values - previousJoystick_ = "None"; - previousLeftWheel_ = "None"; - previousRightWheel_ = "None"; + previousJoystick_ = model_.pluginSettings().joystickPanel().joystick(); + previousLeftWheel_ = model_.pluginSettings().joystickPanel().leftWheel(); + previousRightWheel_ = model_.pluginSettings().joystickPanel().rightWheel(); methods_ = new HashMap<>(); @@ -67,10 +72,16 @@ private void createMap() { scanner.js().inputX(Joystick.Input.NONE); scanner.js().inputY(Joystick.Input.NONE); }); - methods_.put("Light Sheet Tilt", () -> {}); // TODO: impl + methods_.put("Light Sheet Tilt", () -> stage.js().input(Joystick.Input.NONE)); methods_.put("Sample Height", () -> zStage.js().input(Joystick.Input.NONE)); } + private void initHardware() { + setJoystick(cmbJoystick_.getSelected()); + setWheel(cmbLeftWheel_.getSelected(), Joystick.Input.LEFT_WHEEL); + setWheel(cmbRightWheel_.getSelected(), Joystick.Input.RIGHT_WHEEL); + } + private void createUserInterface() { final JLabel lblJoystick = new JLabel("Joystick:"); final JLabel lblLeftWheel = new JLabel("Left Wheel:"); @@ -96,9 +107,9 @@ private void createUserInterface() { "Sample Height" }; - cmbJoystick_ = new ComboBox<>(joystickLabels, "None", 100, 24); - cmbLeftWheel_ = new ComboBox<>(wheelLabels, "None", 100, 24); - cmbRightWheel_ = new ComboBox<>(wheelLabels, "None", 100, 24); + cmbJoystick_ = new ComboBox<>(joystickLabels, previousJoystick_, 100, 24); + cmbLeftWheel_ = new ComboBox<>(wheelLabels, previousLeftWheel_, 100, 24); + cmbRightWheel_ = new ComboBox<>(wheelLabels, previousRightWheel_, 100, 24); add(lblJoystick, ""); add(cmbJoystick_, "wrap"); @@ -113,80 +124,70 @@ private void createEventHandlers() { // select joystick input cmbJoystick_.registerListener(() -> { final String selected = cmbJoystick_.getSelected(); + model_.pluginSettings().joystickPanel().joystick(selected); methods_.get(previousJoystick_).run(); // disable the previous device - switch (selected) { - case "None": - return; // early exit => do nothing - case "Scanner": - final ASIScanner scanner = model_.devices().device("IllumSlice"); - scanner.js().inputX(Joystick.Input.JOYSTICK_X); - scanner.js().inputY(Joystick.Input.JOYSTICK_Y); - break; - case "XYStage": - final ASIXYStage xyStage = model_.devices().device("SampleXY"); - xyStage.js().enabled(true); - break; - default: - break; - } - // track the previous device to disable later - previousJoystick_ = selected; + setJoystick(selected); + previousJoystick_ = selected; // track the previous device to disable later }); // select left wheel input cmbLeftWheel_.registerListener(() -> { final String selected = cmbLeftWheel_.getSelected(); + model_.pluginSettings().joystickPanel().leftWheel(selected); methods_.get(previousLeftWheel_).run(); // disable the previous device - switch (selected) { - case "Imaging Piezo": - final ASIPiezo piezo = model_.devices().device("ImagingFocus"); - piezo.js().input(Joystick.Input.LEFT_WHEEL); - break; - case "Imaging Slice": - final ASIScanner scanner = model_.devices().device("IllumSlice"); - scanner.js().inputX(Joystick.Input.LEFT_WHEEL); - break; - case "Light Sheet Tilt": - final ASIZStage stage = model_.devices().device("IllumAngle"); - stage.js().input(Joystick.Input.LEFT_WHEEL); - break; - case "Sample Height": - final ASIZStage zStage = model_.devices().device("SampleZ"); - zStage.js().input(Joystick.Input.LEFT_WHEEL); - break; - default: - break; - } - // track the previous device to disable later - previousLeftWheel_ = selected; + setWheel(selected, Joystick.Input.LEFT_WHEEL); + previousLeftWheel_ = selected; // track the previous device to disable later }); // select right wheel input cmbRightWheel_.registerListener(() -> { final String selected = cmbRightWheel_.getSelected(); + model_.pluginSettings().joystickPanel().rightWheel(selected); methods_.get(previousRightWheel_).run(); // disable the previous device - switch (selected) { - case "Imaging Piezo": - final ASIPiezo piezo = model_.devices().device("ImagingFocus"); - piezo.js().input(Joystick.Input.RIGHT_WHEEL); - break; - case "Imaging Slice": - final ASIScanner scanner = model_.devices().device("IllumSlice"); - scanner.js().inputX(Joystick.Input.RIGHT_WHEEL); - break; - case "Light Sheet Tilt": - final ASIZStage stage = model_.devices().device("IllumAngle"); - stage.js().input(Joystick.Input.RIGHT_WHEEL); - break; - case "Sample Height": - final ASIZStage zStage = model_.devices().device("SampleZ"); - zStage.js().input(Joystick.Input.RIGHT_WHEEL); - break; - default: - break; - } - // track the previous device to disable later - previousRightWheel_ = selected; + setWheel(selected, Joystick.Input.RIGHT_WHEEL); + previousRightWheel_ = selected; // track the previous device to disable later }); } + + private void setJoystick(final String selected) { + switch (selected) { + case "None": + return; // early exit => do nothing + case "Scanner": + final ASIScanner scanner = model_.devices().device("IllumSlice"); + scanner.js().inputX(Joystick.Input.JOYSTICK_X); + scanner.js().inputY(Joystick.Input.JOYSTICK_Y); + break; + case "XYStage": + final ASIXYStage xyStage = model_.devices().device("SampleXY"); + xyStage.js().enabled(true); + break; + default: + break; + } + } + + private void setWheel(final String selected, final Joystick.Input target) { + switch (selected) { + case "Imaging Piezo": + final ASIPiezo piezo = model_.devices().device("ImagingFocus"); + piezo.js().input(target); + break; + case "Imaging Slice": + final ASIScanner scanner = model_.devices().device("IllumSlice"); + scanner.js().inputY(target); + break; + case "Light Sheet Tilt": + final ASIZStage stage = model_.devices().device("IllumAngle"); + stage.js().input(target); + break; + case "Sample Height": + final ASIZStage zStage = model_.devices().device("SampleZ"); + zStage.js().input(target); + break; + default: + break; + } + } + } diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/DeviceManager.java b/src/main/java/org/micromanager/lightsheetmanager/model/DeviceManager.java index 4636fca1..dd953d2f 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/DeviceManager.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/DeviceManager.java @@ -335,21 +335,78 @@ public String[] imagingCameraNames() { } public CameraBase[] imagingCameras() { - String[] cameraNames; - ArrayList names = new ArrayList<>(); + final List 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); + final String[] cameraNames = names.toArray(String[]::new); return Arrays.stream(cameraNames) .map(name -> (CameraBase)deviceMap_.get(name)) .filter(Objects::nonNull) .toArray(CameraBase[]::new); } + /** + * Returns {@code true} if all cameras in the settings are valid. + * This is used to detect changes in pre-init properties. + * + * @return {@code true} if the cameras are valid + */ + public boolean validateCameras() { + // cameras in settings + final CameraData[] cameras = model_.acquisitions().settings().imagingCameraOrder(); + + // check for no cameras + if (cameras.length == 0) { + final String message = "No cameras found in the settings."; + model_.studio().logs().logError(message); + if (DialogUtils.showYesNoDialog(null, "No Imaging Cameras", + message + "\nWould you like to use the default imaging camera order?")) { + useDefaultImagingCameraOrder(); + return true; + } else { + model_.setErrorText(message); + return false; + } + } + + // valid camera names + final List validNames = Arrays.asList(imagingCameraNames()); + + // check for camera name mismatches + for (CameraData camera : cameras) { + if (!validNames.contains(camera.name())) { + final String message = "Camera in settings not found in hardware: " + camera.name() + + ", consider creating a new user profile if the pre-init properties changed."; + model_.studio().logs().logError(message); + model_.setErrorText(message); + return false; + } + } + + return true; + } + + /** + * Used to create the default imaging camera order. + */ + public void useDefaultImagingCameraOrder() { + final List cameras = new ArrayList<>(); + final String[] cameraNames = imagingCameraNames(); + for (String name : cameraNames) { + // Note: there is no ui control to change the active state of the + // camera when there is only 1 camera, so make sure it's active. + cameras.add(new CameraData(name, cameraNames.length == 1)); + } + // update settings with default camera order + model_.acquisitions().settingsBuilder() + .imagingCameraOrder(cameras.toArray(new CameraData[0])); + model_.acquisitions().updateAcquisitionSettings(); + } + public DeviceAdapter adapter() { return (DeviceAdapter)deviceMap_.get("LightSheetDeviceManager"); } diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/PluginSettings.java b/src/main/java/org/micromanager/lightsheetmanager/model/PluginSettings.java index c57bcff1..e411048e 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/PluginSettings.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/PluginSettings.java @@ -10,8 +10,14 @@ public class PluginSettings { private boolean isPollingPositions_ = true; + private final JoystickData joystick_ = new JoystickData(); + private final XYZGrid xyzGrid_ = new XYZGrid(); + public JoystickData joystickPanel() { + return joystick_; + } + public XYZGrid xyzGrid() { return xyzGrid_; } @@ -37,4 +43,40 @@ public static PluginSettings fromJson(final String json) { return new Gson().fromJson(json, PluginSettings.class); } + public static class JoystickData { + + private String joystick_; + private String leftWheel_; + private String rightWheel_; + + JoystickData() { + joystick_ = "None"; + leftWheel_ = "None"; + rightWheel_ = "None"; + } + + public String joystick() { + return joystick_; + } + + public String leftWheel() { + return leftWheel_; + } + + public String rightWheel() { + return rightWheel_; + } + + public void joystick(final String joystick) { + joystick_ = joystick; + } + + public void leftWheel(final String leftWheel) { + leftWheel_ = leftWheel; + } + + public void rightWheel(final String rightWheel) { + rightWheel_ = rightWheel; + } + } } diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/UserSettings.java b/src/main/java/org/micromanager/lightsheetmanager/model/UserSettings.java index 8902e712..7c8060c8 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/UserSettings.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/UserSettings.java @@ -90,6 +90,8 @@ public void load() { if (json.equals(SETTINGS_NOT_FOUND)) { model_.studio().logs().logDebugMessage( "settings not found, using default settings for " + geometryType); + // add the cameras to the imaging camera order + model_.devices().useDefaultImagingCameraOrder(); } else { // validate user settings and create AcquisitionSettings object final Optional settingsJson = validateUserSettings(json); 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 d01462fa..936c1d50 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineScape.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineScape.java @@ -619,6 +619,7 @@ public void close() { AcquisitionEvent baseEvent = new AcquisitionEvent(currentAcquisition_); if (acqSettings_.isUsingTimePoints()) { baseEvent.setAxisPosition(LightSheetEventAdapter.TIME_AXIS, timeIndex); + baseEvent.setMinimumStartTime((long) (timeIndex * (model_.acquisitions().settings().timePointInterval() * 1000.0))); } // Loop 2: XY positions for (int positionIndex = 0; positionIndex < numPositions; positionIndex++) { @@ -788,6 +789,9 @@ boolean finish() { } } + // unregister to stop ghost events + studio_.events().unregisterForEvents(this); + // start polling for navigation panel if (isPolling_) { studio_.logs().logMessage("started position polling after acquisition"); diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/LightSheetEventAdapter.java b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/LightSheetEventAdapter.java index 68299acd..0f233ccf 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/LightSheetEventAdapter.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/LightSheetEventAdapter.java @@ -195,7 +195,18 @@ public AcquisitionEvent next() { cameraEvent.setAxisPosition(CAMERA_AXIS, cameraIndex_ + (currentChannelIndex_ * cameraDeviceNames_.length)); } else { - cameraEvent.setAxisPosition(CAMERA_AXIS, cameraIndex_); + Object position = event.getAxisPosition(CAMERA_AXIS); + int baseIndex = 0; + + if (position != null) { + try { + baseIndex = Integer.parseInt(position.toString()); + } catch (NumberFormatException e) { + // ignore => number already assigned + } + } + + cameraEvent.setAxisPosition(CAMERA_AXIS, baseIndex + cameraIndex_); } cameraIndex_++; return cameraEvent; @@ -218,6 +229,7 @@ public boolean hasNext() { public AcquisitionEvent next() { AcquisitionEvent sliceEvent = event.copy(); sliceEvent.setAxisPosition(AcqEngMetadata.Z_AXIS, zIndex_); + // System.out.println("Final Event Axes: " + sliceEvent.getAxesAsJSONString()); // The tiger controller handles Z axis, so no need to add the actual Z position zIndex_++; return sliceEvent; @@ -244,7 +256,6 @@ public AcquisitionEvent next() { } timePointEvent.setTimeIndex(frameIndex_); frameIndex_++; - return timePointEvent; } }; @@ -253,7 +264,7 @@ public AcquisitionEvent next() { /** * Make an iterator for events for each active channel * - * @param channelList + * @param channelList the list of channels to iterate over * @return */ public static Function> channels( @@ -299,7 +310,7 @@ public AcquisitionEvent next() { * the axes that assume the order in the list provided correspond to the * desired indices * - * @param positionList + * @param positionList the list of positions or iterate over * @return */ public static Function> positions(