Feat: send state of all ongoing jobs

This commit is contained in:
Laurent Clouet
2025-07-28 21:31:29 +02:00
parent 1dfacaee02
commit ccdfc5d3f4
5 changed files with 56 additions and 27 deletions

View File

@@ -45,6 +45,7 @@ dependencies {
implementation 'com.formdev:flatlaf:2.2' // 2.3+ causes illegal reflective access warning on win + adoptium java 11.0.16 implementation 'com.formdev:flatlaf:2.2' // 2.3+ causes illegal reflective access warning on win + adoptium java 11.0.16
implementation 'com.squareup.okhttp3:okhttp:4.12.+' implementation 'com.squareup.okhttp3:okhttp:4.12.+'
implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.12.+' implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.12.+'
implementation 'com.squareup.moshi:moshi:1.15.0'
implementation 'org.slf4j:slf4j-simple:2.0.12' implementation 'org.slf4j:slf4j-simple:2.0.12'
implementation 'commons-io:commons-io:2.11.0' implementation 'commons-io:commons-io:2.11.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.+' testImplementation 'org.junit.jupiter:junit-jupiter:5.7.+'

View File

@@ -74,22 +74,16 @@ import com.sheepit.client.os.OS;
import com.sheepit.client.rendering.IncompatibleProcessChecker; import com.sheepit.client.rendering.IncompatibleProcessChecker;
import com.sheepit.client.rendering.Job; import com.sheepit.client.rendering.Job;
import com.sheepit.client.rendering.State;
import com.sheepit.client.ui.Gui; import com.sheepit.client.ui.Gui;
import com.sheepit.client.utils.Pair; import com.sheepit.client.utils.Pair;
import com.sheepit.client.utils.Utils; import com.sheepit.client.utils.Utils;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.Getter;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
@Data public class Client { @Data public class Client {
public enum State {
DOWNLOADING,
PREPARING,
RENDERING,
UPLOADING,
UNKNOWN
}
public static final int MIN_JOB_ID = 20; //to distinguish between actual jobs and test frames public static final int MIN_JOB_ID = 20; //to distinguish between actual jobs and test frames
private static final Locale LOCALE = Locale.ENGLISH; private static final Locale LOCALE = Locale.ENGLISH;
private DirectoryManager directoryManager; private DirectoryManager directoryManager;
@@ -97,7 +91,6 @@ import okhttp3.HttpUrl;
private ServerRequest serverRequest; private ServerRequest serverRequest;
private Configuration configuration; private Configuration configuration;
private Log log; private Log log;
private State state;
private Job renderingJob; private Job renderingJob;
private Job previousJob; private Job previousJob;
private BlockingQueue<QueuedJob> jobsToValidate; private BlockingQueue<QueuedJob> jobsToValidate;
@@ -121,7 +114,6 @@ import okhttp3.HttpUrl;
this.log = Log.getInstance(); this.log = Log.getInstance();
this.gui = gui; this.gui = gui;
this.directoryManager = new DirectoryManager(this.configuration); this.directoryManager = new DirectoryManager(this.configuration);
this.state = State.UNKNOWN;
this.renderingJob = null; this.renderingJob = null;
this.previousJob = null; this.previousJob = null;
this.jobsToValidate = new ArrayBlockingQueue<>(5); this.jobsToValidate = new ArrayBlockingQueue<>(5);
@@ -722,10 +714,11 @@ import okhttp3.HttpUrl;
public Error.Type work(final Job ajob) { public Error.Type work(final Job ajob) {
Error.Type downloadRet; Error.Type downloadRet;
ajob.setState(State.PREPARING);
gui.setRenderingProjectName(ajob.getName()); gui.setRenderingProjectName(ajob.getName());
try { try {
this.state = State.DOWNLOADING; ajob.setState(State.DOWNLOADING);
downloadRet = this.downloadExecutable(ajob); downloadRet = this.downloadExecutable(ajob);
if (downloadRet != Error.Type.OK) { if (downloadRet != Error.Type.OK) {
gui.setRenderingProjectName(""); gui.setRenderingProjectName("");
@@ -746,7 +739,7 @@ import okhttp3.HttpUrl;
return downloadRet; return downloadRet;
} }
this.state = State.PREPARING; ajob.setState(State.PREPARING);
int ret = this.prepareWorkingDirectory(ajob); // decompress renderer and scene archives int ret = this.prepareWorkingDirectory(ajob); // decompress renderer and scene archives
if (ret != 0) { if (ret != 0) {
@@ -759,7 +752,7 @@ import okhttp3.HttpUrl;
} }
} }
catch (SheepItException e) { catch (SheepItException e) {
this.state = State.UNKNOWN; ajob.setState(State.ERRORING);
gui.setRenderingProjectName(""); gui.setRenderingProjectName("");
for (String logline : directoryManager.filesystemHealthCheck()) { for (String logline : directoryManager.filesystemHealthCheck()) {
log.debug(logline); log.debug(logline);
@@ -814,7 +807,7 @@ import okhttp3.HttpUrl;
} }
}; };
this.state = State.RENDERING; ajob.setState(State.RENDERING);
Error.Type err = ajob.render(removeSceneDirectoryOnceRenderHasStartedObserver); Error.Type err = ajob.render(removeSceneDirectoryOnceRenderHasStartedObserver);
gui.setRenderingProjectName(""); gui.setRenderingProjectName("");
gui.setRemainingTime(""); gui.setRemainingTime("");
@@ -853,7 +846,7 @@ import okhttp3.HttpUrl;
} }
} }
} }
this.state = State.UNKNOWN; ajob.setState(State.UNKNOWN);
return err; return err;
} }
@@ -1004,6 +997,7 @@ import okhttp3.HttpUrl;
this.log.debug(checkpoint, "Client::confirmeJob url " + urlReal); this.log.debug(checkpoint, "Client::confirmeJob url " + urlReal);
this.log.debug(checkpoint, "path frame " + ajob.getRenderOutput().getFullImagePath()); this.log.debug(checkpoint, "path frame " + ajob.getRenderOutput().getFullImagePath());
ajob.setState(State.UPLOADING);
this.isValidatingJob = true; this.isValidatingJob = true;
int maxTries = 3; int maxTries = 3;
int timeToSleep = 22_000; int timeToSleep = 22_000;
@@ -1096,7 +1090,7 @@ import okhttp3.HttpUrl;
* @int checkpoint - the checkpoint associated with the job (to add any additional log to the render output) * @int checkpoint - the checkpoint associated with the job (to add any additional log to the render output)
* @Job job - the job to be validated * @Job job - the job to be validated
*/ */
@AllArgsConstructor private class QueuedJob { @AllArgsConstructor @Getter public class QueuedJob {
final private int checkpoint; final private int checkpoint;
final private Job job; final private Job job;
} }

