package org.signalml.plugin.newstager.logic.mgr; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.atomic.AtomicInteger; import org.signalml.method.ComputationException; import org.signalml.plugin.data.logic.PluginComputationMgrStepResult; import org.signalml.plugin.exception.PluginToolAbortException; import org.signalml.plugin.exception.PluginToolInterruptedException; import org.signalml.plugin.method.helper.AbstractPluginTrackerUpdaterWithTimer; import org.signalml.plugin.method.logic.AbstractPluginComputationMgrStep; import org.signalml.plugin.method.logic.IPluginComputationMgrStepTrackerProxy; import org.signalml.plugin.method.logic.PluginWorkerSet; import org.signalml.plugin.newstager.data.NewStagerBookInfo; import org.signalml.plugin.newstager.data.NewStagerBookReaderWorkerData; import org.signalml.plugin.newstager.data.NewStagerSignalStatsResult; import org.signalml.plugin.newstager.data.logic.INewStagerWorkerCompletion; import org.signalml.plugin.newstager.data.logic.NewStagerBookProcessorResult; import org.signalml.plugin.newstager.data.logic.NewStagerBookProcessorStepResult; import org.signalml.plugin.newstager.data.logic.NewStagerBookProcessorWorkerData; import org.signalml.plugin.newstager.data.logic.NewStagerComputationProgressPhase; import org.signalml.plugin.newstager.data.logic.NewStagerMgrStepData; import org.signalml.plugin.newstager.io.NewStagerBookReaderWorker; import org.signalml.plugin.newstager.logic.book.NewStagerBookDataProvider; import org.signalml.plugin.newstager.logic.book.NewStagerBookProcessorWorker; import org.signalml.plugin.signal.PluginSignalHelper; public class NewStagerBookProcessStep extends AbstractPluginComputationMgrStep<NewStagerMgrStepData> { private class TrackerUpdater extends AbstractPluginTrackerUpdaterWithTimer { private final NewStagerBookProcessStep step; private final IPluginComputationMgrStepTrackerProxy<NewStagerComputationProgressPhase> tracker; private int segmentCount; public TrackerUpdater( NewStagerBookProcessStep step, IPluginComputationMgrStepTrackerProxy<NewStagerComputationProgressPhase> tracker) { this.step = step; this.tracker = tracker; } @Override public void update(int progress, int prevProgress) { int tick = Math.max(0, progress - prevProgress); if (tick > 0) { int segmentCount; synchronized (this) { segmentCount = this.segmentCount; } this.tracker.advance(this.step, tick); this.tracker.setProgressPhase(NewStagerComputationProgressPhase.BOOK_PROCESSING_PHASE, progress, segmentCount); } } public void setSegmentCount(int segmentCount) { synchronized (this) { this.segmentCount = segmentCount; } } } private final PluginWorkerSet workers; private final NewStagerBookDataProvider bookDataProvider; private final TrackerUpdater trackerUpdater; private boolean hasExactSegmentCount; private AtomicInteger aliveWorkers; private Collection<NewStagerBookProcessorResult> partialResults; private NewStagerSignalStatsResult signalStatResult; public NewStagerBookProcessStep(NewStagerMgrStepData data) { super(data); this.hasExactSegmentCount = false; this.signalStatResult = null; this.workers = new PluginWorkerSet(this.data.threadFactory); this.bookDataProvider = new NewStagerBookDataProvider(); this.trackerUpdater = new TrackerUpdater(this, this.data.tracker); this.partialResults = null; this.aliveWorkers = null; } @Override public int getStepNumberEstimate() { return this.getAtomTickerEstimate(this.getSegmentCount()); } @Override protected void doInitialize() { NewStagerBookReaderWorker bookReaderWorker = new NewStagerBookReaderWorker( new NewStagerBookReaderWorkerData( this.data.stagerData.getParameters().bookFilePath, this.bookDataProvider)); this.workers.submit(bookReaderWorker); } @Override protected NewStagerBookProcessorStepResult prepareStepResult() { if (this.partialResults == null || this.signalStatResult == null) { return null; } NewStagerBookInfo bookInfo; try { bookInfo = this.bookDataProvider.getBookInfo(); } catch (InterruptedException e) { // TODO log exception return null; } if (bookInfo == null) { return null; } return new NewStagerBookProcessorStepResult(bookInfo, this.partialResults, this.signalStatResult.montage); } @Override protected PluginComputationMgrStepResult doRun( PluginComputationMgrStepResult prevStepResult) throws PluginToolAbortException, PluginToolInterruptedException, ComputationException { try { this.signalStatResult = (NewStagerSignalStatsResult) prevStepResult; } catch (ClassCastException e) { throw new ComputationException(e); } this.data.tracker.setProgressPhase(NewStagerComputationProgressPhase.BOOK_FILE_INITIAL_READ_PHASE); this.checkAbortState(); this.prepareWorkers(); this.trackerUpdater.setSegmentCount(this.getSegmentCount()); this.trackerUpdater.start(100); this.checkAbortState(); this.workers.startAll(); this.checkAbortState(); this.waitForWorkerCompletion(); return this.prepareStepResult(); } @Override protected void cleanup() { super.cleanup(); this.trackerUpdater.stop(); this.stopWorkers(); } private void prepareWorkers() { if (this.signalStatResult == null) { return; } int numberOfThreads = Math.max(1, Runtime.getRuntime() .availableProcessors()); final AtomicInteger counter = this.aliveWorkers = new AtomicInteger( numberOfThreads); final AtomicInteger processedAtomCount = new AtomicInteger(); final Collection<NewStagerBookProcessorResult> partialResults = this.partialResults = new ArrayList<NewStagerBookProcessorResult>( numberOfThreads); INewStagerWorkerCompletion<NewStagerBookProcessorResult> completion = new INewStagerWorkerCompletion<NewStagerBookProcessorResult>() { @Override public void signalProgress(int i) { trackerUpdater.setProgress(processedAtomCount.addAndGet(i)); } @Override public void completeWork(NewStagerBookProcessorResult result) { try { synchronized (partialResults) { partialResults.add(result); } } finally { counter.decrementAndGet(); } } }; for (int i = 0; i < numberOfThreads; ++i) { this.workers.add(new NewStagerBookProcessorWorker( new NewStagerBookProcessorWorkerData(this.bookDataProvider, completion, this.data.constants, this.data.stagerData.getChannelMap(), this.signalStatResult.newParameters, this.data.stagerData.getFixedParameters(), this.signalStatResult.muscle, this.signalStatResult.signalStatCoeffs))); } } private void stopWorkers() { this.bookDataProvider.shutdown(); this.workers.terminateAll(); } private void waitForWorkerCompletion() throws PluginToolInterruptedException, PluginToolAbortException { while (this.aliveWorkers.get() != 0) { this.updateSegmentCountIfNeeded(); try { Thread.sleep(500); } catch (InterruptedException e) { throw new PluginToolInterruptedException(e); } this.checkAbortState(); } } private void updateSegmentCountIfNeeded() { if (this.hasExactSegmentCount) { return; } NewStagerBookInfo bookInfo = this.bookDataProvider.tryGetBookInfo(); if (bookInfo == null) { return; } this.trackerUpdater.setSegmentCount(bookInfo.segmentCount); this.data.tracker.setTickerLimit(this, this.getAtomTickerEstimate(bookInfo.segmentCount)); this.hasExactSegmentCount = true; } private int getSegmentCount() { int segmentCount; NewStagerBookInfo bookInfo = this.bookDataProvider.tryGetBookInfo(); if (bookInfo != null) { segmentCount = bookInfo.segmentCount; this.hasExactSegmentCount = true; } else { //very rough estimate of number of atom segments segmentCount = PluginSignalHelper.GetBlockCount( this.data.stagerData.getSampleSource(), this.data.constants.getBlockLengthInSamples()); } return segmentCount; } private int getAtomTickerEstimate(int segmentCount) { int ratio = this.data.constants.getBlockLengthInSamples() / 20; return (int) Math.ceil(((double) segmentCount) / ratio); } }