001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 * image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause).
006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved.
007 * Visit https://imagingbook.com for additional details.
008 ******************************************************************************/
009package imagingbook.common.util.progress;
010
011import imagingbook.common.filter.generic.GenericFilter;
012import imagingbook.common.ij.IjProgressBarMonitor;
013
014/**
015 * <p>
016 * This class represents a "progress monitor". This is a simple {@link Thread} that queries the attached target task
017 * (implementing {@link ProgressReporter}) and calls its {@link #handleProgress(double, long)} method at regular
018 * intervals. {@link ProgressMonitor} implements the {@link AutoCloseable} and thus can (and should) be used in a
019 * try-with-resources context, e.g.,
020 * </p>
021 * <pre>
022 * ProgressReporter task = ....; // the activity to be monitored
023 * try (ProgressMonitor m = new ConsoleProgressMonitor(task)) {
024 *     // run task ...
025 * }
026 * </pre>
027 * <p>
028 * Otherwise, if not used in a auto-start/close mode, the methods {@link #start()} and {@link #close()} should be used
029 * to "manually" start and terminate monitoring. See {@link ProgressMonitorExample}, {@link GenericFilter},
030 * {@link ConsoleProgressMonitor} and {@link IjProgressBarMonitor} for examples.
031 * </p>
032 * <p>
033 * Note that this mechanism cannot be used to monitor activities inside a constructor, since no reference to the
034 * monitored instance is available before the constructor is finished.
035 * </p>
036 *
037 * @see ProgressReporter
038 * @see ProgressMonitorExample
039 * @see ConsoleProgressMonitor
040 */
041public abstract class ProgressMonitor extends Thread implements AutoCloseable {
042        
043        /** The default wait time between successive progress queries. */
044        public static final int DefaultWaitTime = 250; // ms
045        
046        private int waitTime = DefaultWaitTime;
047        private final ProgressReporter target;
048
049        private boolean running;
050        private long startTime, endTime;
051
052        /**
053         * Constructor, starts monitoring immediately. The target object's {@link ProgressReporter#getProgress()} method is
054         * called it regular intervals.
055         *
056         * @param target the object (task) to be monitored
057         */
058        public ProgressMonitor(ProgressReporter target) {
059                this(target, true);
060        }
061
062        /**
063         * Constructor, optionally starts monitoring immediately or waits for its {@link #start()} method to be called
064         * (which starts the associated thread). The target object's {@link ProgressReporter#getProgress()} method is called
065         * it regular intervals.
066         *
067         * @param target the object (task) to be monitored
068         * @param autoStart starts immediately if true
069         */
070        public ProgressMonitor(ProgressReporter target, boolean autoStart) {
071                if (target == null)
072                        throw new IllegalArgumentException("null target instance!");
073                this.target = target;
074                if (autoStart) {
075                        start();
076                }
077        }
078
079        /**
080         * The time interval between progress queries can be set with this method. See also {@link #DefaultWaitTime}.
081         *
082         * @param waitTime the time interval (in milliseconds)
083         */
084        public void setWaitTime(int waitTime) {
085                this.waitTime = waitTime;
086        }
087
088        /**
089         * Called periodically by the {@link ProgressMonitor} thread supplying the current progress value (degree of
090         * completion). It is up to the implementation what action should be performed, e.g., display the current progress
091         * graphically (see e.g. {@link IjProgressBarMonitor}).
092         *
093         * @param progress the current progress value (degree of completion) in [0,1)
094         * @param elapsedNanoTime the time elapsed since this progress monitor was started (in nanoseconds)
095         */
096        public abstract void handleProgress(double progress, long elapsedNanoTime);
097        
098        // --------------------------------------------------------
099
100        @Override
101        public void run() {
102                handleProgress(0, 0);
103                this.startTime = System.nanoTime();
104                running = true;
105                do {
106                        try {
107                                Thread.sleep(waitTime);
108                        } catch (InterruptedException e) {break;}
109                        handleProgress(target.getProgress(), System.nanoTime() - startTime);
110                } while(running);
111                this.endTime = System.nanoTime();
112        }
113        
114        @Override
115        public void close() {
116                running = false;
117        this.interrupt();
118        }
119        
120        // --------------------------------------------------------
121        
122        /**
123         * Returns the time elapsed since monitoring started (in seconds).
124         * @return the elapsed time in seconds
125         */
126        public double getElapsedTime() { // result is in seconds
127                if (running) {
128                        return  (System.nanoTime() - startTime) / 1.0e9d;
129                }
130                else {
131                        return (endTime - startTime) / 1.0e9d;
132                }
133        }
134
135}