View File

@@ -27,13 +27,16 @@ import java.io.OutputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.nio.file.Files; import java.nio.file.Files;
import java.net.*; import java.net.*;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -55,6 +58,9 @@ import com.sheepit.client.ui.Gui;
import com.sheepit.client.ui.Stats; import com.sheepit.client.ui.Stats;
import com.sheepit.client.ui.TransferStats; import com.sheepit.client.ui.TransferStats;
import com.sheepit.client.utils.Utils; import com.sheepit.client.utils.Utils;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import lombok.Getter; import lombok.Getter;
import org.simpleframework.xml.core.Persister; import org.simpleframework.xml.core.Persister;
@@ -121,7 +127,7 @@ public class ServerRequest extends Thread {
this.log = Log.getInstance(); this.log = Log.getInstance();
this.directoryManager = new DirectoryManager(this.user_config); this.directoryManager = new DirectoryManager(this.user_config);
this.lastRequestTime = 0; this.lastRequestTime = 0;
this.keepmealive_duration = 15 * 60 * 1000; // default 15min this.keepmealive_duration = 10 * 60 * 1000; // default 10min
// OkHttp performs best when we create a single OkHttpClient instance and reuse it for all of the HTTP calls. This is because each client holds its own // OkHttp performs best when we create a single OkHttpClient instance and reuse it for all of the HTTP calls. This is because each client holds its own
// connection pool and thread pools.Reusing connections and threads reduces latency and saves memory. Conversely, creating a client for each request // connection pool and thread pools.Reusing connections and threads reduces latency and saves memory. Conversely, creating a client for each request
@@ -142,18 +148,30 @@ public class ServerRequest extends Thread {
HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(this.getPage("keepmealive"))).newBuilder(); HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(this.getPage("keepmealive"))).newBuilder();
urlBuilder.addQueryParameter("paused", String.valueOf(this.client.isSuspended())); urlBuilder.addQueryParameter("paused", String.valueOf(this.client.isSuspended()));
urlBuilder.addQueryParameter("sleeping", String.valueOf(this.client.nextJobRequest() != null)); urlBuilder.addQueryParameter("sleeping", String.valueOf(this.client.nextJobRequest() != null));
urlBuilder.addQueryParameter("state", String.valueOf(this.client.getState()));
if (this.client != null && this.client.getRenderingJob() != null) { if (this.client != null) {
Job job = this.client.getRenderingJob(); Map<String, com.sheepit.client.rendering.State> onGoingJob = new HashMap<>();
if (this.client.getRenderingJob() != null) {
urlBuilder.addQueryParameter("frame", job.getRenderSettings().getFrameNumber()).addQueryParameter("job", job.getId()); Job job = this.client.getRenderingJob();
RenderProcess process = job.getRenderProcess(); urlBuilder.addQueryParameter("frame", job.getRenderSettings().getFrameNumber()).addQueryParameter("job", job.getId());
if (process != null) {
urlBuilder.addQueryParameter("rendertime", String.valueOf(process.getDuration())) RenderProcess process = job.getRenderProcess();
.addQueryParameter("remainingtime", String.valueOf(process.getRemainingDuration())); if (process != null) {
urlBuilder.addQueryParameter("rendertime", String.valueOf(process.getDuration()))
.addQueryParameter("remainingtime", String.valueOf(process.getRemainingDuration()));
}
onGoingJob.put(job.getId(), job.getState());
} }
for (Client.QueuedJob queuedJob : this.client.getJobsToValidate()) { // no blocking call
onGoingJob.put(queuedJob.getJob().getId(), queuedJob.getJob().getState());
}
Moshi moshi = new Moshi.Builder().build();
Type type = Types.newParameterizedType(Map.class, String.class, com.sheepit.client.rendering.State.class);
JsonAdapter<Map<String, com.sheepit.client.rendering.State>> adapter = moshi.adapter(type);
urlBuilder.addQueryParameter("ongoing", adapter.toJson(onGoingJob));
} }
Response response = this.HTTPRequest(urlBuilder); Response response = this.HTTPRequest(urlBuilder);

