Merge branch 'fix/ui-download-progress' into 'master'

Fix/ui download progress

See merge request sheepitrenderfarm/client!298
This commit is contained in:
Laurent Clouet
2024-04-11 15:24:50 +00:00
8 changed files with 97 additions and 58 deletions

View File

@@ -24,7 +24,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.Time;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
@@ -38,11 +37,9 @@ import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
@@ -795,6 +792,8 @@ import okhttp3.HttpUrl;
ExecutorService executor = Executors.newFixedThreadPool(total);
ArrayList<Callable<Error.Type>> tasks = new ArrayList<>();
this.gui.getDownloadProgress().reset("Downloading project");
for (int i = 0; i < total; i++) {
Chunk chunk = ajob_.getArchiveChunks().get(i);
@@ -804,7 +803,6 @@ import okhttp3.HttpUrl;
this.server,
this.gui,
this.log,
String.format(LOCALE, "chunk %d/%d", finalI + 1, total),
this.directoryManager.getActualStoragePathFor(chunk),
chunk.getMd5(),
String.format(LOCALE, "%s?chunk=%s", this.server.getPage("download-chunk"), chunk.getId())
@@ -839,11 +837,11 @@ import okhttp3.HttpUrl;
}
protected Error.Type downloadExecutable(Job ajob) throws SheepItException {
this.gui.getDownloadProgress().reset("Downloading Blender");
return (new DownloadManager(
this.server,
this.gui,
this.log,
"renderer",
this.directoryManager.getActualStorageBinaryPathFor(ajob),
ajob.getRendererMD5(),
String.format(LOCALE, "%s?job=%s", this.server.getPage("download-binary"), ajob.getId())

View File

@@ -36,15 +36,13 @@ public class DownloadManager {
private Log log;
// task specific objects
private String gui_text; // what do display on the gui
private String local_target;
private String md5; // expected md5 of the file, for check purpose
private String remote; // remote url
public DownloadManager(Server server, Gui gui, Log log, String gui_text, String local_target, String md5, String remote) {
public DownloadManager(Server server, Gui gui, Log log, String local_target, String md5, String remote) {
this.server = server;
this.gui = gui;
this.gui_text = gui_text;
this.log = log;
this.local_target = local_target;
this.md5 = md5;
@@ -61,15 +59,14 @@ public class DownloadManager {
do {
// if the binary or scene already exists in the cache
if (local_path_file.exists()) {
this.gui.status("Reusing cached " + this.gui_text);
this.gui.status("Reusing cached");
return Error.Type.OK;
}
// if the binary or scene is being downloaded by another client
else if (this.lockExists()) {
// Wait and check every second for file download completion but only update the GUI every 10 seconds to minimise CPU load
if (remaining % 10000 == 0) {
this.gui.status(String.format("Another client is downloading the %s. Cancel in %dmin %ds",
this.gui_text,
this.gui.status(String.format("Another client is downloading. Cancel in %dmin %ds",
TimeUnit.MILLISECONDS.toMinutes(remaining),
TimeUnit.MILLISECONDS.toSeconds(remaining) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(remaining))
));
@@ -100,16 +97,14 @@ public class DownloadManager {
}
}
this.gui.status(String.format("Downloading %s", this.gui_text));
this.gui.status(String.format("Downloading"));
return this.downloadActual();
}
private Error.Type downloadActual() throws SheepItException {
String update_ui = "Downloading " + this.gui_text;
// must download the archive
Error.Type ret = this.server.HTTPGetFile(this.remote, this.local_target, this.gui, update_ui);
Error.Type ret = this.server.HTTPGetFile(this.remote, this.local_target, this.gui);
if (ret == Error.Type.RENDERER_KILLED_BY_SERVER || ret == Error.Type.RENDERER_KILLED_BY_USER_OVER_TIME || ret == Error.Type.RENDERER_KILLED_BY_USER) {
return ret;
@@ -121,11 +116,11 @@ public class DownloadManager {
while ((ret != Error.Type.OK || md5_check == false) && attempts < this.maxDownloadFileAttempts) {
if (ret != Error.Type.OK) {
this.gui.error(String.format("Unable to download %s (error %s). Retrying now", this.gui_text, ret));
this.gui.error(String.format("Unable to download (error %s). Retrying now", ret));
this.log.debug("DownloadManager::downloadActual problem with Server.HTTPGetFile (return: " + ret + ") removing local file (path: " + this.local_target + ")");
}
else if (md5_check == false) {
this.gui.error(String.format("Verification of downloaded %s has failed. Retrying now", this.gui_text));
this.gui.error(String.format("Verification of downloaded %s has failed. Retrying now"));
this.log.debug("DownloadManager::downloadActual problem with Client::checkFile mismatch on md5, removing local file (path: " + this.local_target + ")");
}
@@ -134,7 +129,7 @@ public class DownloadManager {
this.log.debug("DownloadManager::downloadActual failed, let's try again (" + (attempts + 1) + "/" + this.maxDownloadFileAttempts + ") ...");
String partial_target = this.local_target + ".partial";
ret = this.server.HTTPGetFile(this.remote, partial_target, this.gui, update_ui);
ret = this.server.HTTPGetFile(this.remote, partial_target, this.gui);
md5_check = this.check();
attempts++;

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2024 Laurent CLOUET
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.sheepit.client;
public class DownloadProgress {
// global objects
private Gui gui;
private String guiPattern = "";
private long total = 0;
private long partial = 0;
public DownloadProgress(Gui gui) {
this.gui = gui;
}
public synchronized void reset(String pattern) {
this.guiPattern = pattern;
this.total = 0;
this.partial = 0;
}
public synchronized void addTotal(long size) {
this.total += size;
}
public synchronized void addProgress(long progress) {
this.partial += progress;
gui.status(String.format(this.guiPattern + " %.0f %%", 100.0f * this.partial / this.total));
}
}

View File

@@ -30,8 +30,6 @@ public interface Gui {
public void status(String msg_, int progress);
public void status(String msg_, int progress, long size);
public void updateTrayIcon(Integer percentage_);
public void setRenderingProjectName(String name_);
@@ -57,4 +55,6 @@ public interface Gui {
public void setComputeMethod(String computeMethod_);
public Client getClient();
public DownloadProgress getDownloadProgress();
}

View File

@@ -491,7 +491,7 @@ public class Server extends Thread {
}
}
public Error.Type HTTPGetFile(String url_, String destination_, Gui gui_, String status_) throws SheepItException {
public Error.Type HTTPGetFile(String url_, String destination_, Gui gui_) throws SheepItException {
this.log.debug("Server::HTTPGetFile destination: " + destination_);
InputStream is = null;
OutputStream output = null;
@@ -508,10 +508,14 @@ public class Server extends Thread {
output = new FileOutputStream(destination_ + ".partial");
long size = response.body().contentLength();
byte[] buffer = new byte[8 * 1024];
// only update the gui every 1MB
byte[] buffer = new byte[1024 * 1024];
int len = 0;
long written = 0;
long lastUpd = 0; // last GUI progress update
if (size != -1) {
gui_.getDownloadProgress().addTotal(size);
}
this.log.debug("Downloading file from " + response.request().url().host());
LocalDateTime startRequestTime = LocalDateTime.now();
@@ -526,20 +530,13 @@ public class Server extends Thread {
output.write(buffer, 0, len);
written += len;
if ((written - lastUpd) > 1000000) { // only update the gui every 1MB
if (size != -1) { // no header for contentlength
gui_.status(status_, (int) (100.0 * written / size), written);
}
lastUpd = written;
}
gui_.getDownloadProgress().addProgress(len);
}
LocalDateTime endRequestTime = LocalDateTime.now();
Duration duration = Duration.between(startRequestTime, endRequestTime);
this.dlStats.calc(written, ((duration.getSeconds() * 1000) + (duration.getNano() / 1000000)));
gui_.displayTransferStats(dlStats, ulStats);
gui_.status(status_, 100, size);
this.log.debug(String.format("File downloaded at %s/s, written %d bytes", new TransferStats(size, duration.toMillis() + 1).getAverageSessionSpeed(), written));

View File

@@ -24,6 +24,7 @@ import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.sheepit.client.Client;
import com.sheepit.client.Configuration;
import com.sheepit.client.DownloadProgress;
import com.sheepit.client.Gui;
import com.sheepit.client.SettingsLoader;
import com.sheepit.client.Stats;
@@ -132,6 +133,7 @@ public class GuiSwing extends JFrame implements Gui {
private boolean waitingForAuthentication;
private Client client;
private DownloadProgress downloadProgress;
private BufferedImage iconSprites;
private BufferedImage[] trayIconSprites;
@@ -141,6 +143,7 @@ public class GuiSwing extends JFrame implements Gui {
private ThreadClient threadClient;
public GuiSwing(boolean useSysTray_, String title_) {
downloadProgress = new DownloadProgress(this);
framesRendered = 0;
useSysTray = useSysTray_;
title = title_;
@@ -272,10 +275,6 @@ public class GuiSwing extends JFrame implements Gui {
}
}
@Override public void status(String msg, int progress, long size) {
this.status(msg, progress);
}
@Override public void setRenderingProjectName(String name_) {
if (activityWorking != null) {
this.activityWorking.setRenderingProjectName(name_);
@@ -329,6 +328,10 @@ public class GuiSwing extends JFrame implements Gui {
return client;
}
@Override public DownloadProgress getDownloadProgress() {
return downloadProgress;
}
@Override public void setClient(Client cli) {
client = cli;
}
@@ -496,5 +499,4 @@ public class GuiSwing extends JFrame implements Gui {
}
}
}
}

View File

@@ -20,6 +20,7 @@
package com.sheepit.client.standalone;
import com.sheepit.client.Client;
import com.sheepit.client.DownloadProgress;
import com.sheepit.client.Gui;
import com.sheepit.client.Log;
import com.sheepit.client.Stats;
@@ -46,8 +47,10 @@ public class GuiText implements Gui {
private String eta;
private Client client;
private DownloadProgress downloadProgress;
public GuiText() {
this.downloadProgress = new DownloadProgress(this);
this.framesRendered = 0;
this.log = Log.getInstance(null);
this.df = new SimpleDateFormat("MMM dd HH:mm:ss");
@@ -117,12 +120,8 @@ public class GuiText implements Gui {
}
@Override public void status(String msg, int progress) {
this.status(msg, progress, 0);
}
@Override public void status(String msg, int progress, long size) {
System.out.print("\r");
System.out.print(String.format("%s %s", this.df.format(new Date()), showProgress(msg, progress, size)));
System.out.print(String.format("%s %s", this.df.format(new Date()), showProgress(msg, progress)));
}
@Override public void error(String err_) {
@@ -178,11 +177,15 @@ public class GuiText implements Gui {
return client;
}
@Override public DownloadProgress getDownloadProgress() {
return downloadProgress;
}
@Override public void successfulAuthenticationEvent(String publickey) {
}
private String showProgress(String message, int progress, long size) {
private String showProgress(String message, int progress) {
StringBuilder progressBar = new StringBuilder(140);
if (progress < 100) {
@@ -196,10 +199,6 @@ public class GuiText implements Gui {
.append(String.join("", Collections.nCopies(20 - (int)(progress / 5), " ")))
.append(']');
if (size > 0) {
progressBar.append(String.format(" %dMB", (size / 1024 / 1024)));
}
if (!this.eta.equals("")) {
progressBar.append(String.format(" ETA %s", this.eta));
}

View File

@@ -20,6 +20,7 @@
package com.sheepit.client.standalone;
import com.sheepit.client.Client;
import com.sheepit.client.DownloadProgress;
import com.sheepit.client.Gui;
import com.sheepit.client.Stats;
import com.sheepit.client.TransferStats;
@@ -55,8 +56,10 @@ public class GuiTextOneLine implements Gui {
private boolean exiting = false;
private Client client;
private DownloadProgress downloadProgress;
public GuiTextOneLine() {
downloadProgress = new DownloadProgress(this);
project = "";
rendered = 0;
remaining = 0;
@@ -118,22 +121,19 @@ public class GuiTextOneLine implements Gui {
@Override public void status(String msg_, boolean overwriteSuspendedMsg) {
if (client != null && client.isSuspended()) {
if (overwriteSuspendedMsg) {
status = msg_;
status = msg_.replace("%", "%%"); // escape %
updateLine();
}
}
else {
status = msg_;
status = msg_.replace("%", "%%"); // escape %
updateLine();
}
}
@Override public void status(String msg, int progress) {
this.status(msg, progress, 0);
}
@Override public void status(String msg, int progress, long size) {
status = showProgress(msg, progress, size);
status = showProgress(msg, progress);
updateLine();
}
@@ -186,6 +186,10 @@ public class GuiTextOneLine implements Gui {
client = cli;
}
@Override public DownloadProgress getDownloadProgress() {
return downloadProgress;
}
@Override public void setComputeMethod(String computeMethod_) {
computeMethod = computeMethod_;
}
@@ -224,7 +228,7 @@ public class GuiTextOneLine implements Gui {
}
}
private String showProgress(String message, int progress, long size) {
private String showProgress(String message, int progress) {
StringBuilder progressBar = new StringBuilder(140);
progressBar
.append(message)
@@ -235,10 +239,6 @@ public class GuiTextOneLine implements Gui {
.append(String.join("", Collections.nCopies(10 - (int) (progress / 10), " ")))
.append(']');
if (size > 0) {
progressBar.append(String.format(" %dMB", (size / 1024 / 1024)));
}
if (!this.eta.equals("")) {
progressBar.append(String.format(" ETA %s", this.eta));
}