Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds hook that runs after all hardware except for the Z drive has been moved in postion. #118

Merged
merged 6 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.micro-manager.acqengj</groupId>
<artifactId>AcqEngJ</artifactId>
<version>0.36.0</version>
<version>0.37.0</version>
<packaging>jar</packaging>
<name>AcqEngJ</name>
<description>Java-based Acquisition engine for Micro-Manager</description>
Expand Down
33 changes: 20 additions & 13 deletions src/main/java/org/micromanager/acqj/api/AcquisitionAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public interface AcquisitionAPI {
// This hook runs before changes to the hardware (corresponding to the instructions in the
// event) are made
int BEFORE_HARDWARE_HOOK = 1;
// This hook runs after all hardware except for the Z-drive has been set in place. This is
// an ideal place for things such as autofocussing.
int BEFORE_Z_DRIVE_HOOK = 5;

// This hook runs after changes to the hardware took place, but before camera exposure
// (either a snap or a sequence) is started
Expand All @@ -38,17 +41,17 @@ public interface AcquisitionAPI {
int AFTER_EXPOSURE_HOOK = 4;

/**
* Call to ready acquisition to start receiving acquisiton events. No more hooks
* or image processors should be added after this has been called
* Call to ready acquisition to start receiving acquisition events. No more hooks
* or image processors should be added after this has been called.
*
* This method is no longer needed, because it is called automatically when
* <p>This method is no longer needed, because it is called automatically when
* the first call to submitEventIterator is made
*/
@Deprecated
public void start();

/**
* Add a AcqNotificationListener to receive asynchronous notifications about the acquisition
* Add a AcqNotificationListener to receive asynchronous notifications about the acquisition.
*/
public void addAcqNotificationListener(AcqNotificationListener listener);

Expand All @@ -75,30 +78,34 @@ public interface AcquisitionAPI {
public boolean areEventsFinished();

/**
* Blcok until all acquisitions events are finished or timeout is reached
* @param timeoutSeconds
* Block until all acquisitions events are finished or timeout is reached.
*
* @param timeoutSeconds wait a maximum of this many seconds.
*/
public void blockUntilEventsFinished(Double timeoutSeconds) throws InterruptedException;

/**
* Cancel any pending events and shutdown
*/
/**
* Cancel any pending events and shutdown.
*/
public void abort();

/**
* Abort, and provide an exception that is the reason for the abort. This
* is useful for passing exceptions across threads
* @param e
* is useful for passing exceptions across threads.
*
* @param e Exception that caused the abort.
*/
public void abort(Exception e);

/**
* Has abort been called?
* Wether abort has been called.
*
* @return true if an abort has been requested.
*/
public boolean isAbortRequested();

/**
* return if acquisition is paused (i.e. not acquiring new data but not
* Return if acquisition is paused (i.e. not acquiring new data but not
* finished)
*
* @return
Expand Down
160 changes: 106 additions & 54 deletions src/main/java/org/micromanager/acqj/internal/Engine.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public Future submitEventIterator(Iterator<AcquisitionEvent> eventIterator) {
}
}

//Wait here is acquisition is paused
//Wait here if acquisition is paused
while (event.acquisition_.isPaused()) {
try {
Thread.sleep(5);
Expand All @@ -162,8 +162,9 @@ public Future submitEventIterator(Iterator<AcquisitionEvent> eventIterator) {
//cancelled
return;
} catch (ExecutionException ex) {
//some problem with acuisition, abort and propagate exception
ex.printStackTrace();
//some problem with acquisition, abort and propagate exception
core_.logMessage(ex.getMessage());
core_.logMessage(ex.getStackTrace().toString());
acq.abort(ex);
throw new RuntimeException(ex);
}
Expand Down Expand Up @@ -322,13 +323,33 @@ private void executeAcquisitionEvent(AcquisitionEvent event) throws InterruptedE
}
abortIfRequested(event, null);
}

HardwareSequences hardwareSequencesInProgress = new HardwareSequences();
try {
prepareHardware(event, hardwareSequencesInProgress);
} catch (HardwareControlException e) {
stopHardwareSequences(hardwareSequencesInProgress);
throw e;
}
event.acquisition_.postNotification( new AcqNotification(
AcqNotification.Hardware.class, event.getAxesAsJSONString(), AcqNotification.Hardware.PRE_Z_DRIVE));
for (AcquisitionHook h : event.acquisition_.getBeforeZDriveHooks()) {
event = h.run(event);
if (event == null) {
return; //The hook cancelled this event
}
abortIfRequested(event, hardwareSequencesInProgress);
}

try {
startZDrive(event, hardwareSequencesInProgress);
} catch (HardwareControlException e) {
stopHardwareSequences(hardwareSequencesInProgress);
throw e;
}
//keep track of last event to know what state the hardware was in without having to query it
lastEvent_ = event.getSequence() == null ? event : event.getSequence().get(event.getSequence().size() - 1);

event.acquisition_.postNotification( new AcqNotification(
AcqNotification.Hardware.class, event.getAxesAsJSONString(), AcqNotification.Hardware.POST_HARDWARE));
for (AcquisitionHook h : event.acquisition_.getAfterHardwareHooks()) {
Expand All @@ -338,6 +359,7 @@ private void executeAcquisitionEvent(AcquisitionEvent event) throws InterruptedE
}
abortIfRequested(event, hardwareSequencesInProgress);
}

// Hardware hook may have modified wait time, so check again if we should
// pause until the minimum start time of the event has occurred.
while (event.getMinimumStartTimeAbsolute() != null &&
Expand Down Expand Up @@ -664,12 +686,10 @@ private void prepareHardware(final AcquisitionEvent event,
HardwareSequences hardwareSequencesInProgress) throws HardwareControlException {
//Get the hardware specific to this acquisition
final String xyStage = core_.getXYStageDevice();
final String zStage = core_.getFocusDevice();
final String slm = core_.getSLMDevice();
//prepare sequences if applicable
if (event.getSequence() != null) {
try {
DoubleVector zSequence = event.isZSequenced() ? new DoubleVector() : null;
DoubleVector xSequence = event.isXYSequenced() ? new DoubleVector() : null;
DoubleVector ySequence = event.isXYSequenced() ? new DoubleVector() : null;
DoubleVector exposureSequence_ms =event.isExposureSequenced() ? new DoubleVector() : null;
Expand All @@ -678,9 +698,6 @@ private void prepareHardware(final AcquisitionEvent event,
core_.getConfigData(group, event.getSequence().get(0).getConfigPreset());
LinkedList<StrVector> propSequences = event.isConfigGroupSequenced() ? new LinkedList<StrVector>() : null;
for (AcquisitionEvent e : event.getSequence()) {
if (zSequence != null) {
zSequence.add(e.getZPosition());
}
if (xSequence != null) {
xSequence.add(e.getXPosition());
}
Expand Down Expand Up @@ -718,15 +735,6 @@ private void prepareHardware(final AcquisitionEvent event,
core_.loadXYStageSequence(xyStage, xSequence, ySequence);
hardwareSequencesInProgress.deviceNames.add(xyStage);
}
if (event.isZSequenced()) {
// at least some zStages freak out (in this case, NIDAQ board) when you
// try to load a sequence while the sequence is still running. Nothing in
// the engine stops a stage sequence if all goes well.
// Stopping a sequence if it is not running hopefully will not harm anyone.
core_.stopStageSequence(zStage);
core_.loadStageSequence(zStage, zSequence);
hardwareSequencesInProgress.deviceNames.add(zStage);
}
if (event.isConfigGroupSequenced()) {
for (int i = 0; i < config.size(); i++) {
PropertySetting ps = config.getSetting(i);
Expand Down Expand Up @@ -758,38 +766,6 @@ private void prepareHardware(final AcquisitionEvent event,
if (lastEvent_ != null && lastEvent_.acquisition_ != event.acquisition_) {
lastEvent_ = null; //update all hardware if switching to a new acquisition
}
/////////////////////////////Z stage////////////////////////////////////////////
loopHardwareCommandRetries(new Runnable() {
@Override
public void run() {
try {
if (event.isZSequenced()) {
core_.startStageSequence(zStage);
} else {
Double previousZ = lastEvent_ == null ? null : lastEvent_.getSequence() == null ? lastEvent_.getZPosition() :
lastEvent_.getSequence().get(0).getZPosition();
Double currentZ = event.getSequence() == null ? event.getZPosition() : event.getSequence().get(0).getZPosition();
if (currentZ == null) {
return;
}
boolean change = previousZ == null || !previousZ.equals(currentZ);
if (!change) {
return;
}

//wait for it to not be busy (is this even needed?)
core_.waitForDevice(zStage);
//Move Z
core_.setPosition(zStage, currentZ);
//wait for move to finish
core_.waitForDevice(zStage);
}
} catch (Exception ex) {
throw new HardwareControlException(ex.getMessage());
}

}
}, "Moving Z device");

/////////////////////////////Other stage devices ////////////////////////////////////////////
loopHardwareCommandRetries(new Runnable() {
Expand Down Expand Up @@ -819,10 +795,14 @@ public void run() {
core_.waitForDevice(stageDeviceName);
//Move Z
core_.setPosition(stageDeviceName, event.getStageSingleAxisStagePosition(stageDeviceName));
}
// wait only after having started to move all stages.
// there is a possibility this approach creates complications for certain devices
// but could bring significant speed advantages
for (String stageDeviceName : event.getStageDeviceNames()) {
//wait for move to finish
core_.waitForDevice(stageDeviceName);
}
// }
} catch (Exception ex) {
throw new HardwareControlException(ex.getMessage());
}
Expand Down Expand Up @@ -974,9 +954,76 @@ public void run() {

}
}, "Changing additional properties");
}

/**
* Separate function to set the ZDrive. This should happen after
* all other devices are in place. This order makes it possible to
* create a hook that can be used to run a (hardware) autofocus routine
* after all other devices are in place and before the zDrive is set.
*
* @param event acquisition event with all information we need.
*/
private void startZDrive(final AcquisitionEvent event,
HardwareSequences hardwareSequencesInProgress) throws HardwareControlException {
final String zStage = core_.getFocusDevice();
if (event.getSequence() != null) {
DoubleVector zSequence = event.isZSequenced() ? new DoubleVector() : null;
for (AcquisitionEvent e : event.getSequence()) {
if (zSequence != null) {
zSequence.add(e.getZPosition());
}
}
try {
if (event.isZSequenced()) {
// at least some zStages freak out (in this case, NIDAQ board) when you
// try to load a sequence while the sequence is still running. Nothing in
// the engine stops a stage sequence if all goes well.
// Stopping a sequence if it is not running hopefully will not harm anyone.
core_.stopStageSequence(zStage);
core_.loadStageSequence(zStage, zSequence);
hardwareSequencesInProgress.deviceNames.add(zStage);
}
} catch (Exception ex) {
ex.printStackTrace();
throw new HardwareControlException(ex.getMessage());
}
}

////////////////////////////Set the Z Drive////////////////////////////////////////////
loopHardwareCommandRetries(new Runnable() {
@Override
public void run() {
try {
if (event.isZSequenced()) {
core_.startStageSequence(zStage);
} else {
Double previousZ = lastEvent_ == null ? null
: lastEvent_.getSequence() == null ? lastEvent_.getZPosition()
: lastEvent_.getSequence().get(0).getZPosition();
Double currentZ = event.getSequence() == null ? event.getZPosition()
: event.getSequence().get(0).getZPosition();
if (currentZ == null) {
return;
}
boolean change = previousZ == null || !previousZ.equals(currentZ);
if (!change) {
return;
}

//wait for it to not be busy (is this even needed?)
core_.waitForDevice(zStage);
//Move Z
core_.setPosition(zStage, currentZ);
//wait for move to finish
core_.waitForDevice(zStage);
}
} catch (Exception ex) {
throw new HardwareControlException(ex.getMessage());
}

//keep track of last event to know what state the hardware was in without having to query it
lastEvent_ = event.getSequence() == null ? event : event.getSequence().get(event.getSequence().size() - 1);
}
}, "Moving Z device");
}

/**
Expand All @@ -988,11 +1035,13 @@ public void run() {
* @throws HardwareControlException
*/
private void loopHardwareCommandRetries(Runnable r, String commandName) throws HardwareControlException {
Exception ex = null;
for (int i = 0; i < HARDWARE_ERROR_RETRIES; i++) {
try {
r.run();
return;
} catch (Exception e) {
ex = e;
core_.logMessage(stackTraceToString(e));
System.err.println(getCurrentDateAndTime() + ": Problem "
+ commandName + "\n Retry #" + i + " in " + DELAY_BETWEEN_RETRIES_MS + " ms");
Expand All @@ -1004,7 +1053,7 @@ private void loopHardwareCommandRetries(Runnable r, String commandName) throws H
}
}
}
throw new HardwareControlException(commandName + " unsuccessful");
throw new HardwareControlException(commandName + " unsuccessful" + ": " + ex.getMessage());
}

private static String getCurrentDateAndTime() {
Expand Down Expand Up @@ -1069,7 +1118,10 @@ private static boolean isSequencable(List<AcquisitionEvent> previousEvents,
// arbitrary z stages
// TODO implement sequences along arbitrary other stage decives
for (String stageDevice : previousEvent.getStageDeviceNames() ) {
return false;
if (!nextEvent.getStageSingleAxisStagePosition(stageDevice)
.equals(previousEvent.getStageSingleAxisStagePosition(stageDevice))) {
return false;
}
}

//xy stage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class Acquisition {

public class Hardware {
public static final String PRE_HARDWARE = "pre_hardware";
public static final String PRE_Z_DRIVE = "pre_z_drive";
public static final String POST_HARDWARE = "post_hardware";

}
Expand Down
Loading