diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/DataReductionWriter.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/DataReductionWriter.java index 294612f..ec0fefa 100644 --- a/view-simulation-results/src/main/java/org/vcell/N5/reduction/DataReductionWriter.java +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/DataReductionWriter.java @@ -1,11 +1,14 @@ package org.vcell.N5.reduction; +import com.amazonaws.AbortedException; import com.google.gson.internal.LinkedTreeMap; import com.opencsv.CSVWriter; import ij.ImagePlus; +import ij.WindowManager; import ij.gui.Roi; import org.vcell.N5.ExportDataRepresentation; import org.vcell.N5.N5ImageHandler; +import org.vcell.N5.UI.ControlButtonsPanel; import org.vcell.N5.UI.MainPanel; import org.vcell.N5.UI.N5ExportTable; import org.vcell.N5.reduction.DTO.RangeOfImage; @@ -17,6 +20,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; public class DataReductionWriter implements SimLoadingListener { private final ArrayList arrayOfSimRois; @@ -34,6 +39,9 @@ public class DataReductionWriter implements SimLoadingListener { private final ArrayList> standardDivMatrix = new ArrayList<>(); private final ArrayList> metaDataSheet = new ArrayList<>(); + private final ConcurrentHashMap threadPool = new ConcurrentHashMap<>(); + private final Object threadPoolLock = new Object(); + private final HashMap>> sheetsAvailable = new HashMap>>(){{ put(SelectMeasurements.AvailableMeasurements.AVERAGE, averageMatrix); put(SelectMeasurements.AvailableMeasurements.STD_DEV, standardDivMatrix); @@ -61,10 +69,6 @@ public ReducedData(int rowLen, int colLen, SelectMeasurements.AvailableMeasureme // Initialize Sheet and Lab results // ///////////////////////////////////// - public static void createDataReductionProcess(DataReductionGUI.DataReductionSubmission submission){ - new DataReductionWriter(submission); - } - public DataReductionWriter(DataReductionGUI.DataReductionSubmission submission){ N5ImageHandler.loadingManager.addSimLoadingListener(this); this.submission = submission; @@ -83,8 +87,15 @@ public DataReductionWriter(DataReductionGUI.DataReductionSubmission submission){ Thread processLabResults = new Thread(() -> { calculateAndAddResults(submission.labResults, submission.experiementNormRange, submission.experimentImageRange, - submission.arrayOfLabRois, null); + submission.arrayOfLabRois, null, "Lab"); + synchronized (threadPoolLock){ + threadPool.remove("Lab"); + } }, "Processing Lab Image"); + ThreadStruct threadStruct = new ThreadStruct(null, new AtomicBoolean(true), processLabResults); + synchronized (threadPoolLock){ + threadPool.put("Lab", threadStruct); + } processLabResults.start(); } @@ -137,7 +148,8 @@ private void initializeDataSheets(){ private void calculateAndAddResults(ImagePlus imagePlus, RangeOfImage normRange, RangeOfImage imageRange, ArrayList rois, - LinkedTreeMap> channelInfo){ + LinkedTreeMap> channelInfo, + String threadName){ HashMap normValue = null; if (submission.normalizeMeasurementsBool){ normValue = calculations.calculateNormalValue(imagePlus, normRange, rois, imageRange); @@ -153,9 +165,12 @@ private void calculateAndAddResults(ImagePlus imagePlus, RangeOfImage normRange, reducedDataArrayList.add(reducedData); calculations.addAppropriateHeaders(imagePlus, rois, imageRange, reducedData, channelInfo); } - calculations.calculateStatistics(imagePlus, rois, normValue, reducedDataArrayList, imageRange); + AtomicBoolean continueOperation = threadPool.get(threadName).continueOperation; + calculations.calculateStatistics(imagePlus, rois, normValue, reducedDataArrayList, imageRange, continueOperation); for (ReducedData reducedData: reducedDataArrayList){ - addValuesToCSVMatrix(reducedData); + if (continueOperation.get()){ + addValuesToCSVMatrix(reducedData); + } } } @@ -248,8 +263,26 @@ private void writeCSVMatrix(){ throw new RuntimeException(e); } finally { N5ImageHandler.loadingManager.removeFromSimLoadingListener(this); - MainPanel.controlButtonsPanel.enableCriticalButtons(true); + MainPanel.controlButtonsPanel.updateButtonsToMatchState(false, ControlButtonsPanel.PanelState.NOTHING_OR_LOADING_IMAGE); + } + } + + public void stopAllThreads(){ + synchronized (threadPoolLock){ + for (String threadName: threadPool.keySet()){ + ThreadStruct threadStruct = threadPool.get(threadName); + threadStruct.continueOperation.set(false); + // Experiment image is in thread pool, so trying to retrieve a results loader for it would not work + if (threadStruct.simResultsLoader != null){ + SimResultsLoader loadedResults = threadStruct.simResultsLoader; + MainPanel.n5ExportTable.removeSpecificRowFromLoadingRows(loadedResults.rowNumber); + WindowManager.getImage(loadedResults.getImagePlus().getID()).close(); + } + threadPool.remove(threadName); + } } + N5ImageHandler.loadingManager.removeFromSimLoadingListener(this); + MainPanel.controlButtonsPanel.updateButtonsToMatchState(false, ControlButtonsPanel.PanelState.NOTHING_OR_LOADING_IMAGE); } @@ -266,14 +299,32 @@ public void simFinishedLoading(SimResultsLoader loadedResults) { imagePlus.show(); addMetaData(loadedResults); calculateAndAddResults(imagePlus, submission.simNormRange, submission.simImageRange, - submission.arrayOfSimRois, loadedResults.getChannelInfo()); + submission.arrayOfSimRois, loadedResults.getChannelInfo(), loadedResults.exportID); MainPanel.n5ExportTable.removeSpecificRowFromLoadingRows(loadedResults.rowNumber); imagePlus.close(); + synchronized (threadPoolLock){ + threadPool.remove(loadedResults.exportID); + } }, "Processing Image: " + loadedResults.userSetFileName); + ThreadStruct threadStruct = new ThreadStruct(loadedResults, new AtomicBoolean(true), imageProcessingThread); + synchronized (threadPoolLock){ + threadPool.put(loadedResults.exportID, threadStruct); + } imageProcessingThread.start(); } } + static class ThreadStruct { + public final SimResultsLoader simResultsLoader; + public final AtomicBoolean continueOperation; + public final Thread thread; + public ThreadStruct(SimResultsLoader simResultsLoader, AtomicBoolean continueOperation, Thread thread){ + this.simResultsLoader = simResultsLoader; + this.continueOperation = continueOperation; + this.thread = thread; + } + } + } diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/ReductionCalculations.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/ReductionCalculations.java index b553351..7924ad1 100644 --- a/view-simulation-results/src/main/java/org/vcell/N5/reduction/ReductionCalculations.java +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/ReductionCalculations.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.concurrent.atomic.AtomicBoolean; class ReductionCalculations { private final boolean normalize; @@ -37,9 +38,9 @@ public void addAppropriateHeaders(ImagePlus imagePlus, ArrayList roiList, R * @param rangeOfImage */ void calculateStatistics(ImagePlus imagePlus, ArrayList roiList, - HashMap normalizationValue, - ArrayList reducedDataArrayList, - RangeOfImage rangeOfImage){ + HashMap normalizationValue, + ArrayList reducedDataArrayList, + RangeOfImage rangeOfImage, AtomicBoolean continueOperation){ int roiCounter = 0; for (Roi roi: roiList) { imagePlus.setRoi(roi); @@ -48,6 +49,9 @@ void calculateStatistics(ImagePlus imagePlus, ArrayList roiList, for (int z = rangeOfImage.zStart; z <= rangeOfImage.zEnd; z++){ for (int c = rangeOfImage.channelStart; c <= rangeOfImage.channelEnd; c++){ int channelSize = rangeOfImage.channelEnd - rangeOfImage.channelStart + 1; + if (!continueOperation.get()){ + return; + } imagePlus.setPosition(c, z, t); double calculatedValue; for (DataReductionWriter.ReducedData reducedData : reducedDataArrayList){ @@ -61,7 +65,7 @@ void calculateStatistics(ImagePlus imagePlus, ArrayList roiList, default: throw new RuntimeException("Unknown measurement type selected."); } - if (normalize && reducedData.measurementType == SelectMeasurements.AvailableMeasurements.AVERAGE){ + if (normalize){ calculatedValue = calculatedValue / normalizationValue.get(roi.getName() + c); } reducedData.data[tzCounter][c - 1 + (roiCounter * channelSize)] = calculatedValue; diff --git a/view-simulation-results/src/main/java/org/vcell/N5/retrieving/LoadingManager.java b/view-simulation-results/src/main/java/org/vcell/N5/retrieving/LoadingManager.java index 838d7c7..52ec335 100644 --- a/view-simulation-results/src/main/java/org/vcell/N5/retrieving/LoadingManager.java +++ b/view-simulation-results/src/main/java/org/vcell/N5/retrieving/LoadingManager.java @@ -1,7 +1,6 @@ package org.vcell.N5.retrieving; import com.amazonaws.AbortedException; -import com.amazonaws.http.timers.client.SdkInterruptedException; import ij.ImagePlus; import org.scijava.log.Logger; import org.vcell.N5.N5ImageHandler; @@ -26,6 +25,7 @@ public class LoadingManager implements SimLoadingEventCreator { private final HashMap openingSimulations = new HashMap<>(); private final Object openSimulationsLock = new Object(); + private DataReductionWriter dataReductionWriter = null; private static final Logger logger = N5ImageHandler.getLogger(RangeSelector.class); @@ -38,13 +38,13 @@ public void openN5FileDataset(ArrayList filesToOpen, boolean o ArrayList dimensions = firstSim.getN5Dimensions(); if (dataReduction){ dataReductionGUI = new DataReductionGUI(filesToOpen, dimensions.get(2), dimensions.get(3), dimensions.get(4)); + dataReductionWriter = dataReductionGUI.shouldContinueWithProcess() ? new DataReductionWriter(dataReductionGUI.createSubmission()) : null; } else { rangeSelector.displayRangeMenu(dimensions.get(2), dimensions.get(3), dimensions.get(4)); } } boolean dataReductionOkay = dataReduction && dataReductionGUI.shouldContinueWithProcess(); if (dataReductionOkay || !dataReduction){ - controlButtonsPanel.allowCancel(true); MainPanel.changeCursor(new Cursor(Cursor.WAIT_CURSOR)); for (int i = 0; i < filesToOpen.size(); i++){ SimResultsLoader simResultsLoader = filesToOpen.get(i); @@ -64,6 +64,7 @@ public void openN5FileDataset(ArrayList filesToOpen, boolean o } catch (RuntimeException e) { simResultsLoader.setTagToCanceled(); + MainPanel.n5ExportTable.removeSpecificRowFromLoadingRows(simResultsLoader.rowNumber); if (e instanceof AbortedException){ logger.debug("Simulation stopped loading"); } else { @@ -75,8 +76,6 @@ public void openN5FileDataset(ArrayList filesToOpen, boolean o synchronized (openSimulationsLock){ openingSimulations.remove(simResultsLoader.exportID); } - controlButtonsPanel.enableRowContextDependentButtons(true); - MainPanel.controlButtonsPanel.allowCancel(false); } }); openThread.setName("Opening sim number: " + i + ". With id: " + simResultsLoader.exportID); @@ -88,18 +87,36 @@ public void openN5FileDataset(ArrayList filesToOpen, boolean o } } - public void stopOpeningSimulation(String exportID){ + public void stopAllImagesAndAnalysis(){ + Thread stopEverything = new Thread(() -> { + synchronized (openSimulationsLock){ + for (String threadName : openingSimulations.keySet()){ + openingSimulations.get(threadName).interrupt(); + openingSimulations.remove(threadName); + } + } + if (dataReductionWriter != null){ + dataReductionWriter.stopAllThreads(); + } + }); + stopEverything.start(); + } + + + public void stopLoadingImage(String exportID){ Thread stopOtherThread = new Thread(() -> { synchronized (openSimulationsLock){ - openingSimulations.get(exportID).interrupt(); - openingSimulations.remove(exportID); + if (openingSimulations.containsKey(exportID)){ + openingSimulations.get(exportID).interrupt(); + openingSimulations.remove(exportID); + } } }); stopOtherThread.start(); } public void openLocalN5FS(ArrayList filesToOpen){ - controlButtonsPanel.enableCriticalButtons(true); + controlButtonsPanel.enableAllButtons(true); JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); fileChooser.setAcceptAllFileFilterUsed(false); @@ -121,7 +138,7 @@ public void openLocalN5FS(ArrayList filesToOpen){ @Override public void run() { MainPanel.changeCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - controlButtonsPanel.enableCriticalButtons(true); + controlButtonsPanel.enableAllButtons(true); } }); } diff --git a/view-simulation-results/src/test/java/org/vcell/N5/reduction/ReductionCalculationsTest.java b/view-simulation-results/src/test/java/org/vcell/N5/reduction/ReductionCalculationsTest.java index 9c183b1..556b72f 100644 --- a/view-simulation-results/src/test/java/org/vcell/N5/reduction/ReductionCalculationsTest.java +++ b/view-simulation-results/src/test/java/org/vcell/N5/reduction/ReductionCalculationsTest.java @@ -17,6 +17,7 @@ import java.net.URL; import java.util.ArrayList; import java.util.HashMap; +import java.util.concurrent.atomic.AtomicBoolean; public class ReductionCalculationsTest { // First two are SimROI, last two are LabROI @@ -55,7 +56,7 @@ private void compareExpectedCalculations(ImagePlus imagePlus, ArrayList roi reducedDataArrayList.add(reducedData); HashMap norms = reductionCalculations.calculateNormalValue(imagePlus, normRange, roiList, entireRange); - reductionCalculations.calculateStatistics(imagePlus, roiList, norms, reducedDataArrayList, entireRange); + reductionCalculations.calculateStatistics(imagePlus, roiList, norms, reducedDataArrayList, entireRange, new AtomicBoolean(true)); for (int r = 0; r < expectedResults.length; r++){ for (int c = 0; c < expectedResults[r].length; c++){ Assert.assertEquals(expectedResults[r][c], reducedData.data[r][c], 0.0009);