/*
* Licensed under the Apache License, Version 2.0 (the "License");
*
* You may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributions from 2013-2017 where performed either by US government
* employees, or under US Veterans Health Administration contracts.
*
* US Veterans Health Administration contributions by government employees
* are work of the U.S. Government and are not subject to copyright
* protection in the United States. Portions contributed by government
* employees are USGovWork (17USC §105). Not subject to copyright.
*
* Contribution by contractors to the US Veterans Health Administration
* during this period are contractually contributed under the
* Apache License, Version 2.0.
*
* See: https://www.usa.gov/government-works
*
* Contributions prior to 2013:
*
* Copyright (C) International Health Terminology Standards Development Organisation.
* Licensed under the Apache License, Version 2.0.
*
*/
package sh.isaac.api.task;
//~--- JDK imports ------------------------------------------------------------
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
//~--- non-JDK imports --------------------------------------------------------
import javafx.application.Platform;
import javafx.concurrent.Task;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import sh.isaac.api.ticker.Ticker;
//~--- classes ----------------------------------------------------------------
/**
* The Class TimedTask.
*
* @author kec
* @param <T> the generic type
*/
public abstract class TimedTask<T>
extends Task<T> {
/** The Constant log. */
protected static final Logger log = LogManager.getLogger();
/** The progress update interval in secs. */
public static int progressUpdateIntervalInSecs = 2;
/**
* Seconds per minute.
*/
static final int SECONDS_PER_MINUTE = 60;
/**
* Minutes per hour.
*/
static final int MINUTES_PER_HOUR = 60;
/**
* Seconds per hour.
*/
static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
//~--- fields --------------------------------------------------------------
/** The update ticker. */
private final Ticker updateTicker = new Ticker();
/** The start time. */
private Instant startTime;
/** The end time. */
private Instant endTime;
/** The complete message generator. */
Consumer<TimedTask<T>> completeMessageGenerator;
/** The progress message generator. */
Consumer<TimedTask<T>> progressMessageGenerator;
//~--- methods -------------------------------------------------------------
/**
* Done.
*/
@Override
protected void done() {
super.done();
this.endTime = Instant.now();
this.updateTicker.stop();
if (this.completeMessageGenerator == null) {
setCompleteMessageGenerator((task) -> {
updateMessage(getState() + " in " + formatDuration(getDuration()));
});
}
Platform.runLater(() -> {
this.completeMessageGenerator.accept(this);
log.info(getTitle() + " " + getMessage());
});
}
/**
* Failed.
*/
@Override
protected void failed() {
log.warn("Timed task failed!", this.getException());
}
/**
* Running.
*/
@Override
protected void running() {
super.running();
if (this.startTime == null) {
this.startTime = Instant.now();
}
this.updateTicker.start(progressUpdateIntervalInSecs,
(value) -> {
if (this.progressMessageGenerator != null) {
this.progressMessageGenerator.accept(this);
}
});
}
/**
* Format duration.
*
* @param d the d
* @return the string
*/
private String formatDuration(Duration d) {
final StringBuilder builder = new StringBuilder();
final long seconds = d.getSeconds();
if (seconds > 0) {
final long hours = seconds / SECONDS_PER_HOUR;
final int minutes = (int) ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
final int secs = (int) (seconds % SECONDS_PER_MINUTE);
if (hours != 0) {
builder.append(hours)
.append("h ");
}
if (minutes != 0) {
builder.append(minutes)
.append("m ");
}
builder.append(secs)
.append("s");
return builder.toString();
}
final int nanos = d.getNano();
final long milis = TimeUnit.MILLISECONDS.convert(nanos, TimeUnit.NANOSECONDS);
if (milis > 0) {
return builder.append(milis)
.append(" ms")
.toString();
}
final long micro = TimeUnit.MICROSECONDS.convert(nanos, TimeUnit.NANOSECONDS);
if (micro > 0) {
return builder.append(micro)
.append(" μs")
.toString();
}
return builder.append(nanos)
.append(" ns")
.toString();
}
//~--- set methods ---------------------------------------------------------
/**
* Sets the complete message generator.
*
* @param consumer the new complete message generator
*/
public void setCompleteMessageGenerator(Consumer<TimedTask<T>> consumer) {
this.completeMessageGenerator = consumer;
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the duration.
*
* @return the duration
*/
public Duration getDuration() {
if (this.startTime == null) {
return Duration.ZERO;
}
if (this.endTime == null) {
return Duration.between(this.startTime, Instant.now());
}
return Duration.between(this.startTime, this.endTime);
}
/**
* Gets the formatted duration.
*
* @return the formatted duration
*/
public String getFormattedDuration() {
return formatDuration(getDuration());
}
//~--- set methods ---------------------------------------------------------
/**
* Sets the progress message generator.
*
* @param consumer the new progress message generator
*/
public void setProgressMessageGenerator(Consumer<TimedTask<T>> consumer) {
this.progressMessageGenerator = consumer;
}
/**
* Set start time.
*/
protected void setStartTime() {
this.startTime = Instant.now();
}
}