package plugins.adufour.hcs.blocks; import java.awt.Point; import java.io.File; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import danyfel80.wells.data.IField; import danyfel80.wells.data.IPlate; import danyfel80.wells.data.IWell; import icy.gui.frame.progress.CancelableProgressFrame; import icy.sequence.Sequence; import icy.system.IcyHandledException; import icy.system.SystemUtil; import icy.util.StringUtil; import plugins.adufour.blocks.lang.Batch; import plugins.adufour.blocks.lang.FileBatch; import plugins.adufour.blocks.util.VarList; import plugins.adufour.hcs.WellPlateImporter; import plugins.adufour.hcs.io.AbstractWellPlateReader; import plugins.adufour.vars.gui.FileMode; import plugins.adufour.vars.gui.model.FileTypeModel; import plugins.adufour.vars.lang.Var; import plugins.adufour.vars.lang.VarFile; import plugins.adufour.vars.lang.VarInteger; import plugins.adufour.vars.lang.VarSequence; import plugins.adufour.vars.lang.VarString; import plugins.adufour.vars.util.VarListener; /** * A well plate batch is a work-flow that will process every image of every well * in a given well plate (or a selection thereof). Supported plate formats are * the same as those supported by the OldWellPlateImporter plug-in. * * @author Daniel Felipe Gonzalez Obando */ public class WellPlateBatch extends Batch { private VarString wellFilter; private VarString plateID; private VarString wellID; private VarInteger fieldID; @Override public void declareInput(VarList inputMap) { super.declareInput(inputMap); wellFilter = new VarString("Well filter", ""); inputMap.add(wellFilter.getName(), wellFilter); } @Override public void declareOutput(VarList outputMap) { super.declareOutput(outputMap); plateID = new VarString("Plate", "--"); plateID.setEnabled(false); wellID = new VarString("Well", "--"); wellID.setEnabled(false); fieldID = new VarInteger("Field", 0); fieldID.setEnabled(false); outputMap.add(plateID.getName(), plateID); outputMap.add(wellID.getName(), wellID); outputMap.add(fieldID.getName(), fieldID); } @Override public void declareLoopVariables(List<Var<?>> loopVariables) { super.declareLoopVariables(loopVariables); loopVariables.add(wellFilter); loopVariables.add(plateID); loopVariables.add(wellID); loopVariables.add(fieldID); } /** * The folder containing the well plate. Although we could have extended * {@link FileBatch}, we choose to extend just {@link Batch} since we don't * need extension filters or recursive sub-folder analysis */ private VarFile plateFolder; /** * Indicates whether the current well plate is loaded (avoids reloading it * every time the protocol is run) */ private boolean wellPlateLoaded = false; @Override public Var<?> getBatchSource() { if (plateFolder == null) { // Make sure the well plate is reloaded only when this parameter changes plateFolder = new VarFile("Plate folder", null, new VarListener<File>() { @Override public void valueChanged(Var<File> source, File oldValue, File newValue) { wellPlateLoaded = false; } @Override public void referenceChanged(Var<File> source, Var<? extends File> oldReference, Var<? extends File> newReference) { wellPlateLoaded = false; } }); plateFolder.setDefaultEditorModel(new FileTypeModel(null, FileMode.FOLDERS, null, false)); } return plateFolder; } private VarSequence element; @Override public VarSequence getBatchElement() { if (element == null) { // WARNING: do *not* change the name of this variable // why? see declareInput() and VarList.add() element = new VarSequence("Sequence", null); } return element; } String[] wellFilterValues; AbstractWellPlateReader reader; IPlate currentWellPlate; Iterator<? extends IWell> wellIterator; Iterator<? extends IField> fieldIterator; @Override public void initializeLoop() { wellFilterValues = wellFilter.getValue(true).split("\\s+"); if (!wellPlateLoaded) // Fetch a valid reader and load the well plate { File folder = plateFolder.getValue(true); reader = WellPlateImporter.getReaderFor(folder); if (reader == null) throw new IcyHandledException("No plate reader found for folder " + folder.getPath()); @SuppressWarnings("unchecked") Optional<CancelableProgressFrame> progress = (Optional<CancelableProgressFrame>) (SystemUtil.isHeadLess() ? Optional.empty() : Optional.of(new CancelableProgressFrame("Loading well plate..."))); try { Future<? extends IPlate> plateFuture = reader.loadPlateFromFolder(folder, (p, m) -> progress.ifPresent(pr -> { pr.setPosition(p); pr.setMessage(m); })); currentWellPlate = plateFuture.get(); plateID.setValue(currentWellPlate.getId()); wellPlateLoaded = true; } catch (ExecutionException | InterruptedException e) { throw new RuntimeException(e); } finally { progress.ifPresent(pr -> pr.close()); } } // Prepare the iterator' wellIterator = currentWellPlate.getWells().values().stream().filter(this::shouldLoadWell).iterator(); fieldIterator = Collections.<IField> emptyList().iterator(); } private boolean shouldLoadWell(IWell well) { if (wellFilterValues.length != 0) { Point wellPos = well.getPositionInPlate(); char col = ((char) ('A' + wellPos.x - 1)); String row = StringUtil.toString(wellPos.y, 2); String wellAlphanumeric = "" + col + row; for (String filter : wellFilterValues) { if (wellAlphanumeric.contains(filter)) return true; } return false; } return true; } private IWell currentWell; @Override public void beforeIteration() { try { Sequence sequence = new Sequence(); if (!fieldIterator.hasNext()) { if (wellIterator.hasNext()) { currentWell = wellIterator.next(); fieldIterator = currentWell.getFields().values().iterator(); } } if (fieldIterator.hasNext()) { IField currentField = fieldIterator.next(); Point pos = currentWell.getPositionInPlate(); plateID.setValue(currentWellPlate.getId()); wellID.setValue("" + ((char) ('A' + pos.x - 1)) + StringUtil.toString(pos.y, 2)); fieldID.setValue((int) currentField.getId()); reader.loadField(currentWellPlate, currentWell, currentField, sequence, null); sequence.setName(plateID.getValueAsString() + "_" + wellID.getValueAsString() + "_" + fieldID.getValueAsString()); element.setValue(sequence); } Thread.yield(); } catch (Exception e) { e.printStackTrace(); Thread.currentThread().interrupt(); } } @Override public boolean isStopConditionReached() { return !fieldIterator.hasNext() && !wellIterator.hasNext(); } }