View File

@@ -72,6 +72,8 @@ import static com.sheepit.client.rendering.RenderSettings.UPDATE_METHOD_BY_TILE;
public static final int SHOW_BASE_ICON = -1; public static final int SHOW_BASE_ICON = -1;
private State state;
private DownloadItem projectDownload; private DownloadItem projectDownload;
private DownloadItem rendererDownload; private DownloadItem rendererDownload;
private String id; private String id;
@@ -110,6 +112,7 @@ import static com.sheepit.client.rendering.RenderSettings.UPDATE_METHOD_BY_TILE;
renderProcess = new RenderProcess(log_); renderProcess = new RenderProcess(log_);
renderState = new RenderState(); renderState = new RenderState();
renderOutput = new RenderOutput(); renderOutput = new RenderOutput();
state = State.INITIALIZING;
} }
public void block() { public void block() {
@@ -158,6 +161,7 @@ import static com.sheepit.client.rendering.RenderSettings.UPDATE_METHOD_BY_TILE;
} }
public Error.Type render(Observer renderStarted) { public Error.Type render(Observer renderStarted) {
state = State.RENDERING;
gui.status("Rendering"); gui.status("Rendering");
RenderProcess process = getRenderProcess(); RenderProcess process = getRenderProcess();
Timer timerOfMaxRenderTime = null; Timer timerOfMaxRenderTime = null;

View File

@@ -0,0 +1,12 @@
package com.sheepit.client.rendering;
public enum State {
INITIALIZING,
DOWNLOADING,
PREPARING,
RENDERING,
RENDERING_DONE,
UPLOADING,
ERRORING,
UNKNOWN
}