Move src files to standard java folder structure
This commit is contained in:
1118
src/main/java/com/sheepit/client/Client.java
Normal file
1118
src/main/java/com/sheepit/client/Client.java
Normal file
File diff suppressed because it is too large
Load Diff
337
src/main/java/com/sheepit/client/Configuration.java
Normal file
337
src/main/java/com/sheepit/client/Configuration.java
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 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;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sheepit.client.hardware.cpu.CPU;
|
||||
import com.sheepit.client.hardware.gpu.GPUDevice;
|
||||
import com.sheepit.client.os.OS;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Data public class Configuration {
|
||||
|
||||
public static final String jarVersion = getJarVersion();
|
||||
|
||||
public enum ComputeType {
|
||||
CPU_GPU, CPU, GPU
|
||||
} // accept job for ...
|
||||
|
||||
private String configFilePath;
|
||||
private File workingDirectory;
|
||||
private File sharedDownloadsDirectory;
|
||||
private File storageDirectory; // for permanent storage (binary archive)
|
||||
private boolean userHasSpecifiedACacheDir;
|
||||
private String static_exeDirName;
|
||||
private String login;
|
||||
private String password;
|
||||
private String proxy;
|
||||
private int maxUploadingJob;
|
||||
private int nbCores;
|
||||
private long maxAllowedMemory; // in KiB, max memory allowed for render
|
||||
private int maxRenderTime; // max render time per frame allowed
|
||||
private int priority;
|
||||
private ComputeType computeMethod;
|
||||
private GPUDevice GPUDevice;
|
||||
private boolean detectGPUs;
|
||||
private boolean printLog;
|
||||
private List<Pair<Calendar, Calendar>> requestTime;
|
||||
private long shutdownTime;
|
||||
private String shutdownMode;
|
||||
private String extras;
|
||||
private boolean autoSignIn;
|
||||
private boolean useSysTray;
|
||||
private boolean headless;
|
||||
private String UIType;
|
||||
private String hostname;
|
||||
private String theme;
|
||||
|
||||
public Configuration(File cache_dir_, String login_, String password_) {
|
||||
this.configFilePath = null;
|
||||
this.login = login_;
|
||||
this.password = password_;
|
||||
this.proxy = null;
|
||||
this.hostname = this.getDefaultHostname();
|
||||
this.static_exeDirName = "exe";
|
||||
this.maxUploadingJob = 1;
|
||||
this.nbCores = -1; // ie not set
|
||||
this.maxAllowedMemory = -1; // ie not set
|
||||
this.maxRenderTime = -1; // ie not set
|
||||
this.priority = 19; // default lowest
|
||||
this.computeMethod = null;
|
||||
this.GPUDevice = null;
|
||||
this.userHasSpecifiedACacheDir = false;
|
||||
this.detectGPUs = true;
|
||||
this.workingDirectory = null;
|
||||
this.sharedDownloadsDirectory = null;
|
||||
this.storageDirectory = null;
|
||||
this.setCacheDir(cache_dir_);
|
||||
this.printLog = false;
|
||||
this.requestTime = null;
|
||||
this.shutdownTime = -1;
|
||||
this.shutdownMode = "soft";
|
||||
this.extras = "";
|
||||
this.autoSignIn = false;
|
||||
this.useSysTray = false;
|
||||
this.headless = java.awt.GraphicsEnvironment.isHeadless();
|
||||
this.UIType = null;
|
||||
this.theme = null;
|
||||
}
|
||||
|
||||
public Configuration(Configuration config) {
|
||||
this(config.configFilePath, config.workingDirectory, config.sharedDownloadsDirectory, config.storageDirectory, config.userHasSpecifiedACacheDir,
|
||||
config.static_exeDirName, config.login, config.password, config.proxy, config.maxUploadingJob, config.nbCores, config.maxAllowedMemory, config.maxRenderTime,
|
||||
config.priority, config.computeMethod, config.GPUDevice, config.detectGPUs, config.printLog, config.requestTime, config.shutdownTime,
|
||||
config.shutdownMode, config.extras, config.autoSignIn, config.useSysTray, config.headless, config.UIType, config.hostname, config.theme);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("Configuration (workingDirectory '%s')", this.workingDirectory.getAbsolutePath());
|
||||
}
|
||||
|
||||
public void setUsePriority(int priority) {
|
||||
if (priority > 19)
|
||||
priority = 19;
|
||||
if (priority < -19)
|
||||
priority = -19;
|
||||
|
||||
this.priority = priority;
|
||||
|
||||
}
|
||||
|
||||
public int computeMethodToInt() {
|
||||
return this.computeMethod.ordinal();
|
||||
}
|
||||
|
||||
public void setCacheDir(File cache_dir_) {
|
||||
removeWorkingDirectory();
|
||||
if (cache_dir_ == null) {
|
||||
this.userHasSpecifiedACacheDir = false;
|
||||
try {
|
||||
this.workingDirectory = File.createTempFile("farm_", "");
|
||||
this.workingDirectory.createNewFile(); // hoho...
|
||||
this.workingDirectory.delete(); // hoho
|
||||
this.workingDirectory.mkdir();
|
||||
this.workingDirectory.deleteOnExit();
|
||||
|
||||
// since there is no working directory and the client will be working in the system temp directory,
|
||||
// we can also set up a 'permanent' directory for immutable files (like renderer binary)
|
||||
|
||||
this.storageDirectory = new File(this.workingDirectory.getParent() + File.separator + "sheepit_binary_cache");
|
||||
this.storageDirectory.mkdir();
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.userHasSpecifiedACacheDir = true;
|
||||
this.workingDirectory = new File(cache_dir_.getAbsolutePath() + File.separator + "sheepit");
|
||||
this.storageDirectory = new File(cache_dir_.getAbsolutePath() + File.separator + "sheepit_binary_cache");
|
||||
this.workingDirectory.mkdirs();
|
||||
this.storageDirectory.mkdirs();
|
||||
}
|
||||
|
||||
if (this.sharedDownloadsDirectory != null) {
|
||||
this.sharedDownloadsDirectory.mkdirs();
|
||||
|
||||
if (!this.sharedDownloadsDirectory.exists()) {
|
||||
System.err.println("Configuration::setCacheDir Unable to create common directory " + this.sharedDownloadsDirectory.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setStorageDir(File dir) {
|
||||
if (dir != null) {
|
||||
if (dir.exists() == false) {
|
||||
dir.mkdir();
|
||||
}
|
||||
this.storageDirectory = dir;
|
||||
}
|
||||
}
|
||||
|
||||
public File getStorageDir() {
|
||||
if (this.storageDirectory == null) {
|
||||
return this.workingDirectory;
|
||||
}
|
||||
else {
|
||||
return this.storageDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
public File getCacheDirForSettings() {
|
||||
if (this.userHasSpecifiedACacheDir == false) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
// when the user have a cache directory a "sheepit" and "sheepit_binary_cache" is be automaticaly added
|
||||
return this.workingDirectory.getParentFile();
|
||||
}
|
||||
}
|
||||
|
||||
public String getDefaultHostname() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
}
|
||||
catch (UnknownHostException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanWorkingDirectory() {
|
||||
this.cleanDirectory(this.workingDirectory);
|
||||
this.cleanDirectory(this.storageDirectory);
|
||||
}
|
||||
|
||||
public boolean cleanDirectory(File dir) {
|
||||
if (dir == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
Utils.delete(file);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
String extension = file.getName().substring(file.getName().lastIndexOf('.')).toLowerCase();
|
||||
String name = file.getName().substring(0, file.getName().length() - 1 * extension.length());
|
||||
if (extension.equals(".zip")) {
|
||||
// check if the md5 of the file is ok
|
||||
String md5_local = Utils.md5(file.getAbsolutePath());
|
||||
|
||||
if (md5_local.equals(name) == false) {
|
||||
file.delete();
|
||||
}
|
||||
|
||||
// TODO: remove old one
|
||||
}
|
||||
else {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
catch (StringIndexOutOfBoundsException e) { // because the file does not have an . in his path
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void removeWorkingDirectory() {
|
||||
if (this.userHasSpecifiedACacheDir) {
|
||||
this.cleanWorkingDirectory();
|
||||
}
|
||||
else {
|
||||
Utils.delete(this.workingDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
public List<File> getLocalCacheFiles() {
|
||||
List<File> files_local = new LinkedList<File>();
|
||||
List<File> files = new LinkedList<File>();
|
||||
if (this.workingDirectory != null) {
|
||||
File[] filesInDirectory = this.workingDirectory.listFiles();
|
||||
if (filesInDirectory != null) {
|
||||
files.addAll(Arrays.asList(filesInDirectory));
|
||||
}
|
||||
}
|
||||
if (this.storageDirectory != null) {
|
||||
File[] filesInDirectory = this.storageDirectory.listFiles();
|
||||
if (filesInDirectory != null) {
|
||||
files.addAll(Arrays.asList(filesInDirectory));
|
||||
}
|
||||
}
|
||||
if (this.sharedDownloadsDirectory != null) {
|
||||
File[] filesInDirectory = this.sharedDownloadsDirectory.listFiles();
|
||||
if (filesInDirectory != null) {
|
||||
files.addAll(Arrays.asList(filesInDirectory));
|
||||
}
|
||||
}
|
||||
|
||||
for (File file : files) {
|
||||
if (file.isFile()) {
|
||||
try {
|
||||
String extension = file.getName().substring(file.getName().lastIndexOf('.')).toLowerCase();
|
||||
String name = file.getName().substring(0, file.getName().length() - 1 * extension.length());
|
||||
if (extension.equals(".zip")) {
|
||||
// check if the md5 of the file is ok
|
||||
String md5_local = Utils.md5(file.getAbsolutePath());
|
||||
|
||||
if (md5_local.equals(name)) {
|
||||
files_local.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (StringIndexOutOfBoundsException e) { // because the file does not have an . his path
|
||||
}
|
||||
}
|
||||
}
|
||||
return files_local;
|
||||
}
|
||||
|
||||
private static String getJarVersion() {
|
||||
String versionPath = "/VERSION";
|
||||
String version = "6.0.0";
|
||||
|
||||
InputStream versionStream = Client.class.getResourceAsStream(versionPath);
|
||||
if (versionStream != null) {
|
||||
try {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(versionStream));
|
||||
version = in.readLine();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
System.err.println("Configuration::getJarVersion error while reading manifest file (" + versionPath + "): " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
else {
|
||||
System.err.println("Configuration::getJarVersion Failed to get version file");
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
public boolean checkOSisSupported() {
|
||||
return OS.getOS() != null && OS.getOS().isSupported();
|
||||
}
|
||||
|
||||
public boolean checkCPUisSupported() {
|
||||
OS os = OS.getOS();
|
||||
if (os != null) {
|
||||
CPU cpu = os.getCPU();
|
||||
return cpu != null && cpu.haveData();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
215
src/main/java/com/sheepit/client/Error.java
Normal file
215
src/main/java/com/sheepit/client/Error.java
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 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 Error {
|
||||
public enum Type {
|
||||
// id have to be kept synchronised with the server side.
|
||||
OK(0),
|
||||
UNKNOWN(99),
|
||||
WRONG_CONFIGURATION(1),
|
||||
AUTHENTICATION_FAILED(2),
|
||||
TOO_OLD_CLIENT(3),
|
||||
SESSION_DISABLED(4),
|
||||
RENDERER_NOT_AVAILABLE(5),
|
||||
MISSING_RENDERER(6),
|
||||
MISSING_SCENE(7),
|
||||
NOOUTPUTFILE(8),
|
||||
IMAGE_TOO_LARGE(26),
|
||||
DOWNLOAD_FILE(9),
|
||||
CAN_NOT_CREATE_DIRECTORY(10),
|
||||
NETWORK_ISSUE(11),
|
||||
RENDERER_CRASHED(12),
|
||||
RENDERER_CRASHED_PYTHON_ERROR(24),
|
||||
RENDERER_OUT_OF_VIDEO_MEMORY(13),
|
||||
RENDERER_OUT_OF_MEMORY(21),
|
||||
RENDERER_KILLED(14),
|
||||
RENDERER_KILLED_BY_USER(20),
|
||||
RENDERER_KILLED_BY_USER_OVER_TIME(23),
|
||||
RENDERER_KILLED_BY_SERVER(22),
|
||||
RENDERER_MISSING_LIBRARIES(15),
|
||||
FAILED_TO_EXECUTE(16),
|
||||
OS_NOT_SUPPORTED(17),
|
||||
CPU_NOT_SUPPORTED(18),
|
||||
GPU_NOT_SUPPORTED(19),
|
||||
ENGINE_NOT_AVAILABLE(27),
|
||||
VALIDATION_FAILED(25),
|
||||
IMAGE_WRONG_DIMENSION(26),
|
||||
|
||||
// internal error handling
|
||||
NO_SPACE_LEFT_ON_DEVICE(100),
|
||||
ERROR_BAD_UPLOAD_RESPONSE(101),
|
||||
PATH_INVALID(102),
|
||||
NO_WRITE_PERMISSION(103),
|
||||
;
|
||||
|
||||
private final int id;
|
||||
|
||||
private Type(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ServerCode {
|
||||
OK(0),
|
||||
UNKNOWN(999),
|
||||
|
||||
CONFIGURATION_ERROR_NO_CLIENT_VERSION_GIVEN(100),
|
||||
CONFIGURATION_ERROR_CLIENT_TOO_OLD(101),
|
||||
CONFIGURATION_ERROR_AUTH_FAILED(102),
|
||||
CONFIGURATION_ERROR_WEB_SESSION_EXPIRED(103),
|
||||
CONFIGURATION_ERROR_MISSING_PARAMETER(104),
|
||||
|
||||
JOB_REQUEST_NOJOB(200),
|
||||
JOB_REQUEST_ERROR_NO_RENDERING_RIGHT(201),
|
||||
JOB_REQUEST_ERROR_DEAD_SESSION(202),
|
||||
JOB_REQUEST_ERROR_SESSION_DISABLED(203),
|
||||
JOB_REQUEST_ERROR_INTERNAL_ERROR(204),
|
||||
JOB_REQUEST_ERROR_RENDERER_NOT_AVAILABLE(205),
|
||||
JOB_REQUEST_SERVER_IN_MAINTENANCE(206),
|
||||
JOB_REQUEST_SERVER_OVERLOADED(207),
|
||||
|
||||
JOB_VALIDATION_ERROR_MISSING_PARAMETER(300),
|
||||
JOB_VALIDATION_ERROR_BROKEN_MACHINE(301), // in GPU the generated frame is black
|
||||
JOB_VALIDATION_ERROR_FRAME_IS_NOT_IMAGE(302),
|
||||
JOB_VALIDATION_ERROR_UPLOAD_FAILED(303),
|
||||
JOB_VALIDATION_ERROR_SESSION_DISABLED(304), // missing heartbeat or broken machine
|
||||
JOB_VALIDATION_IMAGE_TOO_LARGE(306),
|
||||
JOB_VALIDATION_ERROR_IMAGE_WRONG_DIMENSION(308),
|
||||
|
||||
KEEPMEALIVE_STOP_RENDERING(400),
|
||||
|
||||
// internal error handling
|
||||
ERROR_NO_ROOT(2),
|
||||
ERROR_BAD_RESPONSE(3),
|
||||
ERROR_REQUEST_FAILED(5),
|
||||
;
|
||||
|
||||
private final int id;
|
||||
|
||||
private ServerCode(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static ServerCode fromInt(int val) {
|
||||
ServerCode[] As = ServerCode.values();
|
||||
for (ServerCode A : As) {
|
||||
if (A.getValue() == val) {
|
||||
return A;
|
||||
}
|
||||
}
|
||||
return ServerCode.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public static Type ServerCodeToType(ServerCode sc) {
|
||||
switch (sc) {
|
||||
case OK:
|
||||
return Type.OK;
|
||||
case UNKNOWN:
|
||||
return Type.UNKNOWN;
|
||||
case CONFIGURATION_ERROR_CLIENT_TOO_OLD:
|
||||
return Type.TOO_OLD_CLIENT;
|
||||
case CONFIGURATION_ERROR_AUTH_FAILED:
|
||||
return Type.AUTHENTICATION_FAILED;
|
||||
|
||||
case CONFIGURATION_ERROR_NO_CLIENT_VERSION_GIVEN:
|
||||
case CONFIGURATION_ERROR_WEB_SESSION_EXPIRED:
|
||||
return Type.WRONG_CONFIGURATION;
|
||||
|
||||
case JOB_REQUEST_ERROR_SESSION_DISABLED:
|
||||
case JOB_VALIDATION_ERROR_SESSION_DISABLED:
|
||||
return Type.SESSION_DISABLED;
|
||||
|
||||
case JOB_REQUEST_ERROR_RENDERER_NOT_AVAILABLE:
|
||||
return Type.RENDERER_NOT_AVAILABLE;
|
||||
|
||||
default:
|
||||
return Type.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public static String humanString(Type in) {
|
||||
switch (in) {
|
||||
case ERROR_BAD_UPLOAD_RESPONSE:
|
||||
return "Corrupt response from the server when trying to upload data. The server might be overloaded or be encountering other issues. Will try again in a few minutes.";
|
||||
case NETWORK_ISSUE:
|
||||
return "Could not connect to the server, please check your connection to the internet.";
|
||||
case TOO_OLD_CLIENT:
|
||||
return "This client is too old, you need to update it.";
|
||||
case AUTHENTICATION_FAILED:
|
||||
return "Failed to authenticate, please check your login and password.";
|
||||
case DOWNLOAD_FILE:
|
||||
return "Error while downloading the project files. Will try another project in a few minutes.";
|
||||
case NOOUTPUTFILE:
|
||||
return "The renderer shut down without saving an image. This could be a broken project, or you could be missing libraries that Blender needs. Will try another project in a few minutes.";
|
||||
case IMAGE_TOO_LARGE:
|
||||
return "The generated image is too big to be handled by the server. Will try another project in a few minutes.";
|
||||
case RENDERER_CRASHED:
|
||||
return "The renderer has crashed. This is usually because the project consumes too much memory, or is just broken. Will try another project in a few minutes.";
|
||||
case RENDERER_CRASHED_PYTHON_ERROR:
|
||||
return "The renderer has crashed due to a Python error. Will try another project in a few minutes.";
|
||||
case RENDERER_OUT_OF_VIDEO_MEMORY:
|
||||
return "Project tried to use too much video memory (VRAM). Will try another project in a few minutes.";
|
||||
case RENDERER_OUT_OF_MEMORY:
|
||||
return "Project tried to use too much memory. Will try another project in a few minutes.";
|
||||
case GPU_NOT_SUPPORTED:
|
||||
return "Project's Blender version requires a newer GPU, or your CUDA setup is broken. Will try another project in a few minutes.";
|
||||
case RENDERER_MISSING_LIBRARIES:
|
||||
return "Failed to launch renderer. Please check if you have all the necessary libraries installed and if you have enough free space in your working directory.";
|
||||
case RENDERER_KILLED:
|
||||
return "Render canceled because either you stopped it from the website or the server did automatically (usually for a render taking too long).";
|
||||
case RENDERER_KILLED_BY_USER:
|
||||
return "Render canceled because you've blocked the project.";
|
||||
case RENDERER_KILLED_BY_SERVER:
|
||||
return "Render canceled because the project has been stopped by the server. Usually because the project will take too much time or it's been paused.";
|
||||
case SESSION_DISABLED:
|
||||
return "The server has disabled your session. Your client may have generated a broken frame (GPU not compatible, not enough RAM/VRAM, etc).";
|
||||
case RENDERER_NOT_AVAILABLE:
|
||||
return "The official Blender builds don't support rendering on this hardware.";
|
||||
case MISSING_RENDERER:
|
||||
return "Unable to locate the Blender binaries within the working directory.";
|
||||
case OS_NOT_SUPPORTED:
|
||||
return "This Operating System is not supported.";
|
||||
case CPU_NOT_SUPPORTED:
|
||||
return "This CPU is not supported.";
|
||||
case ENGINE_NOT_AVAILABLE:
|
||||
return "The project requires a rendering engine that isn't supported on this machine. Will try another project in a few minutes.";
|
||||
case NO_SPACE_LEFT_ON_DEVICE:
|
||||
return "Your storage medium does not have enough free space available.";
|
||||
case PATH_INVALID:
|
||||
return "The working directory path is not valid";
|
||||
case NO_WRITE_PERMISSION:
|
||||
return "Can't create/modify files due to missing write permissions in working directory.";
|
||||
case IMAGE_WRONG_DIMENSION:
|
||||
return "The rendered image had the wrong resolution. This project is configured incorrectly. Switching to another project.";
|
||||
default:
|
||||
return in.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/main/java/com/sheepit/client/Gui.java
Normal file
60
src/main/java/com/sheepit/client/Gui.java
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2013 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 interface Gui {
|
||||
public void start();
|
||||
|
||||
public void stop();
|
||||
|
||||
public void status(String msg_);
|
||||
|
||||
public void status(String msg_, boolean overwriteSuspendedMsg);
|
||||
|
||||
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_);
|
||||
|
||||
public void setRemainingTime(String time_);
|
||||
|
||||
public void setRenderingTime(String time_);
|
||||
|
||||
public void displayTransferStats(TransferStats downloads, TransferStats uploads);
|
||||
|
||||
public void displayStats(Stats stats);
|
||||
|
||||
public void displayUploadQueueStats(int queueSize, long queueVolume);
|
||||
|
||||
public void error(String err_);
|
||||
|
||||
public void AddFrameRendered();
|
||||
|
||||
public void successfulAuthenticationEvent(String publickey);
|
||||
|
||||
public void setClient(Client cli);
|
||||
|
||||
public void setComputeMethod(String computeMethod_);
|
||||
|
||||
public Client getClient();
|
||||
}
|
||||
886
src/main/java/com/sheepit/client/Job.java
Normal file
886
src/main/java/com/sheepit/client/Job.java
Normal file
@@ -0,0 +1,886 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 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;
|
||||
|
||||
import com.sheepit.client.Configuration.ComputeType;
|
||||
import com.sheepit.client.Error.Type;
|
||||
import com.sheepit.client.os.OS;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
import java.util.TimeZone;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Data public class Job {
|
||||
public static final String UPDATE_METHOD_BY_REMAINING_TIME = "remainingtime";
|
||||
public static final String UPDATE_METHOD_BY_TILE = "by_tile";
|
||||
|
||||
public static final int SHOW_BASE_ICON = -1;
|
||||
|
||||
private String frameNumber;
|
||||
private String sceneMD5;
|
||||
private String rendererMD5;
|
||||
private String id;
|
||||
private String outputImagePath;
|
||||
private long outputImageSize;
|
||||
private String path; // path inside of the archive
|
||||
private String rendererCommand;
|
||||
private String validationUrl;
|
||||
private String script;
|
||||
private boolean useGPU;
|
||||
private String name;
|
||||
private char[] password;
|
||||
private String updateRenderingStatusMethod;
|
||||
private String blenderShortVersion;
|
||||
private String blenderLongVersion;
|
||||
private boolean synchronousUpload;
|
||||
private RenderProcess render;
|
||||
private boolean askForRendererKill;
|
||||
private boolean userBlockJob;
|
||||
private boolean serverBlockJob;
|
||||
private Gui gui;
|
||||
private Configuration configuration;
|
||||
private Log log;
|
||||
|
||||
public Job(Configuration config_, Gui gui_, Log log_, String id_, String frame_, String path_, boolean use_gpu, String command_, String validationUrl_,
|
||||
String script_, String sceneMd5_, String rendererMd5_, String name_, char[] password_, boolean synchronous_upload_,
|
||||
String update_method_) {
|
||||
configuration = config_;
|
||||
id = id_;
|
||||
frameNumber = frame_;
|
||||
path = path_;
|
||||
useGPU = use_gpu;
|
||||
rendererCommand = command_;
|
||||
validationUrl = validationUrl_;
|
||||
sceneMD5 = sceneMd5_;
|
||||
rendererMD5 = rendererMd5_;
|
||||
name = name_;
|
||||
password = password_;
|
||||
synchronousUpload = synchronous_upload_;
|
||||
gui = gui_;
|
||||
outputImagePath = null;
|
||||
outputImageSize = 0;
|
||||
script = script_;
|
||||
updateRenderingStatusMethod = update_method_;
|
||||
askForRendererKill = false;
|
||||
userBlockJob = false;
|
||||
serverBlockJob = false;
|
||||
log = log_;
|
||||
render = new RenderProcess(log_);
|
||||
blenderShortVersion = null;
|
||||
blenderLongVersion = null;
|
||||
}
|
||||
|
||||
public void block() {
|
||||
setAskForRendererKill(true);
|
||||
setUserBlockJob(true);
|
||||
RenderProcess process = getProcessRender();
|
||||
if (process != null) {
|
||||
OS.getOS().kill(process.getProcess());
|
||||
}
|
||||
}
|
||||
|
||||
public RenderProcess getProcessRender() {
|
||||
return render;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String
|
||||
.format("Job (numFrame '%s' sceneMD5 '%s' rendererMD5 '%s' ID '%s' pictureFilename '%s' jobPath '%s' gpu %s name '%s' updateRenderingStatusMethod '%s' render %s)",
|
||||
frameNumber, sceneMD5, rendererMD5, id, outputImagePath, path, useGPU, name, updateRenderingStatusMethod, render);
|
||||
}
|
||||
|
||||
public String getPrefixOutputImage() {
|
||||
return id + "_";
|
||||
}
|
||||
|
||||
public String getRendererDirectory() {
|
||||
return configuration.getWorkingDirectory().getAbsolutePath() + File.separator + rendererMD5;
|
||||
}
|
||||
|
||||
public String getRequiredRendererArchivePath() {
|
||||
if (configuration.getSharedDownloadsDirectory() != null) {
|
||||
return configuration.getSharedDownloadsDirectory().getAbsolutePath() + File.separator + rendererMD5 + ".zip";
|
||||
}
|
||||
else {
|
||||
return getRendererArchivePath();
|
||||
}
|
||||
}
|
||||
|
||||
public String getRendererPath() {
|
||||
return getRendererDirectory() + File.separator + OS.getOS().getRenderBinaryPath();
|
||||
}
|
||||
|
||||
public String getRendererArchivePath() {
|
||||
return configuration.getStorageDir().getAbsolutePath() + File.separator + rendererMD5 + ".zip";
|
||||
}
|
||||
|
||||
public String getRequiredSceneArchivePath() {
|
||||
if (configuration.getSharedDownloadsDirectory() != null) {
|
||||
return configuration.getSharedDownloadsDirectory().getAbsolutePath() + File.separator + sceneMD5 + ".zip";
|
||||
}
|
||||
else {
|
||||
return getSceneArchivePath();
|
||||
}
|
||||
}
|
||||
|
||||
public String getSceneDirectory() {
|
||||
return configuration.getWorkingDirectory().getAbsolutePath() + File.separator + sceneMD5;
|
||||
}
|
||||
|
||||
public String getScenePath() {
|
||||
return getSceneDirectory() + File.separator + this.path;
|
||||
}
|
||||
|
||||
public String getSceneArchivePath() {
|
||||
return configuration.getWorkingDirectory().getAbsolutePath() + File.separator + sceneMD5 + ".zip";
|
||||
}
|
||||
|
||||
public Error.Type render(Observer renderStarted) {
|
||||
gui.status("Rendering");
|
||||
RenderProcess process = getProcessRender();
|
||||
Timer timerOfMaxRenderTime = null;
|
||||
String core_script = "";
|
||||
// When sending Ctrl+C to the terminal it also get's sent to all subprocesses e.g. also the render process.
|
||||
// The java program handles Ctrl+C but the renderer quits on Ctrl+C.
|
||||
// This script causes the renderer to ignore Ctrl+C.
|
||||
String ignore_signal_script = "import signal\n" + "def hndl(signum, frame):\n" + " pass\n" + "signal.signal(signal.SIGINT, hndl)\n";
|
||||
if (isUseGPU() && configuration.getGPUDevice() != null && configuration.getComputeMethod() != ComputeType.CPU) {
|
||||
core_script = "sheepit_set_compute_device(\"" + configuration.getGPUDevice().getType() + "\", \"GPU\", \"" + configuration.getGPUDevice().getId()
|
||||
+ "\")\n";
|
||||
gui.setComputeMethod("GPU");
|
||||
}
|
||||
else {
|
||||
// Otherwise (CPU), fix the tile size to 32x32px
|
||||
core_script = "sheepit_set_compute_device(\"NONE\", \"CPU\", \"CPU\")\n";
|
||||
gui.setComputeMethod("CPU");
|
||||
}
|
||||
|
||||
core_script += ignore_signal_script;
|
||||
File script_file = null;
|
||||
String[] command1 = getRendererCommand().split(" ");
|
||||
int size_command = command1.length + 2; // + 2 for script
|
||||
|
||||
if (configuration.getNbCores() > 0) { // user has specified something
|
||||
size_command += 2;
|
||||
}
|
||||
|
||||
List<String> command = new ArrayList<>(size_command);
|
||||
|
||||
Map<String, String> new_env = new HashMap<>();
|
||||
|
||||
|
||||
new_env.put("TEMP", configuration.getWorkingDirectory().getAbsolutePath().replace("\\", "\\\\"));
|
||||
new_env.put("TMP", configuration.getWorkingDirectory().getAbsolutePath().replace("\\", "\\\\"));
|
||||
new_env.put("CORES", Integer.toString(configuration.getNbCores()));
|
||||
new_env.put("PRIORITY", Integer.toString(configuration.getPriority()));
|
||||
//make sure the system doesn´t interfere with the blender runtime, and that blender doesn´t attempt to load external libraries/scripts.
|
||||
new_env.put("BLENDER_USER_CONFIG", "");
|
||||
new_env.put("BLENDER_USER_SCRIPTS", "");
|
||||
new_env.put("BLENDER_SYSTEM_SCRIPTS", "");
|
||||
new_env.put("BLENDER_USER_DATAFILES", "");
|
||||
new_env.put("BLENDER_SYSTEM_DATAFILES", "");
|
||||
new_env.put("BLENDER_SYSTEM_PYTHON", "");
|
||||
new_env.put("PYTHONPATH", ""); // make sure blender is using the embedded python, if not it could create "Fatal Python error: Py_Initialize"
|
||||
new_env.put("PYTHONHOME", "");// make sure blender is using the embedded python, if not it could create "Fatal Python error: Py_Initialize"
|
||||
new_env.put("OCIO", ""); //prevent blender from loading a non-standard color configuration
|
||||
|
||||
for (String arg : command1) {
|
||||
switch (arg) {
|
||||
case ".c":
|
||||
command.add(getScenePath());
|
||||
command.add("-P");
|
||||
|
||||
try {
|
||||
script_file = File.createTempFile("script_", "", configuration.getWorkingDirectory());
|
||||
File file = new File(script_file.getAbsolutePath());
|
||||
FileWriter txt;
|
||||
txt = new FileWriter(file);
|
||||
|
||||
PrintWriter out = new PrintWriter(txt);
|
||||
out.write(getScript());
|
||||
out.write("\n");
|
||||
out.write(core_script); // GPU part
|
||||
out.write("\n"); // GPU part
|
||||
out.close();
|
||||
|
||||
command.add(script_file.getAbsolutePath());
|
||||
}
|
||||
catch (IOException e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
log.error("Job::render exception on script generation, will return UNKNOWN " + e + " stacktrace " + sw.toString());
|
||||
return Error.Type.UNKNOWN;
|
||||
}
|
||||
script_file.deleteOnExit();
|
||||
break;
|
||||
case ".e":
|
||||
command.add(getRendererPath());
|
||||
// the number of cores has to be put after the binary and before the scene arg
|
||||
if (configuration.getNbCores() > 0) {
|
||||
command.add("-t");
|
||||
command.add(Integer.toString(configuration.getNbCores()));
|
||||
}
|
||||
break;
|
||||
case ".o":
|
||||
command.add(configuration.getWorkingDirectory().getAbsolutePath() + File.separator + getPrefixOutputImage());
|
||||
break;
|
||||
case ".f":
|
||||
command.add(getFrameNumber());
|
||||
break;
|
||||
default:
|
||||
command.add(arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Timer memoryCheck = new Timer();
|
||||
try {
|
||||
renderStartedObservable event = new renderStartedObservable(renderStarted);
|
||||
String line;
|
||||
log.debug(command.toString());
|
||||
OS os = OS.getOS();
|
||||
process.setCoresUsed(configuration.getNbCores());
|
||||
process.start();
|
||||
getProcessRender().setProcess(os.exec(command, new_env));
|
||||
getProcessRender().setOsProcess(OS.operatingSystem.getProcess((int) getProcessRender().getProcess().pid()));
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(getProcessRender().getProcess().getInputStream()));
|
||||
memoryCheck.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateProcess();
|
||||
}
|
||||
}, 0L, 200L);
|
||||
|
||||
// Make initial test/power frames ignore the maximum render time in user configuration. Initial test frames have Job IDs below 20
|
||||
// so we just activate the user defined timeout when the scene is not one of the initial ones.
|
||||
if (configuration.getMaxRenderTime() > 0 && Integer.parseInt(this.getId()) >= 20) {
|
||||
timerOfMaxRenderTime = new Timer();
|
||||
timerOfMaxRenderTime.schedule(new TimerTask() {
|
||||
@Override public void run() {
|
||||
RenderProcess process = getProcessRender();
|
||||
if (process != null) {
|
||||
long duration = (new Date().getTime() - process.getStartTime()) / 1000; // in seconds
|
||||
if (configuration.getMaxRenderTime() > 0 && duration > configuration.getMaxRenderTime()) {
|
||||
setAskForRendererKill(true);
|
||||
log.debug("Killing render because process duration");
|
||||
OS.getOS().kill(process.getProcess());
|
||||
}
|
||||
}
|
||||
}
|
||||
}, configuration.getMaxRenderTime() * 1000 + 2000); // +2s to be sure the delay is over
|
||||
}
|
||||
|
||||
log.debug("renderer output");
|
||||
try {
|
||||
int progress = -1;
|
||||
|
||||
Pattern progressPattern = Pattern.compile(" (Rendered|Path Tracing Tile|Rendering|Sample) (\\d+)\\s?\\/\\s?(\\d+)( Tiles| samples|,)*");
|
||||
|
||||
// Initialise the progress bar in the icon and the UI (0% completed at this time)
|
||||
gui.updateTrayIcon(0);
|
||||
gui.status("Preparing scene", 0);
|
||||
|
||||
while ((line = input.readLine()) != null) {
|
||||
log.debug(line);
|
||||
|
||||
// Process lines until the version is loaded (usually first or second line of log)
|
||||
if (blenderLongVersion == null) {
|
||||
Pattern blenderPattern = Pattern.compile("Blender (([0-9]{1,3}\\.[0-9]{0,3}).*)$");
|
||||
Matcher blendDetectedVersion = blenderPattern.matcher(line);
|
||||
|
||||
if (blendDetectedVersion.find()) {
|
||||
blenderLongVersion = blendDetectedVersion.group(1);
|
||||
blenderShortVersion = blendDetectedVersion.group(2);
|
||||
}
|
||||
}
|
||||
|
||||
progress = computeRenderingProgress(line, progressPattern, progress);
|
||||
if (configuration.getMaxAllowedMemory() != -1 && getProcessRender().getMemoryUsed().get() > configuration.getMaxAllowedMemory()) {
|
||||
log.debug("Blocking render because process ram used (" + getProcessRender().getMemoryUsed().get() + "k) is over user setting (" + configuration
|
||||
.getMaxAllowedMemory() + "k)");
|
||||
OS.getOS().kill(process.getProcess());
|
||||
process.finish();
|
||||
if (script_file != null) {
|
||||
script_file.delete();
|
||||
}
|
||||
|
||||
// Once the process is finished (either finished successfully or with an error) move back to
|
||||
// base icon (isolated S with no progress bar)
|
||||
gui.updateTrayIcon(Job.SHOW_BASE_ICON);
|
||||
|
||||
return Error.Type.RENDERER_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
updateRenderingStatus(line, progress);
|
||||
Type error = detectError(line);
|
||||
if (error != Error.Type.OK) {
|
||||
if (script_file != null) {
|
||||
script_file.delete();
|
||||
}
|
||||
|
||||
// Put back base icon
|
||||
gui.updateTrayIcon(Job.SHOW_BASE_ICON);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!event.isStarted() && (getProcessRender().getMemoryUsed().get() > 0 || process.getRemainingDuration() > 0)) {
|
||||
event.doNotifyIsStarted();
|
||||
}
|
||||
}
|
||||
input.close();
|
||||
}
|
||||
catch (IOException err1) { // for the input.readline
|
||||
// most likely The handle is invalid
|
||||
log.error("Job::render exception(B) (silent error) " + err1);
|
||||
}
|
||||
finally {
|
||||
memoryCheck.cancel();
|
||||
}
|
||||
|
||||
// Put back base icon
|
||||
gui.updateTrayIcon(Job.SHOW_BASE_ICON);
|
||||
|
||||
log.debug("end of rendering");
|
||||
}
|
||||
catch (Exception err) {
|
||||
if (script_file != null) {
|
||||
script_file.delete();
|
||||
}
|
||||
StringWriter sw = new StringWriter();
|
||||
err.printStackTrace(new PrintWriter(sw));
|
||||
log.error("Job::render exception(A) " + err + " stacktrace " + sw.toString());
|
||||
return Error.Type.FAILED_TO_EXECUTE;
|
||||
}
|
||||
|
||||
int exit_value = process.exitValue();
|
||||
process.finish();
|
||||
if (timerOfMaxRenderTime != null) {
|
||||
timerOfMaxRenderTime.cancel();
|
||||
}
|
||||
|
||||
if (script_file != null) {
|
||||
script_file.delete();
|
||||
}
|
||||
|
||||
// find the picture file
|
||||
final String filename_without_extension = getPrefixOutputImage() + getFrameNumber();
|
||||
|
||||
FilenameFilter textFilter = new FilenameFilter() {
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.startsWith(filename_without_extension);
|
||||
}
|
||||
};
|
||||
|
||||
File[] files = configuration.getWorkingDirectory().listFiles(textFilter);
|
||||
|
||||
if (isAskForRendererKill()) {
|
||||
log.debug("Job::render been asked to end render");
|
||||
|
||||
long duration = (new Date().getTime() - process.getStartTime()) / 1000; // in seconds
|
||||
if (configuration.getMaxRenderTime() > 0 && duration > configuration.getMaxRenderTime() && Integer.parseInt(this.getId()) >= 20) {
|
||||
log.debug("Render killed because process duration (" + duration + "s) is over user setting (" + configuration.getMaxRenderTime() + "s)");
|
||||
return Error.Type.RENDERER_KILLED_BY_USER_OVER_TIME;
|
||||
}
|
||||
|
||||
if (files.length != 0) {
|
||||
new File(files[0].getAbsolutePath()).delete();
|
||||
}
|
||||
if (isServerBlockJob()) {
|
||||
return Error.Type.RENDERER_KILLED_BY_SERVER;
|
||||
}
|
||||
if (isUserBlockJob()) {
|
||||
return Error.Type.RENDERER_KILLED_BY_USER;
|
||||
}
|
||||
return Error.Type.RENDERER_KILLED;
|
||||
}
|
||||
|
||||
if (files.length == 0) {
|
||||
log.error("Job::render no picture file found (after finished render (filename_without_extension " + filename_without_extension + ")");
|
||||
|
||||
String basename = "";
|
||||
try {
|
||||
basename = getPath().substring(0, getPath().lastIndexOf('.'));
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
File crash_file = new File(configuration.getWorkingDirectory() + File.separator + basename + ".crash.txt");
|
||||
if (crash_file.exists()) {
|
||||
log.error("Job::render crash file found => the renderer crashed");
|
||||
crash_file.delete();
|
||||
return Error.Type.RENDERER_CRASHED;
|
||||
}
|
||||
|
||||
if (exit_value == 127 && process.getDuration() < 10) {
|
||||
log.error("Job::render renderer returned 127 and took " + process.getDuration() + "s, some libraries may be missing");
|
||||
return Error.Type.RENDERER_MISSING_LIBRARIES;
|
||||
}
|
||||
|
||||
return Error.Type.NOOUTPUTFILE;
|
||||
}
|
||||
else {
|
||||
setOutputImagePath(files[0].getAbsolutePath());
|
||||
this.outputImageSize = new File(getOutputImagePath()).length();
|
||||
log.debug(String.format("Job::render pictureFilename: %s, size: %d'", getOutputImagePath(), this.outputImageSize));
|
||||
}
|
||||
|
||||
File scene_dir = new File(getSceneDirectory());
|
||||
long date_modification_scene_directory = (long) Utils.lastModificationTime(scene_dir);
|
||||
if (date_modification_scene_directory > process.getStartTime()) {
|
||||
scene_dir.delete();
|
||||
}
|
||||
|
||||
gui.status(String.format("Frame rendered in %dmin%ds", process.getDuration() / 60, process.getDuration() % 60));
|
||||
|
||||
return Error.Type.OK;
|
||||
}
|
||||
|
||||
private int computeRenderingProgress(String line, Pattern tilePattern, int currentProgress) {
|
||||
Matcher standardTileInfo = tilePattern.matcher(line);
|
||||
int newProgress = currentProgress;
|
||||
|
||||
if (standardTileInfo.find()) {
|
||||
int tileJustProcessed = Integer.parseInt(standardTileInfo.group(2));
|
||||
int totalTilesInJob = Integer.parseInt(standardTileInfo.group(3));
|
||||
|
||||
newProgress = Math.abs((tileJustProcessed * 100) / totalTilesInJob);
|
||||
}
|
||||
|
||||
// Only update the tray icon and the screen if percentage has changed
|
||||
if (newProgress != currentProgress) {
|
||||
gui.updateTrayIcon(newProgress);
|
||||
gui.status("Rendering", newProgress);
|
||||
}
|
||||
|
||||
return newProgress;
|
||||
}
|
||||
|
||||
private void updateRenderingStatus(String line, int progress) {
|
||||
if (getUpdateRenderingStatusMethod() == null || getUpdateRenderingStatusMethod().equals(Job.UPDATE_METHOD_BY_REMAINING_TIME)) {
|
||||
String search_remaining = "remaining:";
|
||||
int index = line.toLowerCase().indexOf(search_remaining);
|
||||
if (index != -1) {
|
||||
String buf1 = line.substring(index + search_remaining.length());
|
||||
index = buf1.indexOf(" ");
|
||||
|
||||
if (index != -1) {
|
||||
String remaining_time = buf1.substring(0, index).trim();
|
||||
int last_index = remaining_time.lastIndexOf('.'); //format 00:00:00.00 (hr:min:sec)
|
||||
if (last_index > 0) {
|
||||
remaining_time = remaining_time.substring(0, last_index);
|
||||
}
|
||||
|
||||
try {
|
||||
DateFormat date_parse_minute = new SimpleDateFormat("m:s");
|
||||
DateFormat date_parse_hour = new SimpleDateFormat("h:m:s");
|
||||
DateFormat date_parse = date_parse_minute;
|
||||
if (remaining_time.split(":").length > 2) {
|
||||
date_parse = date_parse_hour;
|
||||
}
|
||||
date_parse.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
Date date = date_parse.parse(remaining_time);
|
||||
gui.setRemainingTime(Utils.humanDuration(date));
|
||||
getProcessRender().setRemainingDuration((int) (date.getTime() / 1000));
|
||||
}
|
||||
catch (ParseException err) {
|
||||
log.error("Client::updateRenderingStatus ParseException " + err);
|
||||
}
|
||||
}
|
||||
}
|
||||
else { //extrapolate remaining time from time rendered & progress
|
||||
if (line.contains("Time") == true) {
|
||||
long timeRendered = new Date().getTime() - getProcessRender().getStartTime();
|
||||
|
||||
if (progress > 0 && timeRendered > 0) {
|
||||
long linearTimeEstimation = (long) ((100.0 / progress) * timeRendered);
|
||||
long timeRemaining = linearTimeEstimation - timeRendered;
|
||||
Date date = new Date(timeRemaining);
|
||||
|
||||
gui.setRemainingTime(Utils.humanDuration(date));
|
||||
getProcessRender().setRemainingDuration((int) (date.getTime() / 1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (getUpdateRenderingStatusMethod().equals(Job.UPDATE_METHOD_BY_TILE)) {
|
||||
String search = " Tile ";
|
||||
int index = line.lastIndexOf(search);
|
||||
if (index != -1) {
|
||||
String buf = line.substring(index + search.length());
|
||||
String[] parts = buf.split("/");
|
||||
if (parts.length == 2) {
|
||||
try {
|
||||
int current = Integer.parseInt(parts[0]);
|
||||
int total = Integer.parseInt(parts[1]);
|
||||
if (total != 0) {
|
||||
gui.status(String.format("Rendering %s %%", (int) (100.0 * current / total)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
System.out.println("Exception 94: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
gui.status("Rendering");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateProcess() {
|
||||
getProcessRender().update();
|
||||
}
|
||||
|
||||
private Type detectError(String line) {
|
||||
|
||||
if (line.contains("CUDA error: Out of memory")) {
|
||||
// Fra:151 Mem:405.91M (0.00M, Peak 633.81M) | Mem:470.26M, Peak:470.26M | Scene, RenderLayer | Updating Device | Writing constant memory
|
||||
// Fra:151 Mem:405.91M (0.00M, Peak 633.81M) | Mem:470.26M, Peak:470.26M | Scene, RenderLayer | Path Tracing Tile 0/135, Sample 0/200
|
||||
// Fra:151 Mem:405.91M (0.00M, Peak 633.81M) | Mem:470.82M, Peak:470.82M | Scene, RenderLayer | Path Tracing Tile 1/135, Sample 0/200
|
||||
// CUDA error: Out of memory in cuLaunchKernel(cuPathTrace, xblocks , yblocks, 1, xthreads, ythreads, 1, 0, 0, args, 0)
|
||||
// Refer to the Cycles GPU rendering documentation for possible solutions:
|
||||
// http://www.blender.org/manual/render/cycles/gpu_rendering.html
|
||||
// Fra:151 Mem:405.91M (0.00M, Peak 633.81M) | Remaining:09:26.57 | Mem:470.26M, Peak:470.82M | Scene, RenderLayer | Path Tracing Tile 1/135, Sample 200/200
|
||||
// Fra:151 Mem:405.91M (0.00M, Peak 633.81M) | Remaining:00:00.06 | Mem:470.50M, Peak:470.82M | Scene, RenderLayer | Path Tracing Tile 134/135, Sample 0/200
|
||||
// Fra:151 Mem:405.91M (0.00M, Peak 633.81M) | Remaining:00:00.03 | Mem:470.26M, Peak:470.82M | Scene, RenderLayer | Path Tracing Tile 134/135, Sample 200/200
|
||||
// Fra:151 Mem:405.91M (0.00M, Peak 633.81M) | Remaining:00:00.03 | Mem:470.50M, Peak:470.82M | Scene, RenderLayer | Path Tracing Tile 135/135, Sample 0/200
|
||||
// Fra:151 Mem:405.91M (0.00M, Peak 633.81M) | Mem:470.26M, Peak:470.82M | Scene, RenderLayer | Path Tracing Tile 135/135, Sample 200/200
|
||||
// Error: CUDA error: Out of memory in cuLaunchKernel(cuPathTrace, xblocks , yblocks, 1, xthreads, ythreads, 1, 0, 0, args, 0)
|
||||
// Fra:151 Mem:405.91M (0.00M, Peak 633.81M) | Mem:470.26M, Peak:470.82M | Scene, RenderLayer | Cancel | CUDA error: Out of memory in cuLaunchKernel(cuPathTrace, xblocks , yblocks, 1, xthreads, ythreads, 1, 0, 0, args, 0)
|
||||
// Fra:151 Mem:405.89M (0.00M, Peak 633.81M) Sce: Scene Ve:0 Fa:0 La:0
|
||||
// Saved: /tmp/xx/26885_0151.png Time: 00:04.67 (Saving: 00:00.22)
|
||||
// Blender quit
|
||||
return Type.RENDERER_OUT_OF_VIDEO_MEMORY;
|
||||
}
|
||||
else if (line.contains("CUDA error at cuCtxCreate: Out of memory")) {
|
||||
// renderer output
|
||||
// CUDA error at cuCtxCreate: Out of memory
|
||||
// Refer to the Cycles GPU rendering documentation for possible solutions:
|
||||
// http://www.blender.org/manual/render/cycles/gpu_rendering.html
|
||||
// found bundled python: /tmp/aaaa/bbbb/2.78/python
|
||||
// read blend: /tmp/aaaa/bbbb/compute-method.blend
|
||||
// Fra:340 Mem:7.25M (0.00M, Peak 7.25M) | Time:00:00.13 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Synchronizing object | Sun
|
||||
// Fra:340 Mem:7.25M (0.00M, Peak 7.25M) | Time:00:00.13 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Synchronizing object | Plane
|
||||
// Fra:340 Mem:7.25M (0.00M, Peak 7.26M) | Time:00:00.13 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Synchronizing object | Cube
|
||||
// Fra:340 Mem:7.25M (0.00M, Peak 7.26M) | Time:00:00.13 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Synchronizing object | Camera
|
||||
// Fra:340 Mem:7.25M (0.00M, Peak 7.26M) | Time:00:00.13 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Initializing
|
||||
// Fra:340 Mem:7.25M (0.00M, Peak 7.26M) | Time:00:00.13 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Loading render kernels (may take a few minutes the first time)
|
||||
// Fra:340 Mem:7.25M (0.00M, Peak 7.26M) | Time:00:00.13 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Error | CUDA error at cuCtxCreate: Out of memory
|
||||
// Error: CUDA error at cuCtxCreate: Out of memory
|
||||
// Fra:340 Mem:7.25M (0.00M, Peak 7.26M) | Time:00:00.13 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Waiting for render to start
|
||||
// Fra:340 Mem:7.25M (0.00M, Peak 7.26M) | Time:00:00.13 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Cancel | CUDA error at cuCtxCreate: Out of memory
|
||||
// CUDA error: Invalid value in cuCtxDestroy(cuContext)
|
||||
// Fra:340 Mem:7.25M (0.00M, Peak 7.26M) | Time:00:00.13 | Sce: Scene Ve:0 Fa:0 La:0
|
||||
// Blender quit
|
||||
// end of rendering
|
||||
return Type.RENDERER_OUT_OF_VIDEO_MEMORY;
|
||||
}
|
||||
else if (line.contains("CUDA error: Launch exceeded timeout in")) {
|
||||
// Fra:420 Mem:102.41M (0.00M, Peak 215.18M) | Remaining:01:08.44 | Mem:176.04M, Peak:199.23M | Scene, RenderLayer | Path Tracing Tile 2/24, Sample 10/14
|
||||
// Fra:420 Mem:102.41M (0.00M, Peak 215.18M) | Remaining:01:07.08 | Mem:175.48M, Peak:199.23M | Scene, RenderLayer | Path Tracing Tile 2/24, Sample 14/14
|
||||
// Fra:420 Mem:102.41M (0.00M, Peak 215.18M) | Remaining:01:07.11 | Mem:176.04M, Peak:199.23M | Scene, RenderLayer | Path Tracing Tile 3/24, Sample 0/14
|
||||
// CUDA error: Launch exceeded timeout in cuCtxSynchronize()
|
||||
// Refer to the Cycles GPU rendering documentation for possible solutions:
|
||||
// http://www.blender.org/manual/render/cycles/gpu_rendering.html
|
||||
// CUDA error: Launch exceeded timeout in cuMemcpyDtoH((uchar*)mem.data_pointer + offset, (CUdeviceptr)(mem.device_pointer + offset), size)
|
||||
// Fra:420 Mem:102.41M (0.00M, Peak 215.18M) | Remaining:03:04.30 | Mem:176.04M, Peak:199.23M | Scene, RenderLayer | Path Tracing Tile 3/24, Sample 1/14
|
||||
// CUDA error: Launch exceeded timeout in cuMemcpyDtoH((uchar*)mem.data_pointer + offset, (CUdeviceptr)(mem.device_pointer + offset), size)
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// Fra:420 Mem:102.41M (0.00M, Peak 215.18M) | Remaining:02:01.87 | Mem:175.48M, Peak:199.23M | Scene, RenderLayer | Path Tracing Tile 3/24, Sample 14/14
|
||||
// CUDA error: Launch exceeded timeout in cuMemAlloc(&device_pointer, size)
|
||||
// CUDA error: Launch exceeded timeout in cuMemAlloc(&device_pointer, size)
|
||||
// Fra:420 Mem:102.41M (0.00M, Peak 215.18M) | Remaining:02:01.87 | Mem:176.04M, Peak:199.23M | Scene, RenderLayer | Path Tracing Tile 4/24, Sample 0/14
|
||||
// Fra:420 Mem:102.41M (0.00M, Peak 215.18M) | Remaining:01:27.05 | Mem:176.04M, Peak:199.23M | Scene, RenderLayer | Path Tracing Tile 4/24, Sample 14/14
|
||||
// CUDA error: Launch exceeded timeout in cuMemAlloc(&device_pointer, size)
|
||||
// CUDA error: Launch exceeded timeout in cuMemAlloc(&device_pointer, size)
|
||||
// Fra:420 Mem:102.41M (0.00M, Peak 215.18M) | Remaining:00:00.75 | Mem:185.66M, Peak:199.23M | Scene, RenderLayer | Path Tracing Tile 24/24, Sample 0/14
|
||||
// Fra:420 Mem:102.41M (0.00M, Peak 215.18M) | Mem:185.66M, Peak:199.23M | Scene, RenderLayer | Path Tracing Tile 24/24, Sample 14/14
|
||||
// Error: CUDA error: Launch exceeded timeout in cuCtxSynchronize()
|
||||
// Fra:420 Mem:102.41M (0.00M, Peak 215.18M) | Mem:185.66M, Peak:199.23M | Scene, RenderLayer | Cancel | CUDA error: Launch exceeded timeout in cuCtxSynchronize()
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// CUDA error: Launch exceeded timeout in cuMemFree(cuda_device_ptr(mem.device_pointer))
|
||||
// Mem:109.00M (0.00M, Peak 215.18M) | Elapsed 00:00.00 | Tree Compositing Nodetree, Tile 1-6
|
||||
// Mem:109.00M (0.00M, Peak 215.18M) | Elapsed 00:00.00 | Tree Compositing Nodetree, Tile 2-6
|
||||
// Mem:109.00M (0.00M, Peak 215.18M) | Elapsed 00:00.00 | Tree Compositing Nodetree, Tile 3-6
|
||||
// Mem:109.00M (0.00M, Peak 215.18M) | Elapsed 00:00.00 | Tree Compositing Nodetree, Tile 4-6
|
||||
// Mem:109.00M (0.00M, Peak 215.18M) | Elapsed 00:00.00 | Tree Compositing Nodetree, Tile 5-6
|
||||
// Mem:109.00M (0.00M, Peak 215.18M) | Elapsed 00:00.00 | Tree Compositing Nodetree, Tile 6-6
|
||||
// Fra:420 Mem:109.00M (0.00M, Peak 215.18M) Sce: Scene Ve:0 Fa:0 La:0
|
||||
// Saved: /tmp/xx/1234_0420.bmp Time: 00:18.29 (Saving: 00:00.06)
|
||||
// Blender quit
|
||||
// end of rendering
|
||||
return Type.RENDERER_OUT_OF_VIDEO_MEMORY;
|
||||
}
|
||||
else if (line.contains("CUDA error: Invalid value in cuTexRefSetAddress(")) {
|
||||
// Fra:83 Mem:1201.77M (0.00M, Peak 1480.94M) | Time:00:59.30 | Mem:894.21M, Peak:894.21M | color 3, RenderLayer | Updating Mesh | Copying Strands to device
|
||||
// Fra:83 Mem:1316.76M (0.00M, Peak 1480.94M) | Time:01:02.84 | Mem:1010.16M, Peak:1010.16M | color 3, RenderLayer | Cancel | CUDA error: Invalid value in cuTexRefSetAddress(NULL, texref, cuda_device_ptr(mem.device_pointer), size)
|
||||
// Error: CUDA error: Invalid value in cuTexRefSetAddress(NULL, texref, cuda_device_ptr(mem.device_pointer), size)
|
||||
// Fra:83 Mem:136.82M (0.00M, Peak 1480.94M) | Time:01:03.40 | Sce: color 3 Ve:0 Fa:0 La:0
|
||||
// Blender quit
|
||||
// CUDA error: Invalid value in cuTexRefSetAddress(NULL, texref, cuda_device_ptr(mem.device_pointer), size)
|
||||
// Refer to the Cycles GPU rendering documentation for possible solutions:
|
||||
// http://www.blender.org/manual/render/cycles/gpu_rendering.html
|
||||
return Error.Type.RENDERER_OUT_OF_VIDEO_MEMORY;
|
||||
}
|
||||
else if (line.contains("CUDA error: Launch failed in cuCtxSynchronize()")) {
|
||||
// Fra:60 Mem:278.24M (0.00M, Peak 644.01M) | Time:05:08.95 | Remaining:00:03.88 | Mem:210.79M, Peak:210.79M | Scene, W Laser | Path Tracing Tile 16/18, Sample 36/36
|
||||
// Fra:60 Mem:278.24M (0.00M, Peak 644.01M) | Time:05:08.96 | Remaining:00:00.82 | Mem:211.04M, Peak:211.04M | Scene, W Laser | Path Tracing Tile 17/18, Sample 36/36
|
||||
// Fra:60 Mem:278.24M (0.00M, Peak 644.01M) | Time:05:08.96 | Mem:211.11M, Peak:211.11M | Scene, W Laser | Path Tracing Tile 18/18
|
||||
// Error: CUDA error: Launch failed in cuCtxSynchronize(), line 1372
|
||||
// Fra:60 Mem:278.24M (0.00M, Peak 644.01M) | Time:05:08.96 | Mem:211.11M, Peak:211.11M | Scene, W Laser | Cancel | CUDA error: Launch failed in cuCtxSynchronize(), line 1372
|
||||
// Cycles shader graph connect: can only connect closure to closure (Invert.Color to Mix Shader.Closure1).
|
||||
// Cycles shader graph connect: can only connect closure to closure (Mix Shader.Closure to Bump.Normal).
|
||||
// CUDA error: Launch failed in cuCtxSynchronize(), line 1372
|
||||
// Refer to the Cycles GPU rendering documentation for possible solutions:
|
||||
// https://docs.blender.org/manual/en/dev/render/cycles/gpu_rendering.html
|
||||
// CUDA error: Launch failed in cuMemcpyDtoH((uchar*)mem.data_pointer + offset, (CUdeviceptr)(mem.device_pointer + offset), size), line 591
|
||||
// CUDA error: Launch failed in cuMemcpyDtoH((uchar*)mem.data_pointer + offset, (CUdeviceptr)(mem.device_pointer + offset), size), line 591
|
||||
// CUDA error: Launch failed in cuMemFree(cuda_device_ptr(mem.device_pointer)), line 615
|
||||
return Error.Type.RENDERER_OUT_OF_VIDEO_MEMORY;
|
||||
}
|
||||
else if (line.contains("CUDA error: Illegal address in cuCtxSynchronize()")) {
|
||||
// Fra:124 Mem:434.77M (0.00M, Peak 435.34M) | Time:25:50.81 | Remaining:01:10:05.16 | Mem:175.14M, Peak:265.96M | Scene, RenderLayer | Path Tracing Tile 34/135, Sample 800/800, Denoised 17 tiles
|
||||
// Fra:124 Mem:432.71M (0.00M, Peak 435.34M) | Time:25:50.81 | Remaining:01:10:04.95 | Mem:264.84M, Peak:266.90M | Scene, RenderLayer | Path Tracing Tile 34/135, Sample 800/800, Denoised 18 tiles
|
||||
// Fra:124 Mem:434.77M (0.00M, Peak 435.34M) | Time:25:50.82 | Remaining:01:07:20.83 | Mem:266.90M, Peak:266.90M | Scene, RenderLayer | Path Tracing Tile 35/135, Sample 800/800, Denoised 18 tiles
|
||||
// Fra:124 Mem:432.71M (0.00M, Peak 435.34M) | Time:25:50.82 | Remaining:01:07:20.63 | Mem:356.60M, Peak:358.67M | Scene, RenderLayer | Path Tracing Tile 35/135, Sample 800/800, Denoised 19 tiles
|
||||
// Fra:124 Mem:434.77M (0.00M, Peak 435.34M) | Time:25:50.82 | Remaining:01:04:45.63 | Mem:358.67M, Peak:358.67M | Scene, RenderLayer | Path Tracing Tile 36/135, Sample 800/800, Denoised 19 tiles
|
||||
// Fra:124 Mem:432.71M (0.00M, Peak 435.34M) | Time:25:50.82 | Remaining:01:04:45.45 | Mem:448.37M, Peak:450.43M | Scene, RenderLayer | Path Tracing Tile 36/135, Sample 800/800, Denoised 20 tiles
|
||||
// Fra:124 Mem:434.77M (0.00M, Peak 435.34M) | Time:25:50.83 | Remaining:01:02:18.83 | Mem:450.43M, Peak:450.43M | Scene, RenderLayer | Path Tracing Tile 37/135, Sample 800/800, Denoised 20 tiles
|
||||
// CUDA error: Illegal address in cuCtxSynchronize(), line 1372
|
||||
// Refer to the Cycles GPU rendering documentation for possible solutions:
|
||||
return Error.Type.RENDERER_OUT_OF_VIDEO_MEMORY;
|
||||
}
|
||||
else if (line.contains("Error: Out of memory in CUDA")) {
|
||||
// 15-03 21:13:45 (debug) Fra:27 Mem:8441.68M (Peak 9675.81M) | Time:00:16.27 | Mem:8411.20M, Peak:8411.20M | Scene, View Layer | Loading render kernels (may take a few minutes the first time)
|
||||
// 15-03 21:13:45 (debug) Out of memory in CUDA queue enqueue (integrator_shade_surface)
|
||||
// 15-03 21:13:45 (debug) Refer to the Cycles GPU rendering documentation for possible solutions:
|
||||
// 15-03 21:13:45 (debug) https://docs.blender.org/manual/en/latest/render/cycles/gpu_rendering.html
|
||||
// 15-03 21:13:45 (debug) Fra:27 Mem:8441.68M (Peak 9675.81M) | Time:00:16.38 | Mem:8411.27M, Peak:8411.27M | Scene, View Layer | Updating Scene
|
||||
// 15-03 21:13:45 (debug) Fra:27 Mem:8441.68M (Peak 9675.81M) | Time:00:16.38 | Mem:8411.27M, Peak:8411.27M | Scene, View Layer | Updating Shaders
|
||||
// 15-03 21:13:45 (debug) Fra:27 Mem:8447.08M (Peak 9675.81M) | Time:00:16.63 | Mem:8763.00M, Peak:8763.00M | Scene, View Layer | Out of memory in CUDA queue enqueue (integrator_shade_surface)
|
||||
// 15-03 21:13:45 (debug) Error: Out of memory in CUDA queue enqueue (integrator_shade_surface)
|
||||
// 15-03 21:13:46 (debug) Blender quit
|
||||
return Error.Type.RENDERER_OUT_OF_VIDEO_MEMORY;
|
||||
}
|
||||
else if (line.contains("Error: System is out of GPU memory")) {
|
||||
// 16-03 18:37:37 (debug) Fra:192 Mem:15826.70M (Peak 15831.18M) | Time:02:21.86 | Mem:6622.87M, Peak:6625.35M | Scene, ViewLayer | Loading denoising kernels (may take a few minutes the first time)
|
||||
// 16-03 18:37:37 (debug) System is out of GPU memory
|
||||
// 16-03 18:37:37 (debug) Refer to the Cycles GPU rendering documentation for possible solutions:
|
||||
// 16-03 18:37:37 (debug) https://docs.blender.org/manual/en/latest/render/cycles/gpu_rendering.html
|
||||
// 16-03 18:37:37 (debug) System is out of GPU memory
|
||||
// 16-03 18:37:38 (debug) Fra:192 Mem:15831.01M (Peak 15831.18M) | Time:02:23.11 | Mem:7017.46M, Peak:7017.46M | Scene, ViewLayer | System is out of GPU memory
|
||||
// 16-03 18:37:38 (debug) Error: System is out of GPU memory
|
||||
// 16-03 18:37:40 (debug) Blender quit
|
||||
// 16-03 18:37:40 (debug) end of rendering
|
||||
return Error.Type.RENDERER_OUT_OF_VIDEO_MEMORY;
|
||||
}
|
||||
else if (line.contains("CUDA device supported only with compute capability")) {
|
||||
// found bundled python: /tmp/xx/2.73/python
|
||||
// read blend: /tmp/xx/compute-method.blend
|
||||
// Fra:340 Mem:7.64M (0.00M, Peak 8.23M) | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Synchronizing object | Sun
|
||||
// Fra:340 Mem:7.64M (0.00M, Peak 8.23M) | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Synchronizing object | Plane
|
||||
// Fra:340 Mem:7.64M (0.00M, Peak 8.23M) | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Synchronizing object | Cube
|
||||
// Fra:340 Mem:7.64M (0.00M, Peak 8.23M) | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Synchronizing object | Camera
|
||||
// Fra:340 Mem:7.64M (0.00M, Peak 8.23M) | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Initializing
|
||||
// Fra:340 Mem:7.64M (0.00M, Peak 8.23M) | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Loading render kernels (may take a few minutes the first time)
|
||||
// CUDA device supported only with compute capability 2.0 or up, found 1.2.
|
||||
// Refer to the Cycles GPU rendering documentation for possible solutions:
|
||||
// http://www.blender.org/manual/render/cycles/gpu_rendering.html
|
||||
// Fra:340 Mem:7.64M (0.00M, Peak 8.23M) | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Error | CUDA device supported only with compute capability 2.0 or up, found 1.2.
|
||||
// Error: CUDA device supported only with compute capability 2.0 or up, found 1.2.
|
||||
// Fra:340 Mem:7.64M (0.00M, Peak 8.23M) | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Waiting for render to start
|
||||
// Fra:340 Mem:7.64M (0.00M, Peak 8.23M) | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Cancel | CUDA device supported only with compute capability 2.0 or up, found 1.2.
|
||||
// Fra:340 Mem:7.64M (0.00M, Peak 8.23M) Sce: Scene Ve:0 Fa:0 La:0
|
||||
// Saved: /tmp/xx/0_0340.png Time: 00:00.12 (Saving: 00:00.03)
|
||||
// Blender quit
|
||||
return Type.GPU_NOT_SUPPORTED;
|
||||
}
|
||||
else if (line.contains("terminate called after throwing an instance of 'boost::filesystem::filesystem_error'")) {
|
||||
// Fra:2103 Mem:29.54M (0.00M, Peak 29.54M) | Time:00:00.24 | Mem:1.64M, Peak:1.64M | Scene, RenderLayer | Updating Mesh | Computing attributes
|
||||
// Fra:2103 Mem:29.54M (0.00M, Peak 29.54M) | Time:00:00.24 | Mem:1.64M, Peak:1.64M | Scene, RenderLayer | Updating Mesh | Copying Attributes to device
|
||||
// Fra:2103 Mem:29.54M (0.00M, Peak 29.54M) | Time:00:00.24 | Mem:1.97M, Peak:1.97M | Scene, RenderLayer | Updating Scene BVH | Building
|
||||
// Fra:2103 Mem:29.54M (0.00M, Peak 29.54M) | Time:00:00.24 | Mem:1.97M, Peak:1.97M | Scene, RenderLayer | Updating Scene BVH | Building BVH
|
||||
// Fra:2103 Mem:29.54M (0.00M, Peak 29.54M) | Time:00:00.24 | Mem:1.97M, Peak:1.97M | Scene, RenderLayer | Updating Scene BVH | Looking in BVH cache
|
||||
// Fra:2103 Mem:29.54M (0.00M, Peak 29.54M) | Time:00:00.27 | Mem:1.97M, Peak:1.97M | Scene, RenderLayer | Updating Scene BVH | Packing BVH triangles and strands
|
||||
// Fra:2103 Mem:29.54M (0.00M, Peak 29.54M) | Time:00:00.27 | Mem:1.97M, Peak:1.97M | Scene, RenderLayer | Updating Scene BVH | Packing BVH nodes
|
||||
// Fra:2103 Mem:29.54M (0.00M, Peak 29.54M) | Time:00:00.27 | Mem:1.97M, Peak:1.97M | Scene, RenderLayer | Updating Scene BVH | Writing BVH cache
|
||||
// terminate called after throwing an instance of 'boost::filesystem::filesystem_error'
|
||||
// what(): boost::filesystem::create_directory: Permission denied: "/var/local/cache"
|
||||
return Error.Type.NOOUTPUTFILE;
|
||||
}
|
||||
else if (line.contains("terminate called after throwing an instance of 'std::bad_alloc'")) {
|
||||
// Fra:80 Mem:1333.02M (0.00M, Peak 1651.23M) | Mem:780.37M, Peak:780.37M | Scene, RenderLayer | Updating Mesh BVH Plane.083 171/2 | Building BVH
|
||||
// Fra:80 Mem:1333.02M (0.00M, Peak 1651.23M) | Mem:780.37M, Peak:780.37M | Scene, RenderLayer | Updating Mesh BVH Mesh 172/2 | Building BVH
|
||||
// Fra:80 Mem:1333.02M (0.00M, Peak 1651.23M) | Mem:780.37M, Peak:780.37M | Scene, RenderLayer | Updating Mesh BVH Mesh 172/2 | Packing BVH triangles and strands
|
||||
// Fra:80 Mem:1333.02M (0.00M, Peak 1651.23M) | Mem:780.37M, Peak:780.37M | Scene, RenderLayer | Updating Mesh BVH Mesh 172/2 | Packing BVH nodes
|
||||
// Fra:80 Mem:1333.02M (0.00M, Peak 1651.23M) | Mem:780.37M, Peak:780.37M | Scene, RenderLayer | Updating Scene BVH | Building
|
||||
// Fra:80 Mem:1333.02M (0.00M, Peak 1651.23M) | Mem:780.37M, Peak:780.37M | Scene, RenderLayer | Updating Scene BVH | Building BVH
|
||||
// terminate called after throwing an instance of 'std::bad_alloc'
|
||||
// what(): std::bad_alloc
|
||||
return Error.Type.RENDERER_OUT_OF_MEMORY;
|
||||
}
|
||||
else if (line.contains("what(): std::bad_alloc")) {
|
||||
// Fra:7 Mem:1247.01M (0.00M, Peak 1247.01M) | Time:00:28.84 | Mem:207.63M, Peak:207.63M | Scene, RenderLayer | Updating Scene BVH | Building BVH 93%, duplicates 0%terminate called recursively
|
||||
// terminate called after throwing an instance of 'St9bad_alloc'
|
||||
// what(): std::bad_alloc
|
||||
// scandir: Cannot allocate memory
|
||||
return Error.Type.RENDERER_OUT_OF_MEMORY;
|
||||
}
|
||||
else if (line.contains("EXCEPTION_ACCESS_VIOLATION")) {
|
||||
// Fra:638 Mem:342.17M (63.28M, Peak 735.33M) | Time:00:07.65 | Remaining:02:38.28 | Mem:246.91M, Peak:262.16M | scene_top_01_90, chip_top_view_scene_01 | Path Tracing Tile 57/2040, Denoised 0 tiles
|
||||
// Fra:638 Mem:342.32M (63.28M, Peak 735.33M) | Time:00:07.70 | Remaining:02:38.20 | Mem:247.05M, Peak:262.16M | scene_top_01_90, chip_top_view_scene_01 | Path Tracing Tile 58/2040, Denoised 0 tiles
|
||||
// Error: EXCEPTION_ACCESS_VIOLATION
|
||||
return Error.Type.RENDERER_CRASHED;
|
||||
}
|
||||
else if (line.contains("Fatal Python error:")) {
|
||||
// Fatal Python error: Py_Initialize: unable to load the file system codec
|
||||
// ImportError: No module named 'encodings'
|
||||
// Current thread 0x0000388c (most recent call first):
|
||||
return Error.Type.RENDERER_CRASHED_PYTHON_ERROR;
|
||||
}
|
||||
else if (line.contains("Calloc returns null")) {
|
||||
// Fra:1 Mem:976.60M (0.00M, Peak 1000.54M) | Time:00:01.34 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Synchronizing object | Left
|
||||
// Calloc returns null: len=7186416 in CDMLoopUV, total 2145859048
|
||||
// Calloc returns null: len=7186416 in CDMLoopUV, total 2145859048
|
||||
// Malloc returns null: len=3190672 in CDMTexPoly, total 2149293176
|
||||
// Writing: /home/user/.sheepit/LEFT packed.crash.txt
|
||||
return Error.Type.RENDERER_OUT_OF_MEMORY;
|
||||
}
|
||||
else if (line.contains("Malloc returns null")) {
|
||||
// Fra:1 Mem:976.60M (0.00M, Peak 1000.54M) | Time:00:01.34 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Synchronizing object | Left
|
||||
// Calloc returns null: len=7186416 in CDMLoopUV, total 2145859048
|
||||
// Calloc returns null: len=7186416 in CDMLoopUV, total 2145859048
|
||||
// Malloc returns null: len=3190672 in CDMTexPoly, total 2149293176
|
||||
// Writing: /home/user/.sheepit/LEFT packed.crash.txt
|
||||
return Error.Type.RENDERER_OUT_OF_MEMORY;
|
||||
}
|
||||
else if (line.contains("CUDA kernel compilation failed")) {
|
||||
// Fra:1 Mem:200.70M (0.00M, Peak 378.15M) | Time:00:01.02 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Synchronizing object | Sun.001
|
||||
// Fra:1 Mem:200.70M (0.00M, Peak 378.15M) | Time:00:01.02 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Synchronizing object | Sun.002
|
||||
// Fra:1 Mem:200.70M (0.00M, Peak 378.15M) | Time:00:01.02 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Initializing
|
||||
// Fra:1 Mem:200.70M (0.00M, Peak 378.15M) | Time:00:01.02 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Loading render kernels (may take a few minutes the first time)
|
||||
// nvcc fatal : Value 'sm_61' is not defined for option 'gpu-architecture'
|
||||
// CUDA kernel compilation failed, see console for details.
|
||||
// Refer to the Cycles GPU rendering documentation for possible solutions:
|
||||
// http://www.blender.org/manual/render/cycles/gpu_rendering.html
|
||||
// Compiling CUDA kernel ...
|
||||
// "nvcc" -arch=sm_61 -m64 --cubin "/tmp/cache/c36db40aa5e59f5ea4ff139180353dbc/2.77/scripts/addons/cycles/kernel/kernels/cuda/kernel.cu" -o "/tmp/cache/cycles_kernel_sm61_079195D400571E023CC499D037AB6EE5.cubin" --ptxas-options="-v" --use_fast_math -I"/tmp/cache/c36db40aa5e59f5ea4ff139180353dbc/2.77/scripts/addons/cycles/kernel" -DNVCC -D__KERNEL_CUDA_VERSION__=75
|
||||
// Fra:1 Mem:200.70M (0.00M, Peak 378.15M) | Time:00:01.08 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Error | CUDA kernel compilation failed, see console for details.
|
||||
// Error: CUDA kernel compilation failed, see console for details.
|
||||
// Fra:1 Mem:200.70M (0.00M, Peak 378.15M) | Time:00:01.08 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Waiting for render to start
|
||||
// Fra:1 Mem:200.70M (0.00M, Peak 378.15M) | Time:00:01.08 | Mem:0.00M, Peak:0.00M | Scene, RenderLayer | Cancel | CUDA kernel compilation failed, see console for details.
|
||||
// Fra:1 Mem:147.74M (0.00M, Peak 378.15M) | Time:00:01.12 | Sce: Scene Ve:0 Fa:0 La:0
|
||||
// Saved: '/tmp/cache/8_0001.png'
|
||||
return Error.Type.GPU_NOT_SUPPORTED;
|
||||
}
|
||||
else if (line.contains("Engine 'CYCLES' not available for scene") || line.contains("Engine 'BLENDER_EEVEE' not available for scene")) {
|
||||
return Error.Type.ENGINE_NOT_AVAILABLE;
|
||||
}
|
||||
else if (line.contains("Warning: Cycles is not enabled!")) {
|
||||
return Error.Type.ENGINE_NOT_AVAILABLE;
|
||||
}
|
||||
else if (line.contains("OpenImageDenoise error: SSE4.1 support is required at minimum")) {
|
||||
// denoising capability detection
|
||||
// Fra:201 Mem:8.89M (Peak 10.09M) | Time:00:00.04 | Mem:0.27M, Peak:0.27M | Scene, View Layer | Updating Device | Writing constant memory
|
||||
// Fra:201 Mem:8.89M (Peak 10.09M) | Time:00:00.04 | Mem:0.27M, Peak:0.27M | Scene, View Layer | Loading denoising kernels (may take a few minutes the first time)
|
||||
// Fra:201 Mem:8.89M (Peak 10.09M) | Time:00:00.04 | Mem:0.27M, Peak:0.27M | Scene, View Layer | Sample 0/16
|
||||
// Fra:201 Mem:127.55M (Peak 127.55M) | Time:00:00.65 | Remaining:00:09.13 | Mem:118.92M, Peak:118.92M | Scene, View Layer | Sample 1/16
|
||||
// E0308 1719 31449 denoiser_oidn.cpp:197] OpenImageDenoise error: SSE4.1 support is required at minimum
|
||||
// Fra:201 Mem:198.74M (Peak 301.58M) | Time:00:08.74 | Mem:118.92M, Peak:118.92M | Scene, View Layer | Sample 16/16
|
||||
// Rendering done
|
||||
// Fra:201 Mem:198.74M (Peak 301.58M) | Time:00:08.74 | Mem:118.92M, Peak:118.92M | Scene, View Layer | Finished
|
||||
// Saved: '/tmp/5/sheepit/1541_0201.png'
|
||||
// Time: 00:09.30 (Saving: 00:00.55)
|
||||
// Blender quit
|
||||
return Error.Type.CPU_NOT_SUPPORTED;
|
||||
}
|
||||
else if (line.contains("Error: File format is not supported")) {
|
||||
//09-03 23:36:03 (debug) Blender 2.83.19 (hash 86c526d2c733 built 2022-02-02 00:44:40)
|
||||
//09-03 23:36:03 (debug) found bundled python: C:\Users\Username\Documents\sheepit\sheepit\f1f9e587201b428c9416faf614b677bf\2.83\python
|
||||
//09-03 23:36:03 (debug) Error: File format is not supported in file 'C:\Users\Username\Documents\sheepit\sheepit\686c6bdbf752790b28c1b21180aa5db5\correndo4.blend'
|
||||
//09-03 23:36:03 (debug) Blender quit
|
||||
//09-03 23:36:03 (debug) end of rendering
|
||||
return Error.Type.RENDERER_CRASHED;
|
||||
}
|
||||
return Type.OK;
|
||||
}
|
||||
|
||||
public static class renderStartedObservable extends Observable {
|
||||
|
||||
@Getter private boolean isStarted;
|
||||
|
||||
public renderStartedObservable(Observer observer) {
|
||||
super();
|
||||
addObserver(observer);
|
||||
}
|
||||
|
||||
public void doNotifyIsStarted() {
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
isStarted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
130
src/main/java/com/sheepit/client/Log.java
Normal file
130
src/main/java/com/sheepit/client/Log.java
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class Log {
|
||||
private static Log instance = null;
|
||||
|
||||
private Map<Integer, ArrayList<String>> checkpoints = new HashMap<Integer, ArrayList<String>>();
|
||||
private int lastCheckPoint;
|
||||
private DateFormat dateFormat;
|
||||
|
||||
private boolean printStdOut;
|
||||
|
||||
private Log(boolean print_) {
|
||||
this.printStdOut = print_;
|
||||
this.lastCheckPoint = 0;
|
||||
this.checkpoints.put(this.lastCheckPoint, new ArrayList<String>());
|
||||
this.dateFormat = new SimpleDateFormat("dd-MM HH:mm:ss");
|
||||
}
|
||||
|
||||
public void debug(String msg_) {
|
||||
this.debug(-1, msg_);
|
||||
}
|
||||
|
||||
public void debug(int point_, String msg_) {
|
||||
this.append(point_, "debug", msg_);
|
||||
}
|
||||
|
||||
public void info(String msg_) {
|
||||
this.info(-1, msg_);
|
||||
}
|
||||
|
||||
public void info(int point_, String msg_) {
|
||||
this.append(point_, "info", msg_);
|
||||
}
|
||||
|
||||
public void error(String msg_) {
|
||||
this.error(-1, msg_);
|
||||
}
|
||||
|
||||
public void error(int point_, String msg_) {
|
||||
this.append(point_, "error", msg_);
|
||||
}
|
||||
|
||||
private synchronized void append(int point_, String level_, String msg_) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
int checkpointToWrite = (point_ > 0 ? point_ : this.lastCheckPoint);
|
||||
|
||||
if (msg_.equals("") == false) {
|
||||
line = this.dateFormat.format(new java.util.Date()) + " (" + level_ + ") " + msg_;
|
||||
if (this.checkpoints.containsKey(checkpointToWrite) && this.checkpoints.get(checkpointToWrite) != null) {
|
||||
this.checkpoints.get(checkpointToWrite).add(line);
|
||||
}
|
||||
if (this.printStdOut) {
|
||||
System.out.println(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Nothing to do here. Just allow the thread to continue
|
||||
}
|
||||
}
|
||||
|
||||
public int newCheckPoint() {
|
||||
int time = (int) (new Date().getTime());
|
||||
this.checkpoints.put(time, new ArrayList<String>());
|
||||
this.lastCheckPoint = time;
|
||||
return this.lastCheckPoint;
|
||||
}
|
||||
|
||||
public Optional<ArrayList<String>> getForCheckPoint(int point_) {
|
||||
return Optional.ofNullable(this.checkpoints.get(point_));
|
||||
}
|
||||
|
||||
public void removeCheckPoint(int point_) {
|
||||
try {
|
||||
this.checkpoints.remove(point_);
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized Log getInstance(Configuration config) {
|
||||
if (instance == null) {
|
||||
boolean print = false;
|
||||
if (config != null) {
|
||||
print = config.isPrintLog();
|
||||
}
|
||||
instance = new Log(print);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static synchronized void printCheckPoint(int point_) {
|
||||
Log log = Log.getInstance(null);
|
||||
Optional<ArrayList<String>> logs = log.getForCheckPoint(point_);
|
||||
if (logs.isPresent()) {
|
||||
for (String alog : logs.get()) {
|
||||
System.out.println(alog);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/main/java/com/sheepit/client/Option.java
Normal file
19
src/main/java/com/sheepit/client/Option.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.sheepit.client;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class Option<T> {
|
||||
private T value;
|
||||
private boolean isLaunchCommand;
|
||||
private String launchFlag;
|
||||
|
||||
public Option(T value, @NotNull String launchFlag) {
|
||||
this.value = value;
|
||||
this.launchFlag = launchFlag;
|
||||
this.isLaunchCommand = false;
|
||||
}
|
||||
}
|
||||
76
src/main/java/com/sheepit/client/Pair.java
Normal file
76
src/main/java/com/sheepit/client/Pair.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.sheepit.client;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Container to ease passing around a pair of two objects. This object provides a sensible
|
||||
* implementation of equals(), returning true if equals() is true on each of the contained
|
||||
* objects.
|
||||
*/
|
||||
public class Pair<F, S> {
|
||||
public final F first;
|
||||
public final S second;
|
||||
|
||||
/**
|
||||
* Constructor for a Pair.
|
||||
*
|
||||
* @param first the first object in the Pair
|
||||
* @param second the second object in the pair
|
||||
*/
|
||||
public Pair(F first, S second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the two objects for equality by delegating to their respective
|
||||
* {@link Object#equals(Object)} methods.
|
||||
*
|
||||
* @param o the {@link Pair} to which this one is to be checked for equality
|
||||
* @return true if the underlying objects of the Pair are both considered
|
||||
* equal
|
||||
*/
|
||||
@Override public boolean equals(Object o) {
|
||||
if (!(o instanceof Pair)) {
|
||||
return false;
|
||||
}
|
||||
Pair<?, ?> p = (Pair<?, ?>) o;
|
||||
return Objects.equals(p.first, first) && Objects.equals(p.second, second);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a hash code using the hash codes of the underlying objects
|
||||
*
|
||||
* @return a hashcode of the Pair
|
||||
*/
|
||||
@Override public int hashCode() {
|
||||
return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for creating an appropriately typed pair.
|
||||
*
|
||||
* @param a the first object in the Pair
|
||||
* @param b the second object in the pair
|
||||
* @return a Pair that is templatized with the types of a and b
|
||||
*/
|
||||
public static <A, B> Pair<A, B> create(A a, B b) {
|
||||
return new Pair<A, B>(a, b);
|
||||
}
|
||||
}
|
||||
114
src/main/java/com/sheepit/client/RenderProcess.java
Normal file
114
src/main/java/com/sheepit/client/RenderProcess.java
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import oshi.software.os.OSProcess;
|
||||
|
||||
@Data public class RenderProcess {
|
||||
private long startTime;
|
||||
private long endTime;
|
||||
private int remainingDuration; // in seconds
|
||||
private AtomicLong memoryUsed; // in kB
|
||||
private long peakMemoryUsed; // in kB
|
||||
private int coresUsed;
|
||||
private Process process;
|
||||
private OSProcess osProcess;
|
||||
private Log log;
|
||||
|
||||
public RenderProcess(Log _log) {
|
||||
process = null;
|
||||
osProcess = null;
|
||||
startTime = -1;
|
||||
endTime = -1;
|
||||
memoryUsed = new AtomicLong(0);
|
||||
peakMemoryUsed = 0;
|
||||
coresUsed = 0;
|
||||
remainingDuration = 0;
|
||||
log = _log;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
OSProcess osp = osProcess; // Shallow copy to try to not run into a race condition via being nulled
|
||||
try {
|
||||
if (osp != null && osp.updateAttributes()){ // We enter if updateAttributes() was successful
|
||||
long mem = osp.getResidentSetSize() / 1024; // Avoid multiple ram usage calls, because again, they might differ
|
||||
if (mem != 0){
|
||||
memoryUsed.set(mem);
|
||||
if (peakMemoryUsed < mem) {
|
||||
peakMemoryUsed = mem;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException ex) { // We are racing the system itself, we can't avoid catching NPE's
|
||||
log.debug("RenderProcess::Handled process becoming unavailable mid-update");
|
||||
osProcess = null;
|
||||
memoryUsed.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return duration in seconds
|
||||
*/
|
||||
public int getDuration() {
|
||||
if (startTime != -1 && endTime != -1) {
|
||||
return (int) ((endTime - startTime) / 1000);
|
||||
}
|
||||
else if (startTime != -1) {
|
||||
return (int) ((new Date().getTime() - startTime) / 1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
endTime = new Date().getTime();
|
||||
osProcess = null;
|
||||
process = null;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
startTime = new Date().getTime();
|
||||
}
|
||||
|
||||
public int exitValue() {
|
||||
int value = 0;
|
||||
if (process == null) {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
value = process.exitValue();
|
||||
}
|
||||
catch (IllegalThreadStateException e) {
|
||||
// the process is not finished yet
|
||||
value = 0;
|
||||
}
|
||||
catch (Exception e) {
|
||||
// actually is for java.io.IOException: GetExitCodeProcess error=6, The handle is invalid
|
||||
// it was not declared throwable
|
||||
|
||||
// the process is not finished yet
|
||||
value = 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
734
src/main/java/com/sheepit/client/Server.java
Normal file
734
src/main/java/com/sheepit/client/Server.java
Normal file
@@ -0,0 +1,734 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 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;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.file.Files;
|
||||
import java.net.*;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.sheepit.client.datamodel.SpeedTestTarget;
|
||||
import com.sheepit.client.datamodel.SpeedTestResult;
|
||||
import com.sheepit.client.datamodel.SpeedTestTargetResult;
|
||||
import com.sheepit.client.hardware.hwid.HWIdentifier;
|
||||
import com.sheepit.client.os.Windows;
|
||||
import lombok.Getter;
|
||||
import org.simpleframework.xml.core.Persister;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.JavaNetCookieJar;
|
||||
|
||||
import com.sheepit.client.hardware.cpu.CPU;
|
||||
import com.sheepit.client.Configuration.ComputeType;
|
||||
import com.sheepit.client.Error.ServerCode;
|
||||
import com.sheepit.client.datamodel.CacheFileMD5;
|
||||
import com.sheepit.client.datamodel.FileMD5;
|
||||
import com.sheepit.client.datamodel.HeartBeatInfos;
|
||||
import com.sheepit.client.datamodel.JobInfos;
|
||||
import com.sheepit.client.datamodel.JobValidation;
|
||||
import com.sheepit.client.datamodel.RequestEndPoint;
|
||||
import com.sheepit.client.datamodel.ServerConfig;
|
||||
import com.sheepit.client.exception.FermeException;
|
||||
import com.sheepit.client.exception.FermeExceptionBadResponseFromServer;
|
||||
import com.sheepit.client.exception.FermeExceptionNoRendererAvailable;
|
||||
import com.sheepit.client.exception.FermeExceptionNoRightToRender;
|
||||
import com.sheepit.client.exception.FermeExceptionNoSession;
|
||||
import com.sheepit.client.exception.FermeExceptionNoSpaceLeftOnDevice;
|
||||
import com.sheepit.client.exception.FermeExceptionPathInvalid;
|
||||
import com.sheepit.client.exception.FermeExceptionNoWritePermission;
|
||||
import com.sheepit.client.exception.FermeExceptionServerInMaintenance;
|
||||
import com.sheepit.client.exception.FermeExceptionServerOverloaded;
|
||||
import com.sheepit.client.exception.FermeExceptionSessionDisabled;
|
||||
import com.sheepit.client.exception.FermeServerDown;
|
||||
import com.sheepit.client.os.OS;
|
||||
|
||||
|
||||
public class Server extends Thread {
|
||||
private static final int NUMBER_OF_SPEEDTEST_RESULTS = 3;
|
||||
|
||||
final private String HTTP_USER_AGENT = "Java/" + System.getProperty("java.version");
|
||||
private String base_url;
|
||||
private final OkHttpClient httpClient;
|
||||
|
||||
@Getter private ServerConfig serverConfig;
|
||||
|
||||
private Configuration user_config;
|
||||
private Client client;
|
||||
private Log log;
|
||||
private long lastRequestTime;
|
||||
private int keepmealive_duration; // time in ms
|
||||
|
||||
private TransferStats dlStats = new TransferStats();
|
||||
private TransferStats ulStats = new TransferStats();
|
||||
|
||||
public Server(String url_, Configuration user_config_, Client client_) {
|
||||
super();
|
||||
this.base_url = url_;
|
||||
this.user_config = user_config_;
|
||||
this.client = client_;
|
||||
this.log = Log.getInstance(this.user_config);
|
||||
this.lastRequestTime = 0;
|
||||
this.keepmealive_duration = 15 * 60 * 1000; // default 15min
|
||||
|
||||
// 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
|
||||
// wastes resources on idle pools.
|
||||
this.httpClient = getOkHttpClient();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
this.stayAlive();
|
||||
}
|
||||
|
||||
public void stayAlive() {
|
||||
while (true) {
|
||||
long current_time = new Date().getTime();
|
||||
if ((current_time - this.lastRequestTime) > this.keepmealive_duration) {
|
||||
try {
|
||||
HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(this.getPage("keepmealive"))).newBuilder();
|
||||
|
||||
if (this.client != null && this.client.getRenderingJob() != null) {
|
||||
Job job = this.client.getRenderingJob();
|
||||
|
||||
urlBuilder.addQueryParameter("frame", job.getFrameNumber()).addQueryParameter("job", job.getId());
|
||||
|
||||
RenderProcess process = job.getProcessRender();
|
||||
if (process != null) {
|
||||
urlBuilder.addQueryParameter("rendertime", String.valueOf(process.getDuration()))
|
||||
.addQueryParameter("remainingtime", String.valueOf(process.getRemainingDuration()));
|
||||
}
|
||||
}
|
||||
|
||||
Response response = this.HTTPRequest(urlBuilder);
|
||||
|
||||
if (response.code() == HttpURLConnection.HTTP_OK && response.body().contentType().toString().startsWith("text/xml")) {
|
||||
String in = response.body().string();
|
||||
|
||||
try {
|
||||
HeartBeatInfos heartBeatInfos = new Persister().read(HeartBeatInfos.class, in);
|
||||
ServerCode serverCode = ServerCode.fromInt(heartBeatInfos.getStatus());
|
||||
if (serverCode == ServerCode.KEEPMEALIVE_STOP_RENDERING) {
|
||||
this.log.debug("Server::stayAlive server asked to kill local render process");
|
||||
// kill the current process, it will generate an error but it's okay
|
||||
if (this.client != null && this.client.getRenderingJob() != null) {
|
||||
this.client.getRenderingJob().setServerBlockJob(true);
|
||||
|
||||
if (this.client.getRenderingJob().getProcessRender().getProcess() != null) {
|
||||
this.client.getRenderingJob().setAskForRendererKill(true);
|
||||
OS.getOS().kill(this.client.getRenderingJob().getProcessRender().getProcess());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) { // for the read
|
||||
this.log.debug("Server::stayAlive Exception " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NoRouteToHostException e) {
|
||||
this.log.debug("Server::stayAlive can not connect to server");
|
||||
}
|
||||
catch (IOException e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
this.log.debug("Server::stayAlive IOException " + e + " stacktrace: " + sw.toString());
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(60 * 1000); // 1min
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
catch (Exception e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
this.log.debug("Server::stayAlive Exception " + e + " stacktrace: " + sw.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("Server (base_url '%s', user_config %s", this.base_url, this.user_config);
|
||||
}
|
||||
|
||||
public Error.Type getConfiguration() {
|
||||
OS os = OS.getOS();
|
||||
String publickey = null;
|
||||
try {
|
||||
HttpUrl.Builder remoteURL = Objects.requireNonNull(HttpUrl.parse(this.base_url + "/server/config.php")).newBuilder();
|
||||
FormBody formBody = new FormBody.Builder()
|
||||
.add("login", user_config.getLogin())
|
||||
.add("password", user_config.getPassword())
|
||||
.add("cpu_family", os.getCPU().family())
|
||||
.add("cpu_model", os.getCPU().model())
|
||||
.add("cpu_model_name", os.getCPU().name())
|
||||
.add("cpu_cores", String.valueOf(user_config.getNbCores() == -1 ? os.getCPU().cores() : Math.max(CPU.MIN_CORES, user_config.getNbCores())))
|
||||
.add("os", os.name())
|
||||
.add("os_version", os.getVersion())
|
||||
.add("ram", String.valueOf(os.getTotalMemory()))
|
||||
.add("bits", os.getCPU().arch())
|
||||
.add("version", Configuration.jarVersion)
|
||||
.add("hostname", user_config.getHostname())
|
||||
.add("ui", client.getGui().getClass().getSimpleName())
|
||||
.add("extras", user_config.getExtras())
|
||||
.add("headless", java.awt.GraphicsEnvironment.isHeadless() ? "1" : (user_config.isHeadless() ? "1" : "0"))
|
||||
.add("hwid", new HWIdentifier(log).getHardwareHash())
|
||||
.build();
|
||||
|
||||
this.log.debug("Server::getConfiguration url " + remoteURL.build().toString());
|
||||
|
||||
Response response = this.HTTPRequest(remoteURL, formBody);
|
||||
int r = response.code();
|
||||
String contentType = response.body().contentType().toString();
|
||||
|
||||
if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/xml")) {
|
||||
String in = response.body().string();
|
||||
serverConfig = new Persister().read(ServerConfig.class, in);
|
||||
|
||||
if (ServerCode.fromInt(serverConfig.getStatus()) != ServerCode.OK) {
|
||||
return Error.ServerCodeToType(ServerCode.fromInt(serverConfig.getStatus()));
|
||||
}
|
||||
|
||||
publickey = serverConfig.getPublickey();
|
||||
if (publickey.isEmpty()) {
|
||||
publickey = null;
|
||||
}
|
||||
else {
|
||||
this.user_config.setPassword(publickey);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ConnectException e) {
|
||||
this.log.error("Server::getConfiguration error ConnectException " + e);
|
||||
return Error.Type.NETWORK_ISSUE;
|
||||
}
|
||||
catch (UnknownHostException e) {
|
||||
this.log.error("Server::getConfiguration: exception UnknownHostException " + e);
|
||||
return Error.Type.NETWORK_ISSUE;
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
this.log.error("Server::getConfiguration: exception UnsupportedEncodingException " + e);
|
||||
return Error.Type.UNKNOWN;
|
||||
}
|
||||
catch (IOException e) {
|
||||
this.log.error("Server::getConfiguration: exception IOException " + e);
|
||||
return Error.Type.UNKNOWN;
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.log.error("Server::getConfiguration: exception Exception " + e);
|
||||
return Error.Type.UNKNOWN;
|
||||
}
|
||||
|
||||
if (serverConfig.getSpeedTestTargets() != null && serverConfig.getSpeedTestTargets().isEmpty() == false) {
|
||||
try {
|
||||
client.getGui().status("Checking mirror connection speeds");
|
||||
Speedtest speedtest = new Speedtest(log);
|
||||
List<SpeedTestTarget> bestSpeedTestTargets = speedtest.doSpeedtests(serverConfig.getSpeedTestTargets().stream().map(m -> m.getUrl()).collect(Collectors.toList()),
|
||||
NUMBER_OF_SPEEDTEST_RESULTS);
|
||||
SpeedTestResult result = new SpeedTestResult();
|
||||
result.setResults(bestSpeedTestTargets.stream().map(m -> {
|
||||
SpeedTestTargetResult targetResult = new SpeedTestTargetResult();
|
||||
targetResult.setTarget(m.getUrl());
|
||||
targetResult.setSpeed(m.getSpeedtest());
|
||||
targetResult.setPing((int) (m.getPing().getAverage()));
|
||||
return targetResult;
|
||||
}).collect(Collectors.toList()));
|
||||
|
||||
final Persister persister = new Persister();
|
||||
try (StringWriter writer = new StringWriter()) {
|
||||
persister.write(result, writer);
|
||||
|
||||
HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(this.getPage("speedtest-answer"))).newBuilder();
|
||||
Response response = this.HTTPRequest(urlBuilder, RequestBody.create(MediaType.parse("application/xml"), writer.toString()));
|
||||
if (response.code() != HttpURLConnection.HTTP_OK) {
|
||||
throw new IOException("Server::getConfiguration Speedtest unexpected response");
|
||||
}
|
||||
}
|
||||
catch (final Exception e) {
|
||||
throw new IOException("Server::getConfiguration Speedtest failed to generate payload");
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
this.log.error("Server::getConfiguration Speedtest failed: " + e);
|
||||
return Error.Type.NETWORK_ISSUE;
|
||||
}
|
||||
}
|
||||
|
||||
client.setSessionStarted(true);
|
||||
this.client.getGui().successfulAuthenticationEvent(publickey);
|
||||
|
||||
return Error.Type.OK;
|
||||
}
|
||||
|
||||
public Job requestJob() throws FermeException {
|
||||
this.log.debug("Server::requestJob");
|
||||
String url_contents = "";
|
||||
|
||||
try {
|
||||
OS os = OS.getOS();
|
||||
long maxMemory = this.user_config.getMaxAllowedMemory();
|
||||
long freeMemory = os.getFreeMemory() - 1024^2 /*One gigabyte*/ * (os instanceof Windows ? 2 : 1); //Make RAM to reserve 2GB on Windows
|
||||
if (maxMemory < 0) {
|
||||
maxMemory = freeMemory;
|
||||
}
|
||||
else if (freeMemory > 0 && maxMemory > 0) {
|
||||
maxMemory = Math.min(maxMemory, freeMemory);
|
||||
}
|
||||
|
||||
HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(this.getPage("request-job"))).newBuilder()
|
||||
.addQueryParameter("computemethod", String.valueOf(user_config.computeMethodToInt()))
|
||||
.addQueryParameter("network_dl", String.valueOf(dlStats.getRawAverageSessionSpeed()))
|
||||
.addQueryParameter("network_up", String.valueOf(ulStats.getRawAverageSessionSpeed()))
|
||||
.addQueryParameter("cpu_cores", String.valueOf(user_config.getNbCores() == -1 ? os.getCPU().cores() :
|
||||
(Math.max(user_config.getNbCores(), CPU.MIN_CORES))))
|
||||
.addQueryParameter("ram_max", String.valueOf(maxMemory))
|
||||
.addQueryParameter("rendertime_max", String.valueOf(user_config.getMaxRenderTime()));
|
||||
|
||||
if (user_config.getComputeMethod() != ComputeType.CPU && user_config.getGPUDevice() != null) {
|
||||
urlBuilder.addQueryParameter("gpu_model", user_config.getGPUDevice().getModel())
|
||||
.addQueryParameter("gpu_ram", String.valueOf(user_config.getGPUDevice().getMemory()))
|
||||
.addQueryParameter("gpu_type", user_config.getGPUDevice().getType());
|
||||
}
|
||||
|
||||
Response response = this.HTTPRequest(urlBuilder, RequestBody.create(this.generateXMLForMD5cache(), MediaType.parse("application/xml")));
|
||||
|
||||
int r = response.code();
|
||||
String contentType = response.body().contentType().toString();
|
||||
|
||||
if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/xml")) {
|
||||
String in = response.body().string();
|
||||
|
||||
JobInfos jobData = new Persister().read(JobInfos.class, in);
|
||||
|
||||
handleFileMD5DeleteDocument(jobData.getFileMD5s());
|
||||
|
||||
if (jobData.getSessionStats() != null) {
|
||||
this.client.getGui().displayStats(
|
||||
new Stats(jobData.getSessionStats().getRemainingFrames(), jobData.getSessionStats().getPointsEarnedByUser(),
|
||||
jobData.getSessionStats().getPointsEarnedOnSession(), jobData.getSessionStats().getRenderableProjects(),
|
||||
jobData.getSessionStats().getWaitingProjects(), jobData.getSessionStats().getConnectedMachines()));
|
||||
}
|
||||
|
||||
ServerCode serverCode = ServerCode.fromInt(jobData.getStatus());
|
||||
if (serverCode != ServerCode.OK) {
|
||||
switch (serverCode) {
|
||||
case JOB_REQUEST_NOJOB:
|
||||
return null;
|
||||
case JOB_REQUEST_ERROR_NO_RENDERING_RIGHT:
|
||||
throw new FermeExceptionNoRightToRender();
|
||||
case JOB_REQUEST_ERROR_DEAD_SESSION:
|
||||
throw new FermeExceptionNoSession();
|
||||
case JOB_REQUEST_ERROR_SESSION_DISABLED:
|
||||
throw new FermeExceptionSessionDisabled();
|
||||
case JOB_REQUEST_ERROR_INTERNAL_ERROR:
|
||||
throw new FermeExceptionBadResponseFromServer();
|
||||
case JOB_REQUEST_ERROR_RENDERER_NOT_AVAILABLE:
|
||||
throw new FermeExceptionNoRendererAvailable();
|
||||
case JOB_REQUEST_SERVER_IN_MAINTENANCE:
|
||||
throw new FermeExceptionServerInMaintenance();
|
||||
case JOB_REQUEST_SERVER_OVERLOADED:
|
||||
throw new FermeExceptionServerOverloaded();
|
||||
default:
|
||||
throw new FermeException("error requestJob: status is not ok (it's " + serverCode + ")");
|
||||
}
|
||||
}
|
||||
|
||||
String validationUrl = URLDecoder.decode(jobData.getRenderTask().getValidationUrl(), "UTF-8");
|
||||
|
||||
return new Job(this.user_config, this.client.getGui(), this.client.getLog(), jobData.getRenderTask().getId(),
|
||||
jobData.getRenderTask().getFrame(), jobData.getRenderTask().getPath().replace("/", File.separator),
|
||||
jobData.getRenderTask().getUseGpu() == 1, jobData.getRenderTask().getRendererInfos().getCommandline(), validationUrl,
|
||||
jobData.getRenderTask().getScript(), jobData.getRenderTask().getArchive_md5(), jobData.getRenderTask().getRendererInfos().getMd5(),
|
||||
jobData.getRenderTask().getName(), jobData.getRenderTask().getPassword(),
|
||||
jobData.getRenderTask().getSynchronous_upload().equals("1"), jobData.getRenderTask().getRendererInfos().getUpdate_method());
|
||||
}
|
||||
else {
|
||||
System.out.println("Server::requestJob url " + url_contents + " r " + r + " contentType " + contentType);
|
||||
if (r == HttpURLConnection.HTTP_UNAVAILABLE || r == HttpURLConnection.HTTP_CLIENT_TIMEOUT) {
|
||||
// most likely varnish is up but apache down
|
||||
throw new FermeServerDown();
|
||||
}
|
||||
else if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/html")) {
|
||||
throw new FermeExceptionBadResponseFromServer();
|
||||
}
|
||||
System.out.println(response.body().string());
|
||||
}
|
||||
}
|
||||
catch (FermeException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (NoRouteToHostException e) {
|
||||
throw new FermeServerDown();
|
||||
}
|
||||
catch (UnknownHostException e) {
|
||||
throw new FermeServerDown();
|
||||
}
|
||||
catch (Exception e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
throw new FermeException("error requestJob: unknown exception " + e + " stacktrace: " + sw.toString());
|
||||
}
|
||||
throw new FermeException("error requestJob, end of function");
|
||||
}
|
||||
|
||||
public Response HTTPRequest(String url) throws IOException {
|
||||
HttpUrl.Builder httpUrlBuilder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder();
|
||||
return this.HTTPRequest(httpUrlBuilder, null);
|
||||
}
|
||||
|
||||
public Response HTTPRequest(HttpUrl.Builder httpUrlBuilder) throws IOException {
|
||||
return this.HTTPRequest(httpUrlBuilder, null);
|
||||
}
|
||||
|
||||
public Response HTTPRequest(HttpUrl.Builder httpUrlBuilder, RequestBody data_) throws IOException {
|
||||
String url = httpUrlBuilder.build().toString();
|
||||
Request.Builder builder = new Request.Builder().addHeader("User-Agent", HTTP_USER_AGENT).url(url);
|
||||
|
||||
this.log.debug("Server::HTTPRequest url(" + url + ")");
|
||||
|
||||
if (data_ != null) {
|
||||
builder.post(data_);
|
||||
}
|
||||
|
||||
Request request = builder.build();
|
||||
Response response = null;
|
||||
|
||||
try {
|
||||
response = httpClient.newCall(request).execute();
|
||||
|
||||
if (!response.isSuccessful()) {
|
||||
throw new IOException("Unexpected code " + response);
|
||||
}
|
||||
|
||||
this.lastRequestTime = new Date().getTime();
|
||||
return response;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IOException("Unexpected response from HTTP Stack" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public Error.Type HTTPGetFile(String url_, String destination_, Gui gui_, String status_) throws FermeException {
|
||||
InputStream is = null;
|
||||
OutputStream output = null;
|
||||
|
||||
try {
|
||||
Response response = this.HTTPRequest(url_);
|
||||
|
||||
if (response.code() != HttpURLConnection.HTTP_OK) {
|
||||
this.log.error("Server::HTTPGetFile(" + url_ + ", ...) HTTP code is not " + HttpURLConnection.HTTP_OK + " it's " + response.code());
|
||||
return Error.Type.DOWNLOAD_FILE;
|
||||
}
|
||||
|
||||
is = response.body().byteStream();
|
||||
output = new FileOutputStream(destination_ + ".partial");
|
||||
|
||||
long size = response.body().contentLength();
|
||||
byte[] buffer = new byte[8 * 1024];
|
||||
int len = 0;
|
||||
long written = 0;
|
||||
long lastUpd = 0; // last GUI progress update
|
||||
|
||||
LocalDateTime startRequestTime = LocalDateTime.now();
|
||||
|
||||
while ((len = is.read(buffer)) != -1) {
|
||||
if (this.client.getRenderingJob().isServerBlockJob()) {
|
||||
return Error.Type.RENDERER_KILLED_BY_SERVER;
|
||||
}
|
||||
else if (this.client.getRenderingJob().isUserBlockJob()) {
|
||||
return Error.Type.RENDERER_KILLED_BY_USER;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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.getSeconds() + 1).getAverageSessionSpeed(), written));
|
||||
|
||||
this.lastRequestTime = new Date().getTime();
|
||||
return Error.Type.OK;
|
||||
}
|
||||
catch (Exception e) {
|
||||
File destFile = new File(destination_);
|
||||
if (Files.isWritable(destFile.getParentFile().toPath()) == false) {
|
||||
throw new FermeExceptionNoWritePermission();
|
||||
}
|
||||
else if (destFile.getParentFile().isDirectory() == false) {
|
||||
throw new FermeExceptionPathInvalid();
|
||||
}
|
||||
else if (Utils.noFreeSpaceOnDisk(destFile.getParent(), log)) {
|
||||
throw new FermeExceptionNoSpaceLeftOnDevice();
|
||||
}
|
||||
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
this.log.error("Server::HTTPGetFile Exception " + e + " stacktrace " + sw.toString());
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if (output != null) {
|
||||
output.flush();
|
||||
output.close();
|
||||
}
|
||||
|
||||
File downloadedFile = new File(destination_ + ".partial");
|
||||
|
||||
if (downloadedFile.exists()) {
|
||||
// Rename file (or directory)
|
||||
boolean success = downloadedFile.renameTo(new File(destination_));
|
||||
|
||||
if (!success) {
|
||||
this.log.debug(String.format("Server::HTTPGetFile Error trying to rename the downloaded file to final name (%s)", destination_));
|
||||
}
|
||||
}
|
||||
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.log.debug(String.format("Server::HTTPGetFile Error trying to close the open streams (%s)", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
this.log.debug(String.format("Server::HTTPGetFile(%s) did fail", url_));
|
||||
return Error.Type.DOWNLOAD_FILE;
|
||||
}
|
||||
|
||||
public ServerCode HTTPSendFile(String surl, String file1, int checkpoint, Gui gui) {
|
||||
this.log.debug(checkpoint, "Server::HTTPSendFile(" + surl + "," + file1 + ")");
|
||||
|
||||
try {
|
||||
String fileMimeType = Utils.findMimeType(file1);
|
||||
File fileHandler = new File(file1);
|
||||
|
||||
MediaType MEDIA_TYPE = MediaType.parse(fileMimeType); // e.g. "image/png"
|
||||
|
||||
RequestBody uploadContent = new MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||
.addFormDataPart("file", fileHandler.getName(), RequestBody.create(fileHandler, MEDIA_TYPE)).build();
|
||||
|
||||
Request request = new Request.Builder().addHeader("User-Agent", HTTP_USER_AGENT).url(surl).post(uploadContent).build();
|
||||
|
||||
LocalDateTime startRequestTime = LocalDateTime.now();
|
||||
|
||||
Call call = httpClient.newCall(request);
|
||||
Response response = call.execute();
|
||||
|
||||
LocalDateTime endRequestTime = LocalDateTime.now();
|
||||
Duration duration = Duration.between(startRequestTime, endRequestTime);
|
||||
|
||||
this.ulStats.calc(fileHandler.length(), ((duration.getSeconds() * 1000) + (duration.getNano() / 1000000)));
|
||||
gui.displayTransferStats(dlStats, ulStats);
|
||||
|
||||
this.log.debug(String.format("File uploaded at %s/s, uploaded %d bytes", new TransferStats(fileHandler.length(), duration.getSeconds() + 1).getAverageSessionSpeed(), fileHandler.length()));
|
||||
|
||||
int r = response.code();
|
||||
String contentType = response.body().contentType().toString();
|
||||
|
||||
if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/xml")) {
|
||||
try {
|
||||
String in = response.body().string();
|
||||
JobValidation jobValidation = new Persister().read(JobValidation.class, in);
|
||||
|
||||
this.lastRequestTime = new Date().getTime();
|
||||
|
||||
ServerCode serverCode = ServerCode.fromInt(jobValidation.getStatus());
|
||||
if (serverCode != ServerCode.OK) {
|
||||
this.log.error(checkpoint, "Server::HTTPSendFile wrong status (is " + serverCode + ")");
|
||||
return serverCode;
|
||||
}
|
||||
}
|
||||
catch (Exception e) { // for the .read
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return ServerCode.OK;
|
||||
}
|
||||
else if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/html")) {
|
||||
return ServerCode.ERROR_BAD_RESPONSE;
|
||||
}
|
||||
// We don't check all the HTTP 4xx but the 413 in particular, we can always find a huge image larger than whatever configuration we have in the
|
||||
// server and it's worth to send the error back to the server if this happen
|
||||
else if (r == HttpURLConnection.HTTP_ENTITY_TOO_LARGE) {
|
||||
this.log.error(response.body().string());
|
||||
return ServerCode.JOB_VALIDATION_IMAGE_TOO_LARGE;
|
||||
}
|
||||
else {
|
||||
this.log.error(String.format("Server::HTTPSendFile Unknown response received from server: %s", response.body().string()));
|
||||
}
|
||||
|
||||
return ServerCode.UNKNOWN;
|
||||
}
|
||||
catch (IOException e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
this.log.error(checkpoint, String.format("Server::HTTPSendFile Error in upload process. Exception %s stacktrace ", e.getMessage()) + sw.toString());
|
||||
return ServerCode.UNKNOWN;
|
||||
}
|
||||
catch (OutOfMemoryError e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
this.log.error(checkpoint, "Server::HTTPSendFile, OutOfMemoryError " + e + " stacktrace " + sw.toString());
|
||||
return ServerCode.JOB_VALIDATION_ERROR_UPLOAD_FAILED;
|
||||
}
|
||||
catch (Exception e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
this.log.error(checkpoint, "Server::HTTPSendFile, Exception " + e + " stacktrace " + sw.toString());
|
||||
return ServerCode.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
private String generateXMLForMD5cache() {
|
||||
List<FileMD5> md5s = new ArrayList<>();
|
||||
for (File local_file : this.user_config.getLocalCacheFiles()) {
|
||||
try {
|
||||
String extension = local_file.getName().substring(local_file.getName().lastIndexOf('.')).toLowerCase();
|
||||
String name = local_file.getName().substring(0, local_file.getName().length() - 1 * extension.length());
|
||||
if (extension.equals(".zip")) {
|
||||
// node_file.setAttribute("md5", name);
|
||||
FileMD5 fileMD5 = new FileMD5();
|
||||
fileMD5.setMd5(name);
|
||||
|
||||
md5s.add(fileMD5);
|
||||
}
|
||||
}
|
||||
catch (StringIndexOutOfBoundsException e) { // because the file does not have an . its path
|
||||
}
|
||||
}
|
||||
|
||||
CacheFileMD5 cache = new CacheFileMD5();
|
||||
cache.setMd5s(md5s);
|
||||
|
||||
final Persister persister = new Persister();
|
||||
try (StringWriter writer = new StringWriter()) {
|
||||
persister.write(cache, writer);
|
||||
return writer.toString();
|
||||
}
|
||||
catch (final Exception e) {
|
||||
log.debug("Failed to dump md5s " + e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFileMD5DeleteDocument(List<FileMD5> fileMD5s) {
|
||||
if (fileMD5s != null && fileMD5s.isEmpty() == false) {
|
||||
for (FileMD5 fileMD5 : fileMD5s) {
|
||||
if ("delete".equals(fileMD5.getAction()) && fileMD5.getMd5() != null && fileMD5.getMd5().isEmpty() == false) {
|
||||
String path = this.user_config.getWorkingDirectory().getAbsolutePath() + File.separatorChar + fileMD5.getMd5();
|
||||
this.log.debug("Server::handleFileMD5DeleteDocument delete old file " + path);
|
||||
File file_to_delete = new File(path + ".zip");
|
||||
file_to_delete.delete();
|
||||
Utils.delete(new File(path));
|
||||
|
||||
// If we are using a shared downloads directory, then delete the file from the shared downloads directory as well :)
|
||||
if (this.user_config.getSharedDownloadsDirectory() != null) {
|
||||
String commonCacheFile = this.user_config.getSharedDownloadsDirectory().getAbsolutePath() + File.separatorChar + fileMD5.getMd5();
|
||||
this.log.debug("Server::handleFileMD5DeleteDocument delete common file " + commonCacheFile + ".zip");
|
||||
file_to_delete = new File(commonCacheFile + ".zip");
|
||||
file_to_delete.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getPage(String key) {
|
||||
if (this.serverConfig != null) {
|
||||
RequestEndPoint endpoint = this.serverConfig.getRequestEndPoint(key);
|
||||
if (endpoint != null) {
|
||||
return this.base_url + endpoint.getPath();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private OkHttpClient getOkHttpClient() {
|
||||
try {
|
||||
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
||||
|
||||
CookieManager cookieManager = new CookieManager();
|
||||
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
|
||||
builder.cookieJar(new JavaNetCookieJar(cookieManager)); // Cookie store to maintain the session across calls
|
||||
|
||||
builder.connectTimeout(30, TimeUnit.SECONDS); // Cancel the HTTP Request if the connection to server takes more than 10 seconds
|
||||
builder.writeTimeout(60, TimeUnit.SECONDS); // Cancel the upload if the client cannot send any byte in 60 seconds
|
||||
|
||||
// If the user has selected a proxy, then we must increase the download timeout. Reason being the way proxies work. To download a large file (i.e.
|
||||
// a 500MB job), the proxy must first download the file to the proxy cache and then the information is sent fast to the SheepIt client. From a client
|
||||
// viewpoint, the HTTP connection will make the CONNECT step really fast but then the time until the fist byte is received (the time measured by
|
||||
// readTimeout) will be really long (minutes). Without a proxy in the middle, a connection that does receive nothing in 60 seconds might be
|
||||
// considered a dead connection.
|
||||
if (this.user_config.getProxy() != null) {
|
||||
builder.readTimeout(10, TimeUnit.MINUTES); // Proxy enabled - 10 minutes
|
||||
}
|
||||
else {
|
||||
builder.readTimeout(1, TimeUnit.MINUTES); // No proxy - 60 seconds max
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
546
src/main/java/com/sheepit/client/SettingsLoader.java
Normal file
546
src/main/java/com/sheepit/client/SettingsLoader.java
Normal file
@@ -0,0 +1,546 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sheepit.client.Configuration.ComputeType;
|
||||
import com.sheepit.client.hardware.gpu.GPU;
|
||||
import com.sheepit.client.hardware.gpu.GPUDevice;
|
||||
import com.sheepit.client.standalone.GuiText;
|
||||
import com.sheepit.client.standalone.GuiTextOneLine;
|
||||
import com.sheepit.client.os.OS;
|
||||
import com.sheepit.client.os.Linux;
|
||||
import com.sheepit.client.os.Mac;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SettingsLoader {
|
||||
|
||||
private enum PropertyNames {
|
||||
|
||||
PRIORITY("priority"),
|
||||
CACHE_DIR("cache-dir"),
|
||||
COMPUTE_METHOD("compute-method"),
|
||||
GPU("compute-gpu"),
|
||||
CORES("cores"),
|
||||
CORES_BACKWARDS_COMPAT("cpu-cores"),
|
||||
RAM("ram"),
|
||||
RENDER_TIME("rendertime"),
|
||||
LOGIN("login"),
|
||||
PASSWORD("password"),
|
||||
PROXY("proxy"),
|
||||
HOSTNAME("hostname"),
|
||||
AUTO_SIGNIN("auto-signin"),
|
||||
USE_SYSTRAY("use-systray"),
|
||||
HEADLESS("headless"),
|
||||
UI("ui"),
|
||||
THEME("theme");
|
||||
|
||||
String propertyName;
|
||||
|
||||
PropertyNames(String prop) {
|
||||
this.propertyName = prop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final String ARG_SERVER = "-server";
|
||||
public static final String ARG_LOGIN = "-login";
|
||||
public static final String ARG_PASSWORD = "-password";
|
||||
public static final String ARG_CACHE_DIR = "-cache-dir";
|
||||
public static final String ARG_SHARED_ZIP = "-shared-zip";
|
||||
public static final String ARG_GPU = "-gpu";
|
||||
public static final String ARG_NO_GPU = "--no-gpu";
|
||||
public static final String ARG_COMPUTE_METHOD = "-compute-method";
|
||||
public static final String ARG_CORES = "-cores";
|
||||
public static final String ARG_MEMORY = "-memory";
|
||||
public static final String ARG_RENDERTIME = "-rendertime";
|
||||
public static final String ARG_VERBOSE = "--verbose";
|
||||
public static final String ARG_REQUEST_TIME = "-request-time";
|
||||
public static final String ARG_SHUTDOWN = "-shutdown";
|
||||
public static final String ARG_SHUTDOWN_MODE = "-shutdown-mode";
|
||||
public static final String ARG_PROXY = "-proxy";
|
||||
public static final String ARG_EXTRAS = "-extras";
|
||||
public static final String ARG_UI = "-ui";
|
||||
public static final String ARG_CONFIG = "-config";
|
||||
public static final String ARG_VERSION = "--version";
|
||||
public static final String ARG_SHOW_GPU = "--show-gpu";
|
||||
public static final String ARG_NO_SYSTRAY = "--no-systray";
|
||||
public static final String ARG_PRIORITY = "-priority";
|
||||
public static final String ARG_TITLE = "-title";
|
||||
public static final String ARG_THEME = "-theme";
|
||||
public static final String ARG_HOSTNAME = "-hostname";
|
||||
public static final String ARG_HEADLESS = "--headless";
|
||||
|
||||
|
||||
private String path;
|
||||
|
||||
private Option<String> login;
|
||||
|
||||
private Option<String> password;
|
||||
|
||||
private Option<String> proxy;
|
||||
private Option<String> hostname;
|
||||
private Option<String> computeMethod;
|
||||
private Option<String> gpu;
|
||||
private Option<String> cores;
|
||||
private Option<String> ram;
|
||||
private Option<String> renderTime;
|
||||
private Option<String> cacheDir;
|
||||
private Option<String> autoSignIn;
|
||||
private Option<String> useSysTray;
|
||||
private Option<String> headless;
|
||||
private Option<String> ui;
|
||||
private Option<String> theme;
|
||||
private Option<Integer> priority;
|
||||
|
||||
public SettingsLoader(String path_) {
|
||||
if (path_ == null) {
|
||||
path = OS.getOS().getDefaultConfigFilePath();
|
||||
}
|
||||
else {
|
||||
path = path_;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSettings(String path_, String login_, String password_, String proxy_, String hostname_,
|
||||
ComputeType computeMethod_, GPUDevice gpu_, Integer cores_, Long maxRam_,
|
||||
Integer maxRenderTime_, String cacheDir_, Boolean autoSignIn_, Boolean useSysTray_, Boolean isHeadless,
|
||||
String ui_, String theme_, Integer priority_) {
|
||||
if (path_ == null) {
|
||||
path = OS.getOS().getDefaultConfigFilePath();
|
||||
}
|
||||
else {
|
||||
path = path_;
|
||||
}
|
||||
login = setValue(login_, login, ARG_LOGIN);
|
||||
password = setValue(password_, password, ARG_PASSWORD);
|
||||
proxy = setValue(proxy_, proxy, ARG_PROXY);
|
||||
hostname = setValue(hostname_, hostname, ARG_HOSTNAME);
|
||||
cacheDir = setValue(cacheDir_, cacheDir, ARG_CACHE_DIR);
|
||||
autoSignIn = setValue(autoSignIn_.toString(), autoSignIn, "");
|
||||
useSysTray = setValue(useSysTray_.toString(), useSysTray, ARG_NO_SYSTRAY);
|
||||
headless = setValue(isHeadless.toString(), headless, ARG_HEADLESS);
|
||||
ui = setValue(ui_, ui, ARG_UI);
|
||||
priority = setValue(priority_, priority, ARG_PRIORITY);
|
||||
theme = setValue(theme_, theme, ARG_THEME);
|
||||
|
||||
if (cores_ > 0) {
|
||||
cores = setValue(cores_.toString(), cores, ARG_CORES);
|
||||
}
|
||||
if (maxRam_ > 0) {
|
||||
ram = setValue(maxRam_+ "k", ram, ARG_MEMORY);
|
||||
}
|
||||
if (maxRenderTime_ > 0) {
|
||||
renderTime = setValue(maxRenderTime_.toString(), renderTime, ARG_RENDERTIME);
|
||||
}
|
||||
if (computeMethod_ != null) {
|
||||
try {
|
||||
computeMethod = setValue(computeMethod_.name(), computeMethod, ARG_COMPUTE_METHOD);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (gpu_ != null) {
|
||||
gpu = setValue(gpu_.getId(), gpu, ARG_GPU);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sets an option to a given value. If the option being passed on is null it will be created with the given value and returned.
|
||||
* @param value The value to be set
|
||||
* @param option The {@link Option} object that the value is going to be stored in. Can be null
|
||||
* @param launchFlag A flag indicating whether the option was set via a launch argument or not
|
||||
* @param <T> The type of the value stored within the option
|
||||
* @return The {@link Option} object that has been passed on as a parameter with the value being set, or a newly created object if option was null
|
||||
*/
|
||||
private <T> Option<T> setValue(T value, Option<T> option, String launchFlag) {
|
||||
if (option == null && value != null) {
|
||||
option = new Option<>(value, launchFlag);
|
||||
}
|
||||
else if (value != null){
|
||||
option.setValue(value);
|
||||
}
|
||||
return option;
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the list of launch parameters and marks every config setting corresponding to one of the set values as launch command, ensuring that they wont overwrite
|
||||
* the one in the config file
|
||||
* @param argsList a list of the launch arguments
|
||||
*/
|
||||
public void markLaunchSettings(List<String> argsList) {
|
||||
Option options[] = { login, password, proxy, hostname, computeMethod, gpu, cores, ram, renderTime, cacheDir, autoSignIn,
|
||||
useSysTray, headless, ui, theme, priority };
|
||||
|
||||
for (Option option : options) {
|
||||
if (option != null && argsList.contains(option.getLaunchFlag())) {
|
||||
option.setLaunchCommand(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the right setting to store to the config file between the value currently set in the config file and the option the client is working with
|
||||
* currently, depending on whether the setting was set via launch argument or not
|
||||
* @param saveTo the properties object representing the config file that is going to be written
|
||||
* @param configFileProperties the properties object containing the current config file values
|
||||
* @param property an enum representing the name of the setting
|
||||
* @param option the option containing the currently used value
|
||||
*/
|
||||
private void setProperty(Properties saveTo, Properties configFileProperties, PropertyNames property, Option<String> option) {
|
||||
if (option != null) {
|
||||
if (option.isLaunchCommand()) {
|
||||
String configValue = configFileProperties.getProperty(property.propertyName);
|
||||
if (configValue != null) {
|
||||
saveTo.setProperty(property.propertyName, configValue);
|
||||
}
|
||||
}
|
||||
else {
|
||||
saveTo.setProperty(property.propertyName, option.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("PointlessBooleanExpression") public void saveFile() {
|
||||
|
||||
Properties configFileProp = new Properties();
|
||||
if (new File(path).exists()) {
|
||||
InputStream input = null;
|
||||
try {
|
||||
input = new FileInputStream(path);
|
||||
configFileProp.load(input);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Properties prop = new Properties();
|
||||
OutputStream output = null;
|
||||
try {
|
||||
output = new FileOutputStream(path);
|
||||
|
||||
setProperty(prop, configFileProp, PropertyNames.PRIORITY,
|
||||
new Option<>(priority != null ? priority.getValue().toString() : null, priority.isLaunchCommand(), ARG_PRIORITY));
|
||||
setProperty(prop, configFileProp, PropertyNames.CACHE_DIR, cacheDir);
|
||||
setProperty(prop, configFileProp, PropertyNames.COMPUTE_METHOD, computeMethod);
|
||||
setProperty(prop, configFileProp, PropertyNames.GPU, gpu);
|
||||
setProperty(prop, configFileProp, PropertyNames.CORES, cores);
|
||||
setProperty(prop, configFileProp, PropertyNames.RAM, ram);
|
||||
setProperty(prop, configFileProp, PropertyNames.RENDER_TIME, renderTime);
|
||||
setProperty(prop, configFileProp, PropertyNames.LOGIN, login);
|
||||
setProperty(prop, configFileProp, PropertyNames.PASSWORD, password);
|
||||
setProperty(prop, configFileProp, PropertyNames.PROXY, proxy);
|
||||
setProperty(prop, configFileProp, PropertyNames.HOSTNAME, hostname);
|
||||
setProperty(prop, configFileProp, PropertyNames.AUTO_SIGNIN, autoSignIn);
|
||||
setProperty(prop, configFileProp, PropertyNames.USE_SYSTRAY, useSysTray);
|
||||
setProperty(prop, configFileProp, PropertyNames.HEADLESS, headless);
|
||||
setProperty(prop, configFileProp, PropertyNames.UI, ui);
|
||||
setProperty(prop, configFileProp, PropertyNames.THEME, theme);
|
||||
prop.store(output, null);
|
||||
}
|
||||
catch (IOException io) {
|
||||
io.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
if (output != null) {
|
||||
try {
|
||||
output.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set Owner read/write
|
||||
Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>();
|
||||
perms.add(PosixFilePermission.OWNER_READ);
|
||||
perms.add(PosixFilePermission.OWNER_WRITE);
|
||||
|
||||
try {
|
||||
Files.setPosixFilePermissions(Paths.get(path), perms);
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// most likely because it's MS Windows
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes or sets an option object to the corresponding value from the config file
|
||||
* @param config the properties loaded from the config file
|
||||
* @param property the name of the property
|
||||
* @param option the option to store the property value
|
||||
* @param launchFlag the launch argument corresponding to the respective option
|
||||
*/
|
||||
private Option<String> loadConfigOption(Properties config, PropertyNames property, Option<String> option, String launchFlag) {
|
||||
String configValue;
|
||||
if (config.containsKey(property.propertyName)) {
|
||||
configValue = config.getProperty(property.propertyName);
|
||||
if (option == null && configValue != null) {
|
||||
option = new Option<>(configValue, launchFlag);
|
||||
}
|
||||
else if (configValue != null){
|
||||
option.setValue(configValue);
|
||||
}
|
||||
}
|
||||
return option;
|
||||
}
|
||||
|
||||
public void loadFile(boolean initialize) throws Exception {
|
||||
|
||||
if (initialize)
|
||||
initWithDefaults();
|
||||
|
||||
if (new File(path).exists() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
Properties prop = new Properties();
|
||||
InputStream input = null;
|
||||
try {
|
||||
input = new FileInputStream(path);
|
||||
prop.load(input);
|
||||
|
||||
cacheDir = loadConfigOption(prop, PropertyNames.CACHE_DIR, cacheDir, ARG_CACHE_DIR);
|
||||
|
||||
computeMethod = loadConfigOption(prop, PropertyNames.COMPUTE_METHOD, computeMethod, ARG_COMPUTE_METHOD);
|
||||
|
||||
gpu = loadConfigOption(prop, PropertyNames.GPU, gpu, ARG_GPU);
|
||||
|
||||
cores = loadConfigOption(prop, PropertyNames.CORES_BACKWARDS_COMPAT, cores, ARG_CORES);
|
||||
|
||||
cores = loadConfigOption(prop, PropertyNames.CORES, cores, ARG_CORES);
|
||||
|
||||
ram = loadConfigOption(prop, PropertyNames.RAM, ram, ARG_MEMORY);
|
||||
|
||||
renderTime = loadConfigOption(prop, PropertyNames.RENDER_TIME, renderTime, ARG_RENDERTIME);
|
||||
|
||||
login = loadConfigOption(prop, PropertyNames.LOGIN, login, ARG_LOGIN);
|
||||
|
||||
password = loadConfigOption(prop, PropertyNames.PASSWORD, password, ARG_PASSWORD);
|
||||
|
||||
proxy = loadConfigOption(prop, PropertyNames.PROXY, proxy, ARG_PROXY);
|
||||
|
||||
hostname = loadConfigOption(prop, PropertyNames.HOSTNAME, hostname, ARG_HOSTNAME);
|
||||
|
||||
autoSignIn = loadConfigOption(prop, PropertyNames.AUTO_SIGNIN, autoSignIn, "");
|
||||
|
||||
useSysTray = loadConfigOption(prop, PropertyNames.USE_SYSTRAY, useSysTray, ARG_NO_SYSTRAY);
|
||||
|
||||
headless = loadConfigOption(prop, PropertyNames.HEADLESS, headless, ARG_HEADLESS);
|
||||
|
||||
ui = loadConfigOption(prop, PropertyNames.UI, ui, ARG_UI);
|
||||
|
||||
theme = loadConfigOption(prop, PropertyNames.THEME, theme, ARG_THEME);
|
||||
|
||||
if (prop.containsKey(PropertyNames.PRIORITY.propertyName)) {
|
||||
int prio = Integer.parseInt(prop.getProperty(PropertyNames.PRIORITY.propertyName));
|
||||
if (priority == null) {
|
||||
priority = new Option<>(prio, ARG_PRIORITY);
|
||||
}
|
||||
else {
|
||||
priority.setValue(prio);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) { //We need the try-catch here to ensure that the input file will be closed though we'll deal with the exception in the calling method
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the Settings file with the Configuration.
|
||||
* The Configuration will have high priority.
|
||||
* @param config the config file
|
||||
* @param initialize whether to initialize all fields with default values, should only be true on first call
|
||||
*/
|
||||
public void merge(Configuration config, boolean initialize) {
|
||||
if (config == null) {
|
||||
System.out.println("SettingsLoader::merge config is null");
|
||||
}
|
||||
|
||||
try {
|
||||
loadFile(initialize);
|
||||
applyConfigFileValues(config);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("Exception while reading the config file. Falling back to defaults");
|
||||
initWithDefaults();
|
||||
applyConfigFileValues(config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void applyConfigFileValues(Configuration config) {
|
||||
if (config.getLogin().isEmpty() && login != null) {
|
||||
config.setLogin(login.getValue());
|
||||
}
|
||||
if (config.getPassword().isEmpty() && password != null) {
|
||||
config.setPassword(password.getValue());
|
||||
}
|
||||
|
||||
if ((config.getProxy() == null || config.getProxy().isEmpty()) && proxy != null) {
|
||||
config.setProxy(proxy.getValue());
|
||||
}
|
||||
|
||||
if ((config.getHostname() == null || config.getHostname().isEmpty() || config.getHostname().equals(config.getDefaultHostname())) && hostname != null) {
|
||||
config.setHostname(hostname.getValue());
|
||||
}
|
||||
|
||||
if (config.isHeadless() == false && headless != null) {
|
||||
config.setHeadless(Boolean.parseBoolean(headless.getValue()));
|
||||
}
|
||||
|
||||
if (config.getPriority() == 19) { // 19 is default value
|
||||
config.setUsePriority(priority.getValue());
|
||||
}
|
||||
try {
|
||||
if (config.getComputeMethod() == null && computeMethod == null) {
|
||||
config.setComputeMethod(ComputeType.CPU);
|
||||
}
|
||||
else if ((config.getComputeMethod() == null && computeMethod != null) || (computeMethod != null && config.getComputeMethod() != ComputeType
|
||||
.valueOf(computeMethod.getValue()))) {
|
||||
if (config.getComputeMethod() == null) {
|
||||
config.setComputeMethod(ComputeType.valueOf(computeMethod.getValue()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
System.err.println("SettingsLoader::merge failed to handle compute method (raw value: '" + computeMethod + "')");
|
||||
computeMethod = null;
|
||||
}
|
||||
if (config.getGPUDevice() == null && gpu != null) {
|
||||
GPUDevice device = GPU.getGPUDevice(gpu.getValue());
|
||||
if (device != null) {
|
||||
config.setGPUDevice(device);
|
||||
}
|
||||
else if (config.getUIType() != null && (config.getUIType().equals(GuiText.type) || config.getUIType().equals(GuiTextOneLine.type))) {
|
||||
System.err.println("SettingsLoader::merge could not find specified GPU");
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.getNbCores() == -1 && cores != null) {
|
||||
config.setNbCores(Integer.parseInt(cores.getValue()));
|
||||
}
|
||||
|
||||
if (config.getMaxAllowedMemory() == -1 && ram != null) {
|
||||
config.setMaxAllowedMemory(Utils.parseNumber(ram.getValue()) / 1000); // internal ram value is in KiB
|
||||
}
|
||||
|
||||
if (config.getMaxRenderTime() == -1 && renderTime != null) {
|
||||
config.setMaxRenderTime(Integer.parseInt(renderTime.getValue()));
|
||||
}
|
||||
|
||||
if (config.isUserHasSpecifiedACacheDir() == false && cacheDir != null) {
|
||||
config.setCacheDir(new File(cacheDir.getValue()));
|
||||
}
|
||||
|
||||
if (config.getUIType() == null && ui != null) {
|
||||
config.setUIType(ui.getValue());
|
||||
}
|
||||
|
||||
if (config.getTheme() == null) {
|
||||
if (this.theme != null && (this.theme.getValue().equals("dark") || this.theme.getValue().equals("light"))) {
|
||||
config.setTheme(this.theme.getValue());
|
||||
}
|
||||
else {
|
||||
config.setTheme("light");
|
||||
}
|
||||
}
|
||||
|
||||
// if the user has invoked the app with --no-systray, then we just overwrite the existing configuration with (boolean)false. If no parameter has been
|
||||
// specified and the settings file contains use-systray=false, then deactivate as well.
|
||||
if (!config.isUseSysTray() || (config.isUseSysTray() && useSysTray != null && useSysTray.getValue().equals("false"))) {
|
||||
config.setUseSysTray(false);
|
||||
}
|
||||
|
||||
if (config.isAutoSignIn() == false && autoSignIn != null) {
|
||||
config.setAutoSignIn(Boolean.parseBoolean(autoSignIn.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
private void initWithDefaults() {
|
||||
Configuration defaultConfigValues = new Configuration(null, null, null);
|
||||
this.login = null;
|
||||
this.password = null;
|
||||
this.proxy = null;
|
||||
this.hostname = null;
|
||||
this.computeMethod = null;
|
||||
this.gpu = null;
|
||||
this.cacheDir = null;
|
||||
this.autoSignIn = null;
|
||||
this.useSysTray = new Option<>(String.valueOf(defaultConfigValues.isUseSysTray()), ARG_NO_SYSTRAY);
|
||||
this.headless = new Option<>(String.valueOf(defaultConfigValues.isHeadless()), ARG_HEADLESS);
|
||||
this.ui = null;
|
||||
this.priority = new Option<>(defaultConfigValues.getPriority(), ARG_PRIORITY); // must be the same default as Configuration
|
||||
this.ram = null;
|
||||
this.renderTime = null;
|
||||
this.theme = null;
|
||||
this.cores = new Option<>(String.valueOf(defaultConfigValues.getNbCores()), ARG_CORES);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return String.format(
|
||||
"SettingsLoader [path=%s, login=%s, password=%s, computeMethod=%s, gpu=%s, cacheDir=%s, theme=%s, priority=%d, autosign=%s, usetray=%s, headless=%s]",
|
||||
path, login, password, computeMethod, gpu, cacheDir, theme, priority, autoSignIn, useSysTray, headless);
|
||||
}
|
||||
}
|
||||
36
src/main/java/com/sheepit/client/ShutdownHook.java
Normal file
36
src/main/java/com/sheepit/client/ShutdownHook.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 ShutdownHook {
|
||||
private Client _client;
|
||||
|
||||
public ShutdownHook(Client client_) {
|
||||
this._client = client_;
|
||||
}
|
||||
|
||||
public void attachShutDownHook() {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override public void run() {
|
||||
_client.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
141
src/main/java/com/sheepit/client/Speedtest.java
Normal file
141
src/main/java/com/sheepit/client/Speedtest.java
Normal file
@@ -0,0 +1,141 @@
|
||||
package com.sheepit.client;
|
||||
|
||||
import com.sheepit.client.datamodel.SpeedTestTarget;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URL;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
public class Speedtest {
|
||||
public static final int PORT = 443;
|
||||
private static final Comparator<SpeedTestTarget> ORDERED = Comparator.comparing(speedTestTarget -> speedTestTarget.getPing().getAverage());
|
||||
|
||||
private Log log;
|
||||
|
||||
public Speedtest(Log log) {
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param urls the urls to the speedtest payloads
|
||||
* @param numberOfResults number of best mirrors to return
|
||||
*
|
||||
* @return An array of the mirrors with the best connection time. The size of the array is determined by <code>numberOfResults</code> or <code>urls.size()</code>
|
||||
* if <code>numberOfResults > urls.size()</code>
|
||||
*/
|
||||
public List<SpeedTestTarget> doSpeedtests(List<String> urls, int numberOfResults) {
|
||||
|
||||
List<SpeedTestTarget> pingResult = (urls
|
||||
.stream()
|
||||
.map(this::measure)
|
||||
.filter(target -> target.getPing().getAverage() > 0)
|
||||
.sorted(ORDERED)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
numberOfResults = Math.min(numberOfResults, urls.size());
|
||||
|
||||
List<SpeedTestTarget> result = new ArrayList<>(numberOfResults);
|
||||
|
||||
int i = 0;
|
||||
while (result.size() < numberOfResults && i < pingResult.size()) {
|
||||
SpeedTestTarget m = pingResult.get(i);
|
||||
try {
|
||||
var speedtestResult = runTimed(() -> speedtest(m.getUrl()));
|
||||
m.setSpeedtest(
|
||||
Math.round(speedtestResult.second / (speedtestResult.first / (double) 1000L)) // number of bytes / time in seconds
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.log.error("Speedtest::doSpeedtests Exception " + e);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
result.add(m);
|
||||
i++;
|
||||
}
|
||||
|
||||
result.sort(Comparator.comparing(SpeedTestTarget::getSpeedtest).reversed());
|
||||
return result;
|
||||
}
|
||||
|
||||
private SpeedTestTarget measure(String mirror) {
|
||||
long pingCount = 12;
|
||||
|
||||
LongStream.Builder streamBuilder = LongStream.builder();
|
||||
|
||||
for (int i = 0; i < pingCount; i++) {
|
||||
try {
|
||||
streamBuilder.add(runTimed(() -> ping(mirror, PORT)).first);
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.log.error("Speedtest::ping Exception " + e);
|
||||
pingCount /= 2;
|
||||
streamBuilder.add(Long.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
var pingStatistics = streamBuilder.build().summaryStatistics();
|
||||
|
||||
return new SpeedTestTarget(mirror, -1, pingStatistics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return both the time it took to complete the task and the result of it
|
||||
* @param task the task whose execution time we want to measure
|
||||
* @param <T> the return value of the task
|
||||
* @return A pair where the first value is the execution time in ms and the second value is the task result
|
||||
* @throws Exception
|
||||
*/
|
||||
private <T> Pair<Long, T> runTimed(Callable<T> task) throws Exception {
|
||||
long start, end;
|
||||
start = System.nanoTime();
|
||||
T callValue = task.call();
|
||||
end = System.nanoTime();
|
||||
return new Pair<>(Duration.ofNanos(end - start).toMillis(), callValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a payload from the given url and returns the number of downloaded bytes
|
||||
* @param url the url pointing at the speedtest file
|
||||
* @return the number of bytes read
|
||||
*/
|
||||
private int speedtest(String url) {
|
||||
try (InputStream stream = new URL(url).openStream()) {
|
||||
return stream.readAllBytes().length;
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
throw new RuntimeException("Invalid speedtest URL: " + url, e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("Unable to execute speedtest to: " + url, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static int ping(String url, int port) {
|
||||
InetAddress mirrorIP = null;
|
||||
try (Socket socket = new Socket()) {
|
||||
mirrorIP = InetAddress.getByName(new URL(url).getHost());
|
||||
SocketAddress socketAddress = new InetSocketAddress(mirrorIP, port);
|
||||
int maxWaitingTime_ms = 3000;
|
||||
socket.connect(socketAddress, maxWaitingTime_ms);
|
||||
}
|
||||
catch (IOException e) {
|
||||
String problemURL = mirrorIP != null ? mirrorIP + " (derived from: " + url + ")" : url;
|
||||
throw new RuntimeException("Unable to connect to " + problemURL, e);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
76
src/main/java/com/sheepit/client/Stats.java
Normal file
76
src/main/java/com/sheepit/client/Stats.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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 Stats {
|
||||
private int remainingFrame;
|
||||
private int creditsEarned;
|
||||
private int creditsEarnedSession;
|
||||
private int renderableProject;
|
||||
private int waitingProject;
|
||||
private int connectedMachine;
|
||||
|
||||
public Stats(int frame, int credits, int creditsSession, int renderables, int waitings, int machines) {
|
||||
remainingFrame = frame;
|
||||
creditsEarned = credits;
|
||||
creditsEarnedSession = creditsSession;
|
||||
renderableProject = renderables;
|
||||
waitingProject = waitings;
|
||||
connectedMachine = machines;
|
||||
}
|
||||
|
||||
public Stats() {
|
||||
remainingFrame = 0;
|
||||
creditsEarned = 0;
|
||||
creditsEarnedSession = 0;
|
||||
renderableProject = 0;
|
||||
waitingProject = 0;
|
||||
connectedMachine = 0;
|
||||
}
|
||||
|
||||
public int getRemainingFrame() {
|
||||
return remainingFrame;
|
||||
}
|
||||
|
||||
public int getCreditsEarnedDuringSession() {
|
||||
return creditsEarnedSession;
|
||||
}
|
||||
|
||||
public int getCreditsEarned() {
|
||||
return creditsEarned;
|
||||
}
|
||||
|
||||
public int getRenderableProject() {
|
||||
return renderableProject;
|
||||
}
|
||||
|
||||
public int getWaitingProject() {
|
||||
return waitingProject;
|
||||
}
|
||||
|
||||
public int getConnectedMachine() {
|
||||
return connectedMachine;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "Stats [remainingFrame=" + remainingFrame + ", creditsEarned=" + creditsEarned + ", creditsEarnedSession=" + creditsEarnedSession
|
||||
+ ", renderableProject=" + renderableProject + ", waitingProject=" + waitingProject + ", connectedMachine=" + connectedMachine + "]";
|
||||
}
|
||||
}
|
||||
40
src/main/java/com/sheepit/client/TransferStats.java
Normal file
40
src/main/java/com/sheepit/client/TransferStats.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.sheepit.client;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/****************
|
||||
* Holds the session traffic statistics. The constructor accepts two parameters:
|
||||
* @long bytes - bytes transferred in the session
|
||||
* @Job seconds - seconds spent transferring the data
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class TransferStats {
|
||||
private long bytes;
|
||||
private long millis;
|
||||
|
||||
public TransferStats() {
|
||||
this.bytes = 0;
|
||||
this.millis = 0;
|
||||
}
|
||||
|
||||
public void calc(long bytes, long millis) {
|
||||
this.bytes += bytes;
|
||||
this.millis += millis;
|
||||
}
|
||||
|
||||
public String getSessionTraffic() {
|
||||
return Utils.formatDataConsumption(this.bytes);
|
||||
}
|
||||
|
||||
public String getAverageSessionSpeed() {
|
||||
try {
|
||||
return Utils.formatDataConsumption((long) (this.bytes / (this.millis / 1000f)));
|
||||
} catch (ArithmeticException e) { // Unlikely, but potential division by zero fallback if first transfer is done in zero millis
|
||||
return Utils.formatDataConsumption((long) (this.bytes / (0.1f)));
|
||||
}
|
||||
}
|
||||
|
||||
public long getRawAverageSessionSpeed() {
|
||||
return this.millis != 0 ? (long) (this.bytes / (this.millis / 1000f)) : 0;
|
||||
}
|
||||
}
|
||||
259
src/main/java/com/sheepit/client/Utils.java
Normal file
259
src/main/java/com/sheepit/client/Utils.java
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 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;
|
||||
|
||||
import com.sheepit.client.Error.ServerCode;
|
||||
import net.lingala.zip4j.ZipFile;
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Utils {
|
||||
public static int unzipFileIntoDirectory(String zipFileName_, String destinationDirectory, char[] password, Log log) {
|
||||
try {
|
||||
ZipFile zipFile = new ZipFile(zipFileName_);
|
||||
// unzipParameters.setIgnoreDateTimeAttributes(true);
|
||||
|
||||
if (password != null && zipFile.isEncrypted()) {
|
||||
zipFile.setPassword(password);
|
||||
}
|
||||
// zipFile.extractAll(destinationDirectory, unzipParameters);
|
||||
zipFile.extractAll(destinationDirectory);
|
||||
}
|
||||
catch (ZipException e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
log.debug("Utils::unzipFileIntoDirectory(" + zipFileName_ + "," + destinationDirectory + ") exception " + e + " stacktrace: " + sw.toString());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static String md5(String path_of_file_) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
InputStream is = Files.newInputStream(Paths.get(path_of_file_));
|
||||
DigestInputStream dis = new DigestInputStream(is, md);
|
||||
byte[] buffer = new byte[8192];
|
||||
while (dis.read(buffer) > 0)
|
||||
; // process the entire file
|
||||
String data = DatatypeConverter.printHexBinary(md.digest()).toLowerCase();
|
||||
dis.close();
|
||||
is.close();
|
||||
return data;
|
||||
}
|
||||
catch (NoSuchAlgorithmException | IOException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static double lastModificationTime(File directory_) {
|
||||
double max = 0.0;
|
||||
if (directory_.isDirectory()) {
|
||||
File[] list = directory_.listFiles();
|
||||
if (list != null) {
|
||||
for (File aFile : list) {
|
||||
double max1 = lastModificationTime(aFile);
|
||||
if (max1 > max) {
|
||||
max = max1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (directory_.isFile()) {
|
||||
return directory_.lastModified();
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
public static ServerCode statusIsOK(Document document_, String rootname_) {
|
||||
if (document_ == null) {
|
||||
return Error.ServerCode.UNKNOWN;
|
||||
}
|
||||
NodeList ns = document_.getElementsByTagName(rootname_);
|
||||
if (ns.getLength() == 0) {
|
||||
return Error.ServerCode.ERROR_NO_ROOT;
|
||||
}
|
||||
Element a_node = (Element) ns.item(0);
|
||||
if (a_node.hasAttribute("status")) {
|
||||
return Error.ServerCode.fromInt(Integer.parseInt(a_node.getAttribute("status")));
|
||||
}
|
||||
return Error.ServerCode.UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will recursively delete a directory
|
||||
*/
|
||||
public static void delete(File file) {
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
String[] files = file.list();
|
||||
if (files != null) {
|
||||
if (files.length != 0) {
|
||||
for (String temp : files) {
|
||||
File fileDelete = new File(file, temp);
|
||||
delete(fileDelete);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
file.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a number string to a number.
|
||||
* Input can be as "32", "10k", "100K", "100G", "1.3G", "0.4T"
|
||||
*/
|
||||
public static long parseNumber(String in) {
|
||||
in = in.trim();
|
||||
in = in.replaceAll(",", ".");
|
||||
try {
|
||||
return Long.parseLong(in);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
}
|
||||
final Matcher m = Pattern.compile("([\\d.,]+)\\s*(\\w)").matcher(in);
|
||||
m.find();
|
||||
int scale = 1;
|
||||
switch (m.group(2).charAt(0)) {
|
||||
case 'T':
|
||||
case 't':
|
||||
scale = 1000 * 1000 * 1000 * 1000;
|
||||
break;
|
||||
case 'G':
|
||||
case 'g':
|
||||
scale = 1000 * 1000 * 1000;
|
||||
break;
|
||||
case 'M':
|
||||
case 'm':
|
||||
scale = 1000 * 1000;
|
||||
break;
|
||||
case 'K':
|
||||
case 'k':
|
||||
scale = 1000;
|
||||
break;
|
||||
}
|
||||
return Math.round(Double.parseDouble(m.group(1)) * scale);
|
||||
}
|
||||
|
||||
public static String humanDuration(Date date) {
|
||||
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
calendar.setTime(date);
|
||||
|
||||
int hours = (calendar.get(Calendar.DAY_OF_MONTH) - 1) * 24 + calendar.get(Calendar.HOUR_OF_DAY);
|
||||
int minutes = calendar.get(Calendar.MINUTE);
|
||||
int seconds = calendar.get(Calendar.SECOND);
|
||||
|
||||
String output = "";
|
||||
if (hours > 0) {
|
||||
output += hours + "h ";
|
||||
}
|
||||
if (minutes > 0) {
|
||||
output += minutes + "min ";
|
||||
}
|
||||
if (seconds > 0) {
|
||||
output += seconds + "s";
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public static boolean noFreeSpaceOnDisk(String destination_, Log log) {
|
||||
try {
|
||||
File file = new File(destination_);
|
||||
for (int i = 0; i < 3; i++) { //We poll repeatedly because getUsableSpace() might just return 0 on busy disk IO
|
||||
long space = file.getUsableSpace();
|
||||
if (space > 512 * 1024) { // at least the same amount as Server.HTTPGetFile
|
||||
return false; // If we are not "full", we are done, no need for additional polling
|
||||
} else if (i < 2) {
|
||||
long time = (long) (Math.random() * (100 - 40 + 1) + 40); //Wait between 40 and 100 milliseconds
|
||||
log.debug("Utils::Not enough free disk space(" + space + ") encountered on try " + i + ", waiting " + time + "ms");
|
||||
Thread.sleep(time);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (SecurityException | InterruptedException e) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String findMimeType(String file) throws IOException {
|
||||
String mimeType = Files.probeContentType(Paths.get(file));
|
||||
if (mimeType == null) {
|
||||
InputStream stream = new BufferedInputStream(new FileInputStream(file));
|
||||
mimeType = URLConnection.guessContentTypeFromStream(stream);
|
||||
}
|
||||
if (mimeType == null) {
|
||||
mimeType = URLConnection.guessContentTypeFromName(file);
|
||||
}
|
||||
|
||||
if (mimeType == null && file.endsWith(".tga")) { // fallback for TGA
|
||||
mimeType = "image/tga";
|
||||
}
|
||||
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
public static String formatDataConsumption(long bytes) {
|
||||
float divider = 0;
|
||||
String suffix = "";
|
||||
|
||||
if (bytes > 1099511627776f) { // 1TB
|
||||
divider = 1099511627776f;
|
||||
suffix = "TB";
|
||||
}
|
||||
else if (bytes > 1073741824) { // 1GB
|
||||
divider = 1073741824;
|
||||
suffix = "GB";
|
||||
}
|
||||
else { // 1MB
|
||||
divider = 1048576;
|
||||
suffix = "MB";
|
||||
}
|
||||
|
||||
return String.format("%.2f%s", (bytes / divider), suffix);
|
||||
}
|
||||
}
|
||||
16
src/main/java/com/sheepit/client/datamodel/CacheFileMD5.java
Normal file
16
src/main/java/com/sheepit/client/datamodel/CacheFileMD5.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import org.simpleframework.xml.ElementList;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Root(strict = false, name = "cache") @Data @ToString public class CacheFileMD5 {
|
||||
|
||||
@ElementList(inline = true) private List<FileMD5> md5s;
|
||||
|
||||
public CacheFileMD5() {
|
||||
}
|
||||
}
|
||||
16
src/main/java/com/sheepit/client/datamodel/FileMD5.java
Normal file
16
src/main/java/com/sheepit/client/datamodel/FileMD5.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import org.simpleframework.xml.Attribute;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
@Root(strict = false, name = "file") @Data @ToString public class FileMD5 {
|
||||
|
||||
@Attribute private String md5;
|
||||
|
||||
@Attribute(required = false) private String action;
|
||||
|
||||
public FileMD5() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.simpleframework.xml.Attribute;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
@Root(strict = false, name = "keepmealive") @ToString public class HeartBeatInfos {
|
||||
@Attribute @Getter private int status;
|
||||
}
|
||||
24
src/main/java/com/sheepit/client/datamodel/JobInfos.java
Normal file
24
src/main/java/com/sheepit/client/datamodel/JobInfos.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.simpleframework.xml.Attribute;
|
||||
import org.simpleframework.xml.Element;
|
||||
import org.simpleframework.xml.ElementList;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Root(strict = false, name = "jobrequest") @ToString public class JobInfos {
|
||||
|
||||
@Attribute @Getter private int status;
|
||||
|
||||
@Element(name = "stats", required = false) @Getter private SessionStats sessionStats;
|
||||
|
||||
@Element(name = "job", required = false) @Getter() private RenderTask renderTask;
|
||||
|
||||
@ElementList(name = "file", inline = true, required = false) @Getter private List<FileMD5> fileMD5s;
|
||||
|
||||
public JobInfos() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.simpleframework.xml.Attribute;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
@Root(strict = false, name = "jobvalidate") @ToString public class JobValidation {
|
||||
|
||||
@Attribute @Getter private int status;
|
||||
|
||||
public JobValidation() {
|
||||
}
|
||||
}
|
||||
36
src/main/java/com/sheepit/client/datamodel/RenderTask.java
Normal file
36
src/main/java/com/sheepit/client/datamodel/RenderTask.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.simpleframework.xml.Attribute;
|
||||
import org.simpleframework.xml.Element;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
@Root(strict = false, name = "job") @ToString public class RenderTask {
|
||||
|
||||
@Attribute(name = "id") @Getter private String id;
|
||||
|
||||
@Attribute(name = "use_gpu") @Getter private int useGpu;
|
||||
|
||||
@Attribute(name = "archive_md5") @Getter private String archive_md5;
|
||||
|
||||
@Attribute(name = "path") @Getter private String path;
|
||||
|
||||
@Attribute(name = "frame") @Getter private String frame;
|
||||
|
||||
@Attribute(name = "synchronous_upload") @Getter private String synchronous_upload;
|
||||
|
||||
@Attribute(name = "validation_url") @Getter private String validationUrl;
|
||||
|
||||
@Attribute(name = "name") @Getter private String name;
|
||||
|
||||
@Attribute(name = "password") @Getter private char[] password;
|
||||
|
||||
@Element(name = "renderer") @Getter private RendererInfos rendererInfos;
|
||||
|
||||
@Element(name = "script", data = true) @Getter private String script;
|
||||
|
||||
public RenderTask() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.simpleframework.xml.Attribute;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
@Root(strict = false, name = "renderer") @ToString public class RendererInfos {
|
||||
|
||||
@Attribute(name = "md5") @Getter private String md5;
|
||||
|
||||
@Attribute(name = "commandline") @Getter private String commandline;
|
||||
|
||||
@Attribute(name = "update_method") @Getter private String update_method;
|
||||
|
||||
public RendererInfos() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.simpleframework.xml.Attribute;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
@Root(strict = false, name = "request") @ToString public class RequestEndPoint {
|
||||
|
||||
@Attribute @Getter private String type;
|
||||
|
||||
@Attribute @Getter private String path;
|
||||
|
||||
@Attribute(name = "max-period", required = false) @Getter private int maxPeriod;
|
||||
|
||||
public RequestEndPoint() {
|
||||
}
|
||||
}
|
||||
36
src/main/java/com/sheepit/client/datamodel/ServerConfig.java
Normal file
36
src/main/java/com/sheepit/client/datamodel/ServerConfig.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.simpleframework.xml.Attribute;
|
||||
import org.simpleframework.xml.ElementList;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Root(strict = false, name = "config") @ToString public class ServerConfig {
|
||||
|
||||
@Attribute @Getter private int status;
|
||||
|
||||
@Attribute(required = false) @Getter private String publickey;
|
||||
|
||||
@ElementList(name = "request", inline = true, required = false) private List<RequestEndPoint> requestEndPoints;
|
||||
|
||||
@Getter @Setter
|
||||
@ElementList(name = "speedtest", required = false) private List<SpeedTestTarget> speedTestTargets;
|
||||
|
||||
public ServerConfig() {
|
||||
}
|
||||
|
||||
public RequestEndPoint getRequestEndPoint(String type) {
|
||||
if (requestEndPoints != null) {
|
||||
for (RequestEndPoint endPoint : requestEndPoints) {
|
||||
if (type.equals(endPoint.getType())) {
|
||||
return endPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
25
src/main/java/com/sheepit/client/datamodel/SessionStats.java
Normal file
25
src/main/java/com/sheepit/client/datamodel/SessionStats.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.simpleframework.xml.Attribute;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
@Root(strict = false, name = "stats") @ToString public class SessionStats {
|
||||
|
||||
@Attribute(name = "credits_session") @Getter private int pointsEarnedOnSession;
|
||||
|
||||
@Attribute(name = "credits_total") @Getter private int pointsEarnedByUser;
|
||||
|
||||
@Attribute(name = "frame_remaining") @Getter private int remainingFrames;
|
||||
|
||||
@Attribute(name = "waiting_project") @Getter private int waitingProjects;
|
||||
|
||||
@Attribute(name = "renderable_project", required = false) @Getter private int renderableProjects;
|
||||
|
||||
@Attribute(name = "connected_machine") @Getter private int connectedMachines;
|
||||
|
||||
public SessionStats() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import org.simpleframework.xml.ElementList;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Root(strict = false, name = "speedtest") @Data @ToString public class SpeedTestResult {
|
||||
|
||||
@ElementList(inline = true) private List<SpeedTestTargetResult> results;
|
||||
|
||||
public SpeedTestResult() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.simpleframework.xml.Attribute;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
import java.util.LongSummaryStatistics;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Root(name = "target")
|
||||
public class SpeedTestTarget {
|
||||
|
||||
@Attribute(name = "url")
|
||||
private String url;
|
||||
private long speedtest;
|
||||
private LongSummaryStatistics ping;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.sheepit.client.datamodel;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import org.simpleframework.xml.Attribute;
|
||||
import org.simpleframework.xml.Root;
|
||||
|
||||
@Root(strict = false, name = "result") @Data @ToString public class SpeedTestTargetResult {
|
||||
|
||||
@Attribute private String target;
|
||||
|
||||
@Attribute private Long speed;
|
||||
|
||||
@Attribute private Integer ping;
|
||||
|
||||
public SpeedTestTargetResult() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2013 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.exception;
|
||||
|
||||
public class FermeException extends Exception {
|
||||
public FermeException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FermeException(String message_) {
|
||||
super(message_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.exception;
|
||||
|
||||
public class FermeExceptionBadResponseFromServer extends FermeException {
|
||||
public FermeExceptionBadResponseFromServer() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FermeExceptionBadResponseFromServer(String message_) {
|
||||
super(message_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.exception;
|
||||
|
||||
public class FermeExceptionNoRendererAvailable extends FermeException {
|
||||
public FermeExceptionNoRendererAvailable() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FermeExceptionNoRendererAvailable(String message_) {
|
||||
super(message_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.exception;
|
||||
|
||||
public class FermeExceptionNoRightToRender extends FermeException {
|
||||
public FermeExceptionNoRightToRender() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FermeExceptionNoRightToRender(String message_) {
|
||||
super(message_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.exception;
|
||||
|
||||
public class FermeExceptionNoSession extends FermeException {
|
||||
public FermeExceptionNoSession() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FermeExceptionNoSession(String message_) {
|
||||
super(message_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.exception;
|
||||
|
||||
public class FermeExceptionNoSpaceLeftOnDevice extends FermeException {
|
||||
public FermeExceptionNoSpaceLeftOnDevice() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FermeExceptionNoSpaceLeftOnDevice(String message_) {
|
||||
super(message_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.exception;
|
||||
|
||||
public class FermeExceptionNoWritePermission extends FermeException {
|
||||
public FermeExceptionNoWritePermission() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FermeExceptionNoWritePermission(String message_) {
|
||||
super(message_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.exception;
|
||||
|
||||
public class FermeExceptionPathInvalid extends FermeException {
|
||||
public FermeExceptionPathInvalid() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FermeExceptionPathInvalid(String message_) {
|
||||
super(message_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.exception;
|
||||
|
||||
public class FermeExceptionServerInMaintenance extends FermeException {
|
||||
public FermeExceptionServerInMaintenance() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FermeExceptionServerInMaintenance(String message_) {
|
||||
super(message_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.exception;
|
||||
|
||||
public class FermeExceptionServerOverloaded extends FermeException {
|
||||
public FermeExceptionServerOverloaded() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FermeExceptionServerOverloaded(String message_) {
|
||||
super(message_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.exception;
|
||||
|
||||
public class FermeExceptionSessionDisabled extends FermeException {
|
||||
public FermeExceptionSessionDisabled() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FermeExceptionSessionDisabled(String message_) {
|
||||
super(message_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.exception;
|
||||
|
||||
/**
|
||||
* Server down (server side error) or unreachable (client side error)
|
||||
*/
|
||||
public class FermeServerDown extends FermeException {
|
||||
public FermeServerDown() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FermeServerDown(String message_) {
|
||||
super(message_);
|
||||
}
|
||||
}
|
||||
96
src/main/java/com/sheepit/client/hardware/cpu/CPU.java
Normal file
96
src/main/java/com/sheepit/client/hardware/cpu/CPU.java
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 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.hardware.cpu;
|
||||
|
||||
public class CPU {
|
||||
public static final int MIN_CORES = Runtime.getRuntime().availableProcessors() > 1 ? 2 : 1;
|
||||
private String name;
|
||||
private String model;
|
||||
private String family;
|
||||
private String arch; // 32 or 64 bits
|
||||
|
||||
public CPU() {
|
||||
this.name = null;
|
||||
this.model = null;
|
||||
this.family = null;
|
||||
this.generateArch();
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String model() {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
public String family() {
|
||||
return this.family;
|
||||
}
|
||||
|
||||
public String arch() {
|
||||
return this.arch;
|
||||
}
|
||||
|
||||
public int cores() {
|
||||
return Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
|
||||
public void setName(String name_) {
|
||||
this.name = name_;
|
||||
}
|
||||
|
||||
public void setModel(String model_) {
|
||||
this.model = model_;
|
||||
}
|
||||
|
||||
public void setFamily(String family_) {
|
||||
this.family = family_;
|
||||
}
|
||||
|
||||
public void setArch(String arch_) {
|
||||
this.arch = arch_;
|
||||
}
|
||||
|
||||
public void generateArch() {
|
||||
String arch = System.getProperty("os.arch").toLowerCase();
|
||||
switch (arch) {
|
||||
case "i386":
|
||||
case "i686":
|
||||
case "x86":
|
||||
this.arch = "32bit";
|
||||
break;
|
||||
case "amd64":
|
||||
case "x86_64":
|
||||
case "x64":
|
||||
case "aarch64":
|
||||
this.arch = "64bit";
|
||||
break;
|
||||
default:
|
||||
this.arch = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean haveData() {
|
||||
return this.name != null && this.model != null && this.family != null && this.arch != null;
|
||||
}
|
||||
|
||||
}
|
||||
97
src/main/java/com/sheepit/client/hardware/gpu/GPU.java
Normal file
97
src/main/java/com/sheepit/client/hardware/gpu/GPU.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 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.hardware.gpu;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sheepit.client.Configuration;
|
||||
import com.sheepit.client.hardware.gpu.nvidia.Nvidia;
|
||||
import com.sheepit.client.hardware.gpu.opencl.OpenCL;
|
||||
import com.sheepit.client.os.OS;
|
||||
import com.sheepit.client.os.Windows;
|
||||
|
||||
public class GPU {
|
||||
public static List<GPUDevice> devices = null;
|
||||
|
||||
public static boolean generate() {
|
||||
devices = new LinkedList<GPUDevice>();
|
||||
List<GPUDevice> gpus = new Nvidia().getGpus();
|
||||
if (gpus != null) {
|
||||
devices.addAll(gpus);
|
||||
}
|
||||
|
||||
OS os = OS.getOS();
|
||||
if (os instanceof Windows) { // opencl detection will crash on Mac (and sometimes on Linux)
|
||||
gpus = new OpenCL().getGpus();
|
||||
if (gpus != null) {
|
||||
devices.addAll(gpus);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<String> listModels() {
|
||||
if (devices == null) {
|
||||
generate();
|
||||
}
|
||||
|
||||
List<String> devs = new LinkedList<String>();
|
||||
for (GPUDevice dev : devices) {
|
||||
devs.add(dev.getModel());
|
||||
}
|
||||
return devs;
|
||||
}
|
||||
|
||||
public static List<GPUDevice> listDevices(Configuration config) {
|
||||
if (devices == null) {
|
||||
if (config.isDetectGPUs()) {
|
||||
generate();
|
||||
}
|
||||
else {
|
||||
devices = new LinkedList<GPUDevice>();
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
public static GPUDevice getGPUDevice(String deviceId) {
|
||||
if (deviceId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (devices == null) {
|
||||
generate();
|
||||
}
|
||||
|
||||
if (devices == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (GPUDevice dev : devices) {
|
||||
if (deviceId.equals(dev.getId()) || deviceId.equals(dev.getOldId())) {
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
89
src/main/java/com/sheepit/client/hardware/gpu/GPUDevice.java
Normal file
89
src/main/java/com/sheepit/client/hardware/gpu/GPUDevice.java
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 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.hardware.gpu;
|
||||
|
||||
import com.sheepit.client.hardware.gpu.nvidia.Nvidia;
|
||||
import com.sheepit.client.hardware.gpu.opencl.OpenCL;
|
||||
|
||||
public class GPUDevice {
|
||||
private String type;
|
||||
private String model;
|
||||
private long memory; // in B
|
||||
|
||||
private String id;
|
||||
|
||||
private String oldId; // for backward compatibility
|
||||
|
||||
public GPUDevice(String type, String model, long ram, String id) {
|
||||
this.type = type;
|
||||
this.model = model;
|
||||
this.memory = ram;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public GPUDevice(String type, String model, long ram, String id, String oldId) {
|
||||
this(type, model, ram, id);
|
||||
this.oldId = oldId;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public long getMemory() {
|
||||
return memory;
|
||||
}
|
||||
|
||||
public void setMemory(long memory) {
|
||||
this.memory = memory;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getOldId() {
|
||||
return oldId;
|
||||
}
|
||||
|
||||
public void setOldId(String id) {
|
||||
this.oldId = id;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "GPUDevice [type=" + type + ", model='" + model + "', memory=" + memory + ", id=" + id + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.sheepit.client.hardware.gpu;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface GPULister {
|
||||
public abstract List<GPUDevice> getGpus();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 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.hardware.gpu.nvidia;
|
||||
|
||||
import com.sun.jna.Library;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
import com.sun.jna.ptr.LongByReference;
|
||||
|
||||
public interface CUDA extends Library {
|
||||
public int cuInit(int flags);
|
||||
|
||||
/*
|
||||
* @return: CUDA_SUCCESS, CUDA_ERROR_DEINITIALIZED, CUDA_ERROR_NOT_INITIALIZED, CUDA_ERROR_INVALID_CONTEXT, CUDA_ERROR_INVALID_VALUE
|
||||
*/
|
||||
public int cuDeviceGetCount(IntByReference count);
|
||||
|
||||
public int cuDeviceGetName(byte[] name, int len, int dev);
|
||||
|
||||
public int cuDeviceGet(IntByReference device, int ordinal);
|
||||
|
||||
public int cuDeviceGetAttribute(IntByReference pi, int attrib, int dev);
|
||||
|
||||
public int cuDeviceTotalMem_v2(LongByReference bytes, int dev);
|
||||
|
||||
public int cuDeviceTotalMem(LongByReference bytes, int dev);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.hardware.gpu.nvidia;
|
||||
|
||||
/**
|
||||
* CUDA Device properties. Taken directly from the online manual:
|
||||
* https://docs.nvidia.com/cuda/cuda-driver-api
|
||||
*/
|
||||
public class CUDeviceAttribute {
|
||||
/**
|
||||
* PCI bus ID of the device
|
||||
*/
|
||||
public static final int CU_DEVICE_ATTRIBUTE_PCI_BUS_ID = 33;
|
||||
|
||||
/**
|
||||
* PCI device ID of the device
|
||||
*/
|
||||
public static final int CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID = 34;
|
||||
|
||||
/**
|
||||
* PCI domain ID of the device
|
||||
*/
|
||||
public static final int CU_DEVICE_ATTRIBUTE_PCI_DOMAIN_ID = 50;
|
||||
}
|
||||
@@ -0,0 +1,518 @@
|
||||
/*
|
||||
* JCuda - Java bindings for NVIDIA CUDA driver and runtime API
|
||||
*
|
||||
* Copyright (c) 2009-2012 Marco Hutter - http://www.jcuda.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//package jcuda.driver;
|
||||
package com.sheepit.client.hardware.gpu.nvidia;
|
||||
|
||||
/**
|
||||
* Error codes.<br />
|
||||
* <br />
|
||||
* Most comments are taken from the CUDA reference manual.
|
||||
*/
|
||||
public class CUresult {
|
||||
/**
|
||||
* The API call returned with no errors. In the case of query calls, this
|
||||
* can also mean that the operation being queried is complete (see
|
||||
* ::cuEventQuery() and ::cuStreamQuery()).
|
||||
*/
|
||||
public static final int CUDA_SUCCESS = 0;
|
||||
|
||||
/**
|
||||
* This indicates that one or more of the parameters passed to the API call
|
||||
* is not within an acceptable range of values.
|
||||
*/
|
||||
public static final int CUDA_ERROR_INVALID_VALUE = 1;
|
||||
|
||||
/**
|
||||
* The API call failed because it was unable to allocate enough memory to
|
||||
* perform the requested operation.
|
||||
*/
|
||||
public static final int CUDA_ERROR_OUT_OF_MEMORY = 2;
|
||||
|
||||
/**
|
||||
* This indicates that the CUDA driver has not been initialized with
|
||||
* ::cuInit() or that initialization has failed.
|
||||
*/
|
||||
public static final int CUDA_ERROR_NOT_INITIALIZED = 3;
|
||||
|
||||
/**
|
||||
* This indicates that the CUDA driver is in the process of shutting down.
|
||||
*/
|
||||
public static final int CUDA_ERROR_DEINITIALIZED = 4;
|
||||
|
||||
/**
|
||||
* This indicates profiling APIs are called while application is running
|
||||
* in visual profiler mode.
|
||||
*/
|
||||
public static final int CUDA_ERROR_PROFILER_DISABLED = 5;
|
||||
|
||||
/**
|
||||
* This indicates profiling has not been initialized for this context.
|
||||
* Call cuProfilerInitialize() to resolve this.
|
||||
*
|
||||
* @deprecated This error return is deprecated as of CUDA 5.0.
|
||||
* It is no longer an error to attempt to enable/disable the
|
||||
* profiling via ::cuProfilerStart or ::cuProfilerStop without
|
||||
* initialization.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int CUDA_ERROR_PROFILER_NOT_INITIALIZED = 6;
|
||||
|
||||
/**
|
||||
* This indicates profiler has already been started and probably
|
||||
* cuProfilerStart() is incorrectly called.
|
||||
*
|
||||
* @deprecated This error return is deprecated as of CUDA 5.0.
|
||||
* It is no longer an error to call cuProfilerStart() when
|
||||
* profiling is already enabled.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int CUDA_ERROR_PROFILER_ALREADY_STARTED = 7;
|
||||
|
||||
/**
|
||||
* This indicates profiler has already been stopped and probably
|
||||
* cuProfilerStop() is incorrectly called.
|
||||
*
|
||||
* @deprecated This error return is deprecated as of CUDA 5.0.
|
||||
* It is no longer an error to call cuProfilerStop() when
|
||||
* profiling is already disabled.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int CUDA_ERROR_PROFILER_ALREADY_STOPPED = 8;
|
||||
|
||||
/**
|
||||
* This indicates that no CUDA-capable devices were detected by the installed
|
||||
* CUDA driver.
|
||||
*/
|
||||
public static final int CUDA_ERROR_NO_DEVICE = 100;
|
||||
|
||||
/**
|
||||
* This indicates that the device ordinal supplied by the user does not
|
||||
* correspond to a valid CUDA device.
|
||||
*/
|
||||
public static final int CUDA_ERROR_INVALID_DEVICE = 101;
|
||||
|
||||
/**
|
||||
* This indicates that the device kernel image is invalid. This can also
|
||||
* indicate an invalid CUDA module.
|
||||
*/
|
||||
public static final int CUDA_ERROR_INVALID_IMAGE = 200;
|
||||
|
||||
/**
|
||||
* This most frequently indicates that there is no context bound to the
|
||||
* current thread. This can also be returned if the context passed to an
|
||||
* API call is not a valid handle (such as a context that has had
|
||||
* ::cuCtxDestroy() invoked on it). This can also be returned if a user
|
||||
* mixes different API versions (i.e. 3010 context with 3020 API calls).
|
||||
* See ::cuCtxGetApiVersion() for more details.
|
||||
*/
|
||||
public static final int CUDA_ERROR_INVALID_CONTEXT = 201;
|
||||
|
||||
/**
|
||||
* This indicated that the context being supplied as a parameter to the
|
||||
* API call was already the active context.
|
||||
* \deprecated
|
||||
* This error return is deprecated as of CUDA 3.2. It is no longer an
|
||||
* error to attempt to push the active context via ::cuCtxPushCurrent().
|
||||
*/
|
||||
public static final int CUDA_ERROR_CONTEXT_ALREADY_CURRENT = 202;
|
||||
|
||||
/**
|
||||
* This indicates that a map or register operation has failed.
|
||||
*/
|
||||
public static final int CUDA_ERROR_MAP_FAILED = 205;
|
||||
|
||||
/**
|
||||
* This indicates that an unmap or unregister operation has failed.
|
||||
*/
|
||||
public static final int CUDA_ERROR_UNMAP_FAILED = 206;
|
||||
|
||||
/**
|
||||
* This indicates that the specified array is currently mapped and thus
|
||||
* cannot be destroyed.
|
||||
*/
|
||||
public static final int CUDA_ERROR_ARRAY_IS_MAPPED = 207;
|
||||
|
||||
/**
|
||||
* This indicates that the resource is already mapped.
|
||||
*/
|
||||
public static final int CUDA_ERROR_ALREADY_MAPPED = 208;
|
||||
|
||||
/**
|
||||
* This indicates that there is no kernel image available that is suitable
|
||||
* for the device. This can occur when a user specifies code generation
|
||||
* options for a particular CUDA source file that do not include the
|
||||
* corresponding device configuration.
|
||||
*/
|
||||
public static final int CUDA_ERROR_NO_BINARY_FOR_GPU = 209;
|
||||
|
||||
/**
|
||||
* This indicates that a resource has already been acquired.
|
||||
*/
|
||||
public static final int CUDA_ERROR_ALREADY_ACQUIRED = 210;
|
||||
|
||||
/**
|
||||
* This indicates that a resource is not mapped.
|
||||
*/
|
||||
public static final int CUDA_ERROR_NOT_MAPPED = 211;
|
||||
|
||||
/**
|
||||
* This indicates that a mapped resource is not available for access as an
|
||||
* array.
|
||||
*/
|
||||
public static final int CUDA_ERROR_NOT_MAPPED_AS_ARRAY = 212;
|
||||
|
||||
/**
|
||||
* This indicates that a mapped resource is not available for access as a
|
||||
* pointer.
|
||||
*/
|
||||
public static final int CUDA_ERROR_NOT_MAPPED_AS_POINTER = 213;
|
||||
|
||||
/**
|
||||
* This indicates that an uncorrectable ECC error was detected during
|
||||
* execution.
|
||||
*/
|
||||
public static final int CUDA_ERROR_ECC_UNCORRECTABLE = 214;
|
||||
|
||||
/**
|
||||
* This indicates that the ::CUlimit passed to the API call is not
|
||||
* supported by the active device.
|
||||
*/
|
||||
public static final int CUDA_ERROR_UNSUPPORTED_LIMIT = 215;
|
||||
|
||||
/**
|
||||
* This indicates that the ::CUcontext passed to the API call can
|
||||
* only be bound to a single CPU thread at a time but is already
|
||||
* bound to a CPU thread.
|
||||
*/
|
||||
public static final int CUDA_ERROR_CONTEXT_ALREADY_IN_USE = 216;
|
||||
|
||||
/**
|
||||
* This indicates that peer access is not supported across the given
|
||||
* devices.
|
||||
*/
|
||||
public static final int CUDA_ERROR_PEER_ACCESS_UNSUPPORTED = 217;
|
||||
|
||||
/**
|
||||
* This indicates that the device kernel source is invalid.
|
||||
*/
|
||||
public static final int CUDA_ERROR_INVALID_SOURCE = 300;
|
||||
|
||||
/**
|
||||
* This indicates that the file specified was not found.
|
||||
*/
|
||||
public static final int CUDA_ERROR_FILE_NOT_FOUND = 301;
|
||||
|
||||
/**
|
||||
* This indicates that a link to a shared object failed to resolve.
|
||||
*/
|
||||
public static final int CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND = 302;
|
||||
|
||||
/**
|
||||
* This indicates that initialization of a shared object failed.
|
||||
*/
|
||||
public static final int CUDA_ERROR_SHARED_OBJECT_INIT_FAILED = 303;
|
||||
|
||||
/**
|
||||
* This indicates that an OS call failed.
|
||||
*/
|
||||
public static final int CUDA_ERROR_OPERATING_SYSTEM = 304;
|
||||
|
||||
/**
|
||||
* This indicates that a resource handle passed to the API call was not
|
||||
* valid. Resource handles are opaque types like ::CUstream and ::CUevent.
|
||||
*/
|
||||
public static final int CUDA_ERROR_INVALID_HANDLE = 400;
|
||||
|
||||
/**
|
||||
* This indicates that a named symbol was not found. Examples of symbols
|
||||
* are global/constant variable names, texture names, and surface names.
|
||||
*/
|
||||
public static final int CUDA_ERROR_NOT_FOUND = 500;
|
||||
|
||||
/**
|
||||
* This indicates that asynchronous operations issued previously have not
|
||||
* completed yet. This result is not actually an error, but must be indicated
|
||||
* differently than ::CUDA_SUCCESS (which indicates completion). Calls that
|
||||
* may return this value include ::cuEventQuery() and ::cuStreamQuery().
|
||||
*/
|
||||
public static final int CUDA_ERROR_NOT_READY = 600;
|
||||
|
||||
/**
|
||||
* An exception occurred on the device while executing a kernel. Common
|
||||
* causes include dereferencing an invalid device pointer and accessing
|
||||
* out of bounds shared memory. The context cannot be used, so it must
|
||||
* be destroyed (and a new one should be created). All existing device
|
||||
* memory allocations from this context are invalid and must be
|
||||
* reconstructed if the program is to continue using CUDA.
|
||||
*/
|
||||
public static final int CUDA_ERROR_LAUNCH_FAILED = 700;
|
||||
|
||||
/**
|
||||
* This indicates that a launch did not occur because it did not have
|
||||
* appropriate resources. This error usually indicates that the user has
|
||||
* attempted to pass too many arguments to the device kernel, or the
|
||||
* kernel launch specifies too many threads for the kernel's register
|
||||
* count. Passing arguments of the wrong size (i.e. a 64-bit pointer
|
||||
* when a 32-bit int is expected) is equivalent to passing too many
|
||||
* arguments and can also result in this error.
|
||||
*/
|
||||
public static final int CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES = 701;
|
||||
|
||||
/**
|
||||
* This indicates that the device kernel took too long to execute. This can
|
||||
* only occur if timeouts are enabled - see the device attribute
|
||||
* ::CU_DEVICE_ATTRIBUTE_KERNEL_EXEC_TIMEOUT for more information. The
|
||||
* context cannot be used (and must be destroyed similar to
|
||||
* ::CUDA_ERROR_LAUNCH_FAILED). All existing device memory allocations from
|
||||
* this context are invalid and must be reconstructed if the program is to
|
||||
* continue using CUDA.
|
||||
*/
|
||||
public static final int CUDA_ERROR_LAUNCH_TIMEOUT = 702;
|
||||
|
||||
/**
|
||||
* This error indicates a kernel launch that uses an incompatible texturing
|
||||
* mode.
|
||||
*/
|
||||
public static final int CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING = 703;
|
||||
|
||||
/**
|
||||
* This error indicates that a call to ::cuCtxEnablePeerAccess() is
|
||||
* trying to re-enable peer access to a context which has already
|
||||
* had peer access to it enabled.
|
||||
*/
|
||||
public static final int CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED = 704;
|
||||
|
||||
/**
|
||||
* This error indicates that a call to ::cuMemPeerRegister is trying to
|
||||
* register memory from a context which has not had peer access
|
||||
* enabled yet via ::cuCtxEnablePeerAccess(), or that
|
||||
* ::cuCtxDisablePeerAccess() is trying to disable peer access
|
||||
* which has not been enabled yet.
|
||||
*/
|
||||
public static final int CUDA_ERROR_PEER_ACCESS_NOT_ENABLED = 705;
|
||||
|
||||
/**
|
||||
* This error indicates that a call to ::cuMemPeerRegister is trying to
|
||||
* register already-registered memory.
|
||||
*
|
||||
* @deprecated This value has been added in CUDA 4.0 RC,
|
||||
* and removed in CUDA 4.0 RC2
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int CUDA_ERROR_PEER_MEMORY_ALREADY_REGISTERED = 706;
|
||||
|
||||
/**
|
||||
* This error indicates that a call to ::cuMemPeerUnregister is trying to
|
||||
* unregister memory that has not been registered.
|
||||
*
|
||||
* @deprecated This value has been added in CUDA 4.0 RC,
|
||||
* and removed in CUDA 4.0 RC2
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int CUDA_ERROR_PEER_MEMORY_NOT_REGISTERED = 707;
|
||||
|
||||
/**
|
||||
* This error indicates that ::cuCtxCreate was called with the flag
|
||||
* ::CU_CTX_PRIMARY on a device which already has initialized its
|
||||
* primary context.
|
||||
*/
|
||||
public static final int CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE = 708;
|
||||
|
||||
/**
|
||||
* This error indicates that the context current to the calling thread
|
||||
* has been destroyed using ::cuCtxDestroy, or is a primary context which
|
||||
* has not yet been initialized.
|
||||
*/
|
||||
public static final int CUDA_ERROR_CONTEXT_IS_DESTROYED = 709;
|
||||
|
||||
/**
|
||||
* A device-side assert triggered during kernel execution. The context
|
||||
* cannot be used anymore, and must be destroyed. All existing device
|
||||
* memory allocations from this context are invalid and must be
|
||||
* reconstructed if the program is to continue using CUDA.
|
||||
*/
|
||||
public static final int CUDA_ERROR_ASSERT = 710;
|
||||
|
||||
/**
|
||||
* This error indicates that the hardware resources required to enable
|
||||
* peer access have been exhausted for one or more of the devices
|
||||
* passed to ::cuCtxEnablePeerAccess().
|
||||
*/
|
||||
public static final int CUDA_ERROR_TOO_MANY_PEERS = 711;
|
||||
|
||||
/**
|
||||
* This error indicates that the memory range passed to ::cuMemHostRegister()
|
||||
* has already been registered.
|
||||
*/
|
||||
public static final int CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED = 712;
|
||||
|
||||
/**
|
||||
* This error indicates that the pointer passed to ::cuMemHostUnregister()
|
||||
* does not correspond to any currently registered memory region.
|
||||
*/
|
||||
public static final int CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED = 713;
|
||||
|
||||
/**
|
||||
* This error indicates that the attempted operation is not permitted.
|
||||
*/
|
||||
public static final int CUDA_ERROR_NOT_PERMITTED = 800;
|
||||
|
||||
/**
|
||||
* This error indicates that the attempted operation is not supported
|
||||
* on the current system or device.
|
||||
*/
|
||||
public static final int CUDA_ERROR_NOT_SUPPORTED = 801;
|
||||
|
||||
/**
|
||||
* This indicates that an unknown internal error has occurred.
|
||||
*/
|
||||
public static final int CUDA_ERROR_UNKNOWN = 999;
|
||||
|
||||
/**
|
||||
* Returns the String identifying the given CUresult
|
||||
*
|
||||
* @param result The CUresult value
|
||||
* @return The String identifying the given CUresult
|
||||
*/
|
||||
public static String stringFor(int result) {
|
||||
switch (result) {
|
||||
case CUDA_SUCCESS:
|
||||
return "CUDA_SUCCESS";
|
||||
case CUDA_ERROR_INVALID_VALUE:
|
||||
return "CUDA_ERROR_INVALID_VALUE";
|
||||
case CUDA_ERROR_OUT_OF_MEMORY:
|
||||
return "CUDA_ERROR_OUT_OF_MEMORY";
|
||||
case CUDA_ERROR_NOT_INITIALIZED:
|
||||
return "CUDA_ERROR_NOT_INITIALIZED";
|
||||
case CUDA_ERROR_DEINITIALIZED:
|
||||
return "CUDA_ERROR_DEINITIALIZED";
|
||||
case CUDA_ERROR_PROFILER_DISABLED:
|
||||
return "CUDA_ERROR_PROFILER_DISABLED";
|
||||
case CUDA_ERROR_PROFILER_NOT_INITIALIZED:
|
||||
return "CUDA_ERROR_PROFILER_NOT_INITIALIZED";
|
||||
case CUDA_ERROR_PROFILER_ALREADY_STARTED:
|
||||
return "CUDA_ERROR_PROFILER_ALREADY_STARTED";
|
||||
case CUDA_ERROR_PROFILER_ALREADY_STOPPED:
|
||||
return "CUDA_ERROR_PROFILER_ALREADY_STOPPED";
|
||||
case CUDA_ERROR_NO_DEVICE:
|
||||
return "CUDA_ERROR_NO_DEVICE";
|
||||
case CUDA_ERROR_INVALID_DEVICE:
|
||||
return "CUDA_ERROR_INVALID_DEVICE";
|
||||
case CUDA_ERROR_INVALID_IMAGE:
|
||||
return "CUDA_ERROR_INVALID_IMAGE";
|
||||
case CUDA_ERROR_INVALID_CONTEXT:
|
||||
return "CUDA_ERROR_INVALID_CONTEXT";
|
||||
case CUDA_ERROR_CONTEXT_ALREADY_CURRENT:
|
||||
return "CUDA_ERROR_CONTEXT_ALREADY_CURRENT";
|
||||
case CUDA_ERROR_MAP_FAILED:
|
||||
return "CUDA_ERROR_MAP_FAILED";
|
||||
case CUDA_ERROR_UNMAP_FAILED:
|
||||
return "CUDA_ERROR_UNMAP_FAILED";
|
||||
case CUDA_ERROR_ARRAY_IS_MAPPED:
|
||||
return "CUDA_ERROR_ARRAY_IS_MAPPED";
|
||||
case CUDA_ERROR_ALREADY_MAPPED:
|
||||
return "CUDA_ERROR_ALREADY_MAPPED";
|
||||
case CUDA_ERROR_NO_BINARY_FOR_GPU:
|
||||
return "CUDA_ERROR_NO_BINARY_FOR_GPU";
|
||||
case CUDA_ERROR_ALREADY_ACQUIRED:
|
||||
return "CUDA_ERROR_ALREADY_ACQUIRED";
|
||||
case CUDA_ERROR_NOT_MAPPED:
|
||||
return "CUDA_ERROR_NOT_MAPPED";
|
||||
case CUDA_ERROR_NOT_MAPPED_AS_ARRAY:
|
||||
return "CUDA_ERROR_NOT_MAPPED_AS_ARRAY";
|
||||
case CUDA_ERROR_NOT_MAPPED_AS_POINTER:
|
||||
return "CUDA_ERROR_NOT_MAPPED_AS_POINTER";
|
||||
case CUDA_ERROR_ECC_UNCORRECTABLE:
|
||||
return "CUDA_ERROR_ECC_UNCORRECTABLE";
|
||||
case CUDA_ERROR_UNSUPPORTED_LIMIT:
|
||||
return "CUDA_ERROR_UNSUPPORTED_LIMIT";
|
||||
case CUDA_ERROR_CONTEXT_ALREADY_IN_USE:
|
||||
return "CUDA_ERROR_CONTEXT_ALREADY_IN_USE";
|
||||
case CUDA_ERROR_PEER_ACCESS_UNSUPPORTED:
|
||||
return "CUDA_ERROR_PEER_ACCESS_UNSUPPORTED";
|
||||
case CUDA_ERROR_INVALID_SOURCE:
|
||||
return "CUDA_ERROR_INVALID_SOURCE";
|
||||
case CUDA_ERROR_FILE_NOT_FOUND:
|
||||
return "CUDA_ERROR_FILE_NOT_FOUND";
|
||||
case CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND:
|
||||
return "CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND";
|
||||
case CUDA_ERROR_SHARED_OBJECT_INIT_FAILED:
|
||||
return "CUDA_ERROR_SHARED_OBJECT_INIT_FAILED";
|
||||
case CUDA_ERROR_OPERATING_SYSTEM:
|
||||
return "CUDA_ERROR_OPERATING_SYSTEM";
|
||||
case CUDA_ERROR_INVALID_HANDLE:
|
||||
return "CUDA_ERROR_INVALID_HANDLE";
|
||||
case CUDA_ERROR_NOT_FOUND:
|
||||
return "CUDA_ERROR_NOT_FOUND";
|
||||
case CUDA_ERROR_NOT_READY:
|
||||
return "CUDA_ERROR_NOT_READY";
|
||||
case CUDA_ERROR_LAUNCH_FAILED:
|
||||
return "CUDA_ERROR_LAUNCH_FAILED";
|
||||
case CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES:
|
||||
return "CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES";
|
||||
case CUDA_ERROR_LAUNCH_TIMEOUT:
|
||||
return "CUDA_ERROR_LAUNCH_TIMEOUT";
|
||||
case CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING:
|
||||
return "CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING";
|
||||
case CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED:
|
||||
return "CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED";
|
||||
case CUDA_ERROR_PEER_ACCESS_NOT_ENABLED:
|
||||
return "CUDA_ERROR_PEER_ACCESS_NOT_ENABLED";
|
||||
case CUDA_ERROR_PEER_MEMORY_ALREADY_REGISTERED:
|
||||
return "CUDA_ERROR_PEER_MEMORY_ALREADY_REGISTERED";
|
||||
case CUDA_ERROR_PEER_MEMORY_NOT_REGISTERED:
|
||||
return "CUDA_ERROR_PEER_MEMORY_NOT_REGISTERED";
|
||||
case CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE:
|
||||
return "CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE";
|
||||
case CUDA_ERROR_CONTEXT_IS_DESTROYED:
|
||||
return "CUDA_ERROR_CONTEXT_IS_DESTROYED";
|
||||
case CUDA_ERROR_ASSERT:
|
||||
return "CUDA_ERROR_ASSERT";
|
||||
case CUDA_ERROR_TOO_MANY_PEERS:
|
||||
return "CUDA_ERROR_TOO_MANY_PEERS";
|
||||
case CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED:
|
||||
return "CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED";
|
||||
case CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED:
|
||||
return "CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED";
|
||||
case CUDA_ERROR_NOT_PERMITTED:
|
||||
return "CUDA_ERROR_NOT_PERMITTED";
|
||||
case CUDA_ERROR_NOT_SUPPORTED:
|
||||
return "CUDA_ERROR_NOT_SUPPORTED";
|
||||
case CUDA_ERROR_UNKNOWN:
|
||||
return "CUDA_ERROR_UNKNOWN";
|
||||
}
|
||||
return "INVALID CUresult: " + result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor to prevent instantiation.
|
||||
*/
|
||||
private CUresult() {
|
||||
}
|
||||
|
||||
}
|
||||
127
src/main/java/com/sheepit/client/hardware/gpu/nvidia/Nvidia.java
Normal file
127
src/main/java/com/sheepit/client/hardware/gpu/nvidia/Nvidia.java
Normal file
@@ -0,0 +1,127 @@
|
||||
package com.sheepit.client.hardware.gpu.nvidia;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sheepit.client.hardware.gpu.GPUDevice;
|
||||
import com.sheepit.client.hardware.gpu.GPULister;
|
||||
import com.sheepit.client.os.OS;
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
import com.sun.jna.ptr.LongByReference;
|
||||
|
||||
public class Nvidia implements GPULister {
|
||||
public static String TYPE = "CUDA";
|
||||
|
||||
@Override public List<GPUDevice> getGpus() {
|
||||
OS os = OS.getOS();
|
||||
String path = os.getCUDALib();
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
CUDA cudalib = null;
|
||||
try {
|
||||
cudalib = (CUDA) Native.load(path, CUDA.class);
|
||||
}
|
||||
catch (java.lang.UnsatisfiedLinkError e) {
|
||||
return null;
|
||||
}
|
||||
catch (java.lang.ExceptionInInitializerError e) {
|
||||
System.out.println("Nvidia::getGpus ExceptionInInitializerError " + e);
|
||||
return null;
|
||||
}
|
||||
catch (Exception e) {
|
||||
System.out.println("Nvidia::getGpus generic exception " + e);
|
||||
return null;
|
||||
}
|
||||
|
||||
int result = CUresult.CUDA_ERROR_UNKNOWN;
|
||||
|
||||
result = cudalib.cuInit(0);
|
||||
if (result != CUresult.CUDA_SUCCESS) {
|
||||
System.out.println("Nvidia::getGpus cuInit failed (ret: " + result + ")");
|
||||
if (result == CUresult.CUDA_ERROR_UNKNOWN) {
|
||||
System.out.println("If you are running Linux, this error is usually due to nvidia kernel module 'nvidia_uvm' not loaded.");
|
||||
System.out.println("Relaunch the application as root or load the module.");
|
||||
System.out.println("Most of time it does fix the issue.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (result == CUresult.CUDA_ERROR_NO_DEVICE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IntByReference count = new IntByReference();
|
||||
result = cudalib.cuDeviceGetCount(count);
|
||||
|
||||
if (result != CUresult.CUDA_SUCCESS) {
|
||||
System.out.println("Nvidia::getGpus cuDeviceGetCount failed (ret: " + CUresult.stringFor(result) + ")");
|
||||
return null;
|
||||
}
|
||||
|
||||
List<GPUDevice> devices = new ArrayList<>(count.getValue());
|
||||
|
||||
for (int num = 0; num < count.getValue(); num++) {
|
||||
IntByReference aDevice = new IntByReference();
|
||||
|
||||
result = cudalib.cuDeviceGet(aDevice, num);
|
||||
if (result != CUresult.CUDA_SUCCESS) {
|
||||
System.out.println("Nvidia::getGpus cuDeviceGet failed (ret: " + CUresult.stringFor(result) + ")");
|
||||
continue;
|
||||
}
|
||||
|
||||
IntByReference pciDomainId = new IntByReference();
|
||||
IntByReference pciBusId = new IntByReference();
|
||||
IntByReference pciDeviceId = new IntByReference();
|
||||
result = cudalib.cuDeviceGetAttribute(pciDomainId, CUDeviceAttribute.CU_DEVICE_ATTRIBUTE_PCI_DOMAIN_ID, aDevice.getValue());
|
||||
if (result != CUresult.CUDA_SUCCESS) {
|
||||
System.out
|
||||
.println("Nvidia::getGpus cuDeviceGetAttribute for CU_DEVICE_ATTRIBUTE_PCI_DOMAIN_ID failed (ret: " + CUresult.stringFor(result) + ")");
|
||||
continue;
|
||||
}
|
||||
result = cudalib.cuDeviceGetAttribute(pciBusId, CUDeviceAttribute.CU_DEVICE_ATTRIBUTE_PCI_BUS_ID, aDevice.getValue());
|
||||
if (result != CUresult.CUDA_SUCCESS) {
|
||||
System.out.println("Nvidia::getGpus cuDeviceGetAttribute for CU_DEVICE_ATTRIBUTE_PCI_BUS_ID failed (ret: " + CUresult.stringFor(result) + ")");
|
||||
continue;
|
||||
}
|
||||
result = cudalib.cuDeviceGetAttribute(pciDeviceId, CUDeviceAttribute.CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID, aDevice.getValue());
|
||||
if (result != CUresult.CUDA_SUCCESS) {
|
||||
System.out
|
||||
.println("Nvidia::getGpus cuDeviceGetAttribute for CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID failed (ret: " + CUresult.stringFor(result) + ")");
|
||||
continue;
|
||||
}
|
||||
|
||||
byte name[] = new byte[256];
|
||||
|
||||
result = cudalib.cuDeviceGetName(name, 256, num);
|
||||
if (result != CUresult.CUDA_SUCCESS) {
|
||||
System.out.println("Nvidia::getGpus cuDeviceGetName failed (ret: " + CUresult.stringFor(result) + ")");
|
||||
continue;
|
||||
}
|
||||
|
||||
LongByReference ram = new LongByReference();
|
||||
try {
|
||||
result = cudalib.cuDeviceTotalMem_v2(ram, num);
|
||||
}
|
||||
catch (UnsatisfiedLinkError e) {
|
||||
// fall back to old function
|
||||
result = cudalib.cuDeviceTotalMem(ram, num);
|
||||
}
|
||||
|
||||
if (result != CUresult.CUDA_SUCCESS) {
|
||||
System.out.println("Nvidia::getGpus cuDeviceTotalMem failed (ret: " + CUresult.stringFor(result) + ")");
|
||||
return null;
|
||||
}
|
||||
|
||||
String blenderId = String
|
||||
.format("CUDA_%s_%04x:%02x:%02x", new String(name).trim(), pciDomainId.getValue(), pciBusId.getValue(), pciDeviceId.getValue());
|
||||
GPUDevice gpu = new GPUDevice(TYPE, new String(name).trim(), ram.getValue(), blenderId);
|
||||
// for backward compatibility generate a CUDA_N id
|
||||
gpu.setOldId(TYPE + "_" + num);
|
||||
devices.add(gpu);
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
}
|
||||
175
src/main/java/com/sheepit/client/hardware/gpu/opencl/OpenCL.java
Normal file
175
src/main/java/com/sheepit/client/hardware/gpu/opencl/OpenCL.java
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 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.hardware.gpu.opencl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sheepit.client.hardware.gpu.GPUDevice;
|
||||
import com.sheepit.client.hardware.gpu.GPULister;
|
||||
import com.sheepit.client.hardware.gpu.opencl.OpenCLLib.CLDeviceId;
|
||||
import com.sheepit.client.hardware.gpu.opencl.OpenCLLib.CLPlatformId;
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
|
||||
public class OpenCL implements GPULister {
|
||||
public static String TYPE = "OPENCL";
|
||||
|
||||
@Override public List<GPUDevice> getGpus() {
|
||||
OpenCLLib lib = null;
|
||||
|
||||
String path = "OpenCL";
|
||||
try {
|
||||
lib = (OpenCLLib) Native.load(path, OpenCLLib.class);
|
||||
}
|
||||
catch (java.lang.UnsatisfiedLinkError e) {
|
||||
System.out.println("OpenCL::getGpus failed(A) to load OpenCL lib (path: " + path + ")");
|
||||
return null;
|
||||
}
|
||||
catch (java.lang.ExceptionInInitializerError e) {
|
||||
System.out.println("OpenCL::getGpus failed(B) ExceptionInInitializerError " + e);
|
||||
return null;
|
||||
}
|
||||
catch (Exception e) {
|
||||
System.out.println("OpenCL::getGpus failed(C) generic exception " + e);
|
||||
return null;
|
||||
}
|
||||
|
||||
int status = -1;
|
||||
|
||||
// get the number of platform
|
||||
IntByReference number_platforms = new IntByReference();
|
||||
|
||||
status = lib.clGetPlatformIDs(0, null, number_platforms);
|
||||
if (status != OpenCLLib.CL_SUCCESS) {
|
||||
System.out.println("OpenCL::getGpus failed(D) status: " + status);
|
||||
return null;
|
||||
}
|
||||
|
||||
// now we can create the platforms
|
||||
|
||||
final OpenCLLib.CLPlatformId.ByReference e6ref = new OpenCLLib.CLPlatformId.ByReference();
|
||||
OpenCLLib.CLPlatformId.ByReference[] plateforms = (OpenCLLib.CLPlatformId.ByReference[]) e6ref.toArray(number_platforms.getValue());
|
||||
|
||||
status = lib.clGetPlatformIDs(number_platforms.getValue(), plateforms, null);
|
||||
if (status != OpenCLLib.CL_SUCCESS) {
|
||||
System.out.println("OpenCL::getGpus failed(E) status: " + status);
|
||||
return null;
|
||||
}
|
||||
|
||||
List<GPUDevice> available_devices = new ArrayList<GPUDevice>(1);
|
||||
// Devices are numbered consecutively across platforms.
|
||||
int id = 0;
|
||||
for (int i = 0; i < number_platforms.getValue(); i++) {
|
||||
// get number of devices in platform
|
||||
IntByReference device_count = new IntByReference();
|
||||
|
||||
status = lib.clGetDeviceIDs(plateforms[i], OpenCLLib.CL_DEVICE_TYPE_GPU, 0, null, device_count);
|
||||
if (status == OpenCLLib.CL_DEVICE_NOT_FOUND) {
|
||||
System.out.println("OpenCL::getGpus no device found on plateforms[" + i + "]");
|
||||
continue;
|
||||
}
|
||||
if (status != OpenCLLib.CL_SUCCESS) {
|
||||
System.out.println("OpenCL::getGpus failed(F) status: " + status);
|
||||
return null;
|
||||
}
|
||||
|
||||
final OpenCLLib.CLDeviceId.ByReference e6ref4 = new OpenCLLib.CLDeviceId.ByReference();
|
||||
|
||||
OpenCLLib.CLDeviceId.ByReference[] devices = (OpenCLLib.CLDeviceId.ByReference[]) e6ref4.toArray(device_count.getValue());
|
||||
|
||||
status = lib.clGetDeviceIDs(plateforms[i], OpenCLLib.CL_DEVICE_TYPE_GPU, device_count.getValue(), devices, null);
|
||||
if (status != OpenCLLib.CL_SUCCESS) {
|
||||
System.out.println("OpenCL::getGpus failed(G) status: " + status);
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int j = 0; j < device_count.getValue(); j++) {
|
||||
String name = getInfodeviceString(lib, devices[j], OpenCLLib.CL_DEVICE_BOARD_NAME_AMD);
|
||||
String platform_name = getInfoPlatform(lib, plateforms[i], OpenCLLib.CL_PLATFORM_NAME);
|
||||
long vram = getInfodeviceLong(lib, devices[j], OpenCLLib.CL_DEVICE_GLOBAL_MEM_SIZE);
|
||||
if (name != null && vram > 0) {
|
||||
if (name.equals("Radeon RX Vega")) {
|
||||
name += " " + getInfodeviceLong(lib, devices[j], OpenCLLib.CL_DEVICE_MAX_COMPUTE_UNITS);
|
||||
}
|
||||
GPUDevice gpu = new GPUDevice(TYPE, name, vram, getBlenderId(lib, devices[j], platform_name, name));
|
||||
gpu.setOldId(TYPE + "_" + id);
|
||||
available_devices.add(gpu);
|
||||
}
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
return available_devices;
|
||||
}
|
||||
|
||||
private static String getInfodeviceString(OpenCLLib lib, CLDeviceId.ByReference device, int type) {
|
||||
byte name[] = new byte[256];
|
||||
|
||||
int status = lib.clGetDeviceInfo(device, type, 256, name, null);
|
||||
if (status != OpenCLLib.CL_SUCCESS) {
|
||||
System.out.println("OpenCL::getInfodeviceString failed(H) status: " + status + " type: " + type);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new String(name).trim();
|
||||
}
|
||||
|
||||
private static long getInfodeviceLong(OpenCLLib lib, CLDeviceId.ByReference device, int type) {
|
||||
byte name[] = new byte[256];
|
||||
|
||||
int status = lib.clGetDeviceInfo(device, type, 256, name, null);
|
||||
if (status != OpenCLLib.CL_SUCCESS) {
|
||||
System.out.println("OpenCL::getInfodeviceLong failed(I) status: " + status + " type: " + type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ByteBuffer wrapped = ByteBuffer.wrap(name);
|
||||
wrapped.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
return wrapped.getLong();
|
||||
}
|
||||
|
||||
private static String getInfoPlatform(OpenCLLib lib, CLPlatformId.ByReference platform, int type) {
|
||||
byte name[] = new byte[256];
|
||||
|
||||
int status = lib.clGetPlatformInfo(platform, type, 256, name, null);
|
||||
if (status != OpenCLLib.CL_SUCCESS) {
|
||||
System.out.println("GPU::getInfoPlatform failed(J) status: " + status + " type: " + type);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new String(name).trim();
|
||||
}
|
||||
|
||||
private static String getBlenderId(OpenCLLib lib, CLDeviceId.ByReference device, String platform, String name) {
|
||||
byte topology[] = new byte[24];
|
||||
|
||||
int status = lib.clGetDeviceInfo(device, lib.CL_DEVICE_TOPOLOGY_AMD, 24, topology, null);
|
||||
if (status != OpenCLLib.CL_SUCCESS) {
|
||||
System.out.println("OpenCL::getBlenderId failed(I) status: " + status);
|
||||
return "";
|
||||
}
|
||||
|
||||
return String.format("%s_%s_%s_%02x:%02x.%01x", TYPE, platform, name, topology[21], topology[22], topology[23]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 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.hardware.gpu.opencl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.sun.jna.Library;
|
||||
import com.sun.jna.Structure;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
|
||||
public interface OpenCLLib extends Library {
|
||||
// status
|
||||
public static final int CL_SUCCESS = 0;
|
||||
public static final int CL_DEVICE_NOT_FOUND = -1;
|
||||
|
||||
public static final int CL_PLATFORM_VENDOR = 0x0903;
|
||||
public static final int CL_PLATFORM_NAME = 0x0902;
|
||||
|
||||
// cl_device_type
|
||||
public static final int CL_DEVICE_TYPE_DEFAULT = (1 << 0);
|
||||
public static final int CL_DEVICE_TYPE_CPU = (1 << 1);
|
||||
public static final int CL_DEVICE_TYPE_GPU = (1 << 2);
|
||||
public static final int CL_DEVICE_TYPE_ACCELERATOR = (1 << 3);
|
||||
public static final int CL_DEVICE_TYPE_CUSTOM = (1 << 4);
|
||||
public static final int CL_DEVICE_TYPE_ALL = 0xFFFFFFFF;
|
||||
|
||||
// cl_device_info
|
||||
public static final int CL_DEVICE_NAME = 0x102B;
|
||||
public static final int CL_DEVICE_VENDOR = 0x102C;
|
||||
public static final int CL_DEVICE_VERSION = 0x102D;
|
||||
public static final int CL_DEVICE_MAX_COMPUTE_UNITS = 0x1002;
|
||||
public static final int CL_DEVICE_GLOBAL_MEM_SIZE = 0x101F;
|
||||
public static final int CL_DEVICE_BOARD_NAME_AMD = 0x4038;
|
||||
public static final int CL_DEVICE_TOPOLOGY_AMD = 0x4037;
|
||||
|
||||
public int clGetPlatformIDs(int num_entries, CLPlatformId.ByReference[] platforms, IntByReference num_platforms);
|
||||
|
||||
public int clGetPlatformInfo(CLPlatformId.ByReference platform, int param_name, long param_value_size, byte[] destination, long size_ret[]);
|
||||
|
||||
public int clGetDeviceIDs(CLPlatformId.ByReference platform, int param_name, int num_entries, CLDeviceId.ByReference[] devices,
|
||||
IntByReference device_count);
|
||||
|
||||
public int clGetDeviceInfo(CLDeviceId.ByReference device, int param_name, long param_value_size, byte[] destination, long size_ret[]);
|
||||
|
||||
public static class CLPlatformId extends Structure {
|
||||
public static class ByReference extends CLPlatformId implements Structure.ByReference {
|
||||
}
|
||||
|
||||
public int id;
|
||||
|
||||
@Override protected List<String> getFieldOrder() {
|
||||
return Arrays.asList(new String[] { "id" });
|
||||
}
|
||||
}
|
||||
|
||||
public static class CLDeviceId extends Structure {
|
||||
public static class ByReference extends CLDeviceId implements Structure.ByReference {
|
||||
}
|
||||
|
||||
public int id;
|
||||
|
||||
@Override protected List<String> getFieldOrder() {
|
||||
return Arrays.asList(new String[] { "id" });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.sheepit.client.hardware.hwid;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface BasicHWInfoStrategy {
|
||||
|
||||
Optional<String> getHarddriveID();
|
||||
Optional<String> getMAC();
|
||||
Optional<String> getProcessorName();
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.sheepit.client.hardware.hwid;
|
||||
|
||||
import com.sheepit.client.Log;
|
||||
import com.sheepit.client.hardware.hwid.impl.BaseHWInfoImpl;
|
||||
|
||||
import java.io.File;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public class HWIdentifier {
|
||||
private final BasicHWInfoStrategy strategy;
|
||||
private Log log;
|
||||
|
||||
public HWIdentifier(Log log) {
|
||||
strategy = new BaseHWInfoImpl();
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
public String getMAC() {
|
||||
return strategy.getMAC().orElse("");
|
||||
}
|
||||
|
||||
public String getHarddriveSerial() {
|
||||
return strategy.getHarddriveID().orElse("");
|
||||
}
|
||||
|
||||
public String getProcessorName() {
|
||||
return strategy.getProcessorName().orElse("");
|
||||
}
|
||||
|
||||
public String getHardwareHash() {
|
||||
byte[] hash;
|
||||
String mac;
|
||||
String cpuName;
|
||||
String hdSerial;
|
||||
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("md5");
|
||||
if ((hdSerial = getHarddriveSerial()).length() > 0) {
|
||||
hash = digest.digest(hdSerial.getBytes(StandardCharsets.UTF_8));
|
||||
log.debug("HWIdentifier::getHardwareHash found hdd hash");
|
||||
}
|
||||
else if ((mac = getMAC()).length() > 0) {
|
||||
hash = digest.digest(mac.getBytes(StandardCharsets.UTF_8));
|
||||
log.debug("HWIdentifier::getHardwareHash found MAC hash");
|
||||
}
|
||||
else { //Fallback: computing a hash out of homepath+jarFileLocation+cpuName
|
||||
log.debug("HWIdentifier::getHardwareHash using fallback method");
|
||||
cpuName = getProcessorName();
|
||||
if (cpuName.isEmpty()) {
|
||||
log.error("HWIdentifier::getHardwareHash failed to retrieve CPU name. Can't create hardware hash");
|
||||
throw new UnsupportedOperationException("Unable to create hash!");
|
||||
}
|
||||
|
||||
String homeDir = System.getProperty("user.home"); //get home path
|
||||
URL clientURL = getClass().getProtectionDomain().getCodeSource().getLocation();
|
||||
String clientPath = new File(clientURL.toString()).getParent(); //get jar file location
|
||||
|
||||
hash = digest.digest((homeDir + clientPath + cpuName).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
BigInteger num = new BigInteger(1, hash);
|
||||
return num.toString(16);
|
||||
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("HWIdentifier::getHardwareHash could not retrieve hash: " + e);
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.sheepit.client.hardware.hwid.impl;
|
||||
|
||||
import com.sheepit.client.hardware.hwid.BasicHWInfoStrategy;
|
||||
import com.sun.jna.Platform;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.HWDiskStore;
|
||||
import oshi.hardware.HardwareAbstractionLayer;
|
||||
import oshi.hardware.NetworkIF;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BaseHWInfoImpl implements BasicHWInfoStrategy {
|
||||
protected HardwareAbstractionLayer hardware;
|
||||
|
||||
public BaseHWInfoImpl() {
|
||||
hardware = new SystemInfo().getHardware();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getMAC() {
|
||||
List<String> macs = new ArrayList<>();
|
||||
List<NetworkIF> nics = hardware.getNetworkIFs();
|
||||
for (NetworkIF nic : nics) {
|
||||
macs.add(nic.getMacaddr());
|
||||
}
|
||||
Collections.sort(macs);
|
||||
return Optional.of(String.join(" ", macs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getProcessorName() {
|
||||
return Optional.of(hardware.getProcessor().getProcessorIdentifier().getName());
|
||||
}
|
||||
|
||||
public Optional<String> getHarddriveID() {
|
||||
String rootMountpoint;
|
||||
if (Platform.isWindows()) {
|
||||
rootMountpoint = "C:";
|
||||
}
|
||||
else {
|
||||
rootMountpoint = "/";
|
||||
}
|
||||
return getHarddriveID(rootMountpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find the root partition and returns that hard drives serial
|
||||
* @param rootMountpoint
|
||||
* @return
|
||||
*/
|
||||
private Optional<String> getHarddriveID(String rootMountpoint) {
|
||||
var drives = hardware.getDiskStores();
|
||||
|
||||
String hddSerial = "";
|
||||
boolean rootFound = false;
|
||||
Iterator<HWDiskStore> iterator = drives.iterator();
|
||||
|
||||
while (rootFound == false && iterator.hasNext()) {
|
||||
var drive = iterator.next();
|
||||
for (var partition : drive.getPartitions()) {
|
||||
if (partition.getMountPoint().equals(rootMountpoint)) {
|
||||
hddSerial = drive.getSerial();
|
||||
rootFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hddSerial.isEmpty() ? Optional.empty() : Optional.of(hddSerial);
|
||||
}
|
||||
}
|
||||
71
src/main/java/com/sheepit/client/network/Proxy.java
Normal file
71
src/main/java/com/sheepit/client/network/Proxy.java
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.network;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.Authenticator;
|
||||
|
||||
public class Proxy {
|
||||
|
||||
public static void set(String url_) throws MalformedURLException {
|
||||
if (url_ == null || url_.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
URL url = new URL(url_);
|
||||
String userinfo = url.getUserInfo();
|
||||
if (userinfo != null) {
|
||||
String[] elements = userinfo.split(":");
|
||||
if (elements.length == 2) {
|
||||
String proxy_user = elements[0];
|
||||
String proxy_password = elements[1];
|
||||
|
||||
if (proxy_user != null && proxy_password != null) {
|
||||
Authenticator.setDefault(new ProxyAuthenticator(proxy_user, proxy_password));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int port = url.getPort();
|
||||
if (port == -1) {
|
||||
port = 8080;
|
||||
}
|
||||
|
||||
System.setProperty("http.proxyHost", url.getHost());
|
||||
System.setProperty("http.proxyPort", Integer.toString(port));
|
||||
|
||||
System.setProperty("https.proxyHost", url.getHost());
|
||||
System.setProperty("https.proxyPort", Integer.toString(port));
|
||||
}
|
||||
|
||||
public static boolean isValidURL(String url_) {
|
||||
if (url_ == null || url_.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
new URL(url_);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.network;
|
||||
|
||||
import java.net.Authenticator;
|
||||
import java.net.PasswordAuthentication;
|
||||
|
||||
public class ProxyAuthenticator extends Authenticator {
|
||||
|
||||
private String user;
|
||||
private String password;
|
||||
|
||||
public ProxyAuthenticator(String user, String password) {
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(user, password.toCharArray());
|
||||
}
|
||||
}
|
||||
194
src/main/java/com/sheepit/client/os/Linux.java
Normal file
194
src/main/java/com/sheepit/client/os/Linux.java
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 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.os;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sheepit.client.Log;
|
||||
|
||||
public class Linux extends OS {
|
||||
private final String NICE_BINARY_PATH = "nice";
|
||||
private final String ID_COMMAND_INVOCATION = "id -u";
|
||||
|
||||
public Linux() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override public String name() {
|
||||
return "linux";
|
||||
}
|
||||
|
||||
@Override public String getRenderBinaryPath() {
|
||||
return "rend.exe";
|
||||
}
|
||||
|
||||
@Override public String getCUDALib() {
|
||||
return "cuda";
|
||||
}
|
||||
|
||||
@Override public Process exec(List<String> command, Map<String, String> env_overight) throws IOException {
|
||||
Map<String, String> new_env = new HashMap<String, String>();
|
||||
new_env.putAll(java.lang.System.getenv()); // clone the env
|
||||
|
||||
// if Blender is already loading an OpenGL library, don't need to load Blender's default one (it will
|
||||
// create system incompatibilities). If no OpenGL library is found, then load the one included in the binary
|
||||
// zip file
|
||||
if (isOpenGLAlreadyInstalled(command.get(0)) == false) {
|
||||
Boolean has_ld_library_path = new_env.containsKey("LD_LIBRARY_PATH");
|
||||
|
||||
String lib_dir = (new File(command.get(0))).getParent() + File.separator + "lib";
|
||||
if (has_ld_library_path == false) {
|
||||
new_env.put("LD_LIBRARY_PATH", lib_dir);
|
||||
}
|
||||
else {
|
||||
new_env.put("LD_LIBRARY_PATH", new_env.get("LD_LIBRARY_PATH") + ":" + lib_dir);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> actual_command = command;
|
||||
if (checkNiceAvailability()) {
|
||||
// launch the process in lowest priority
|
||||
if (env_overight != null) {
|
||||
actual_command.add(0, env_overight.get("PRIORITY"));
|
||||
}
|
||||
else {
|
||||
actual_command.add(0, "19");
|
||||
}
|
||||
actual_command.add(0, "-n");
|
||||
actual_command.add(0, NICE_BINARY_PATH);
|
||||
}
|
||||
else {
|
||||
Log.getInstance(null).error("No low priority binary, will not launch renderer in normal priority");
|
||||
}
|
||||
|
||||
ProcessBuilder builder = new ProcessBuilder(actual_command);
|
||||
builder.redirectErrorStream(true);
|
||||
Map<String, String> env = builder.environment();
|
||||
env.putAll(new_env);
|
||||
if (env_overight != null) {
|
||||
env.putAll(env_overight);
|
||||
}
|
||||
return builder.start();
|
||||
}
|
||||
|
||||
@Override public boolean getSupportHighPriority() {
|
||||
try {
|
||||
ProcessBuilder builder = new ProcessBuilder();
|
||||
builder.command("bash", "-c", ID_COMMAND_INVOCATION);
|
||||
builder.redirectErrorStream(true);
|
||||
|
||||
Process process = builder.start();
|
||||
InputStream is = process.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
||||
|
||||
String userLevel = null;
|
||||
if ((userLevel = reader.readLine()) != null) {
|
||||
// Root user in *ix systems -independently of the alias used to login- has a id value of 0. On top of being a user with root capabilities,
|
||||
// to support changing the priority the nice tool must be accessible from the current user
|
||||
return (userLevel.equals("0")) & checkNiceAvailability();
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.err.println(String.format("ERROR Linux::getSupportHighPriority Unable to execute id command. IOException %s", e.getMessage()));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean checkNiceAvailability() {
|
||||
ProcessBuilder builder = new ProcessBuilder();
|
||||
builder.command(NICE_BINARY_PATH);
|
||||
builder.redirectErrorStream(true);
|
||||
|
||||
Process process = null;
|
||||
boolean hasNiceBinary = false;
|
||||
try {
|
||||
process = builder.start();
|
||||
hasNiceBinary = true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
Log.getInstance(null).error("Failed to find low priority binary, will not launch renderer in normal priority (" + e + ")");
|
||||
}
|
||||
finally {
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
return hasNiceBinary;
|
||||
}
|
||||
|
||||
protected boolean isOpenGLAlreadyInstalled(String pathToRendEXE) {
|
||||
ProcessBuilder processBuilder = new ProcessBuilder();
|
||||
processBuilder.command("bash", "-c", "ldd '" + pathToRendEXE + "'"); // support for paths with an space
|
||||
processBuilder.redirectErrorStream(true);
|
||||
|
||||
try {
|
||||
Process process = processBuilder.start();
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
|
||||
String line;
|
||||
StringBuilder screenOutput = new StringBuilder();
|
||||
while ((line = reader.readLine()) != null) {
|
||||
// check the shared libraries that Blender is loading at run time. If it already loads an existing
|
||||
// version of OpenGL (ie the one shipped with NVIDIA drivers) then return false to avoid the client
|
||||
// replacing them (and glitching the EEVEE render). Otherwise return true and load the /lib folder
|
||||
// to ensure that Blender works correctly
|
||||
if (line.toLowerCase().contains("libgl.so")) {
|
||||
return !line.toLowerCase().contains("not found");
|
||||
}
|
||||
|
||||
// In case of error we can later check the screen output from ldd
|
||||
screenOutput.append(line);
|
||||
}
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode != 0) {
|
||||
System.err.println(String.format("ERROR Linux::isOpenGLAlreadyInstalled Unable to execute ldd command. Exit code %d", exitCode));
|
||||
System.err.println(String.format("Screen output from ldd execution: %s", screenOutput.toString()));
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.err.println(String.format("ERROR Linux::isOpenGLAreadyInstalled Unable to execute ldd command. IOException %s", e.getMessage()));
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
System.err.println(String.format("ERROR Linux::isOpenGLAreadyInstalled Unable to execute ldd command. InterruptedException %s", e.getMessage()));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public void shutdownComputer(int delayInMinutes) {
|
||||
try {
|
||||
// Shutdown the computer waiting delayInMinutes minutes to allow all SheepIt threads to close and exit the app
|
||||
ProcessBuilder builder = new ProcessBuilder("shutdown", "-h", String.valueOf(delayInMinutes));
|
||||
Process process = builder.inheritIO().start();
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.err.println(String.format("Linux::shutdownComputer Unable to execute the 'shutdown -h 1' command. Exception %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
137
src/main/java/com/sheepit/client/os/Mac.java
Normal file
137
src/main/java/com/sheepit/client/os/Mac.java
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 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.os;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sheepit.client.Log;
|
||||
|
||||
public class Mac extends OS {
|
||||
private final String NICE_BINARY_PATH = "nice";
|
||||
private final String ID_COMMAND_INVOCATION = "id -u";
|
||||
|
||||
public Mac() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override public String name() {
|
||||
return "mac";
|
||||
}
|
||||
|
||||
@Override public String getRenderBinaryPath() {
|
||||
return "Blender" + File.separator + "Blender.app" + File.separator + "Contents" + File.separator + "MacOS" + File.separator + "Blender";
|
||||
}
|
||||
|
||||
@Override public Process exec(List<String> command, Map<String, String> env) throws IOException {
|
||||
List<String> actual_command = command;
|
||||
if (checkNiceAvailability()) {
|
||||
// launch the process in lowest priority
|
||||
if (env != null) {
|
||||
actual_command.add(0, env.get("PRIORITY"));
|
||||
}
|
||||
else {
|
||||
actual_command.add(0, "19");
|
||||
}
|
||||
actual_command.add(0, "-n");
|
||||
actual_command.add(0, NICE_BINARY_PATH);
|
||||
}
|
||||
else {
|
||||
Log.getInstance(null).error("No low priority binary, will not launch renderer in normal priority");
|
||||
}
|
||||
ProcessBuilder builder = new ProcessBuilder(actual_command);
|
||||
builder.redirectErrorStream(true);
|
||||
if (env != null) {
|
||||
builder.environment().putAll(env);
|
||||
}
|
||||
return builder.start();
|
||||
}
|
||||
|
||||
@Override public boolean isSupported() {
|
||||
String[] ver = operatingSystem.getVersionInfo().getVersion().split("\\.");
|
||||
int majorVer = Integer.parseInt(ver[0]), minorVer = Integer.parseInt(ver[1]);
|
||||
return super.isSupported() && ((majorVer == 10 && minorVer >= 13) || majorVer >= 11);
|
||||
}
|
||||
|
||||
@Override public String getCUDALib() {
|
||||
return "/usr/local/cuda/lib/libcuda.dylib";
|
||||
}
|
||||
|
||||
@Override public boolean getSupportHighPriority() {
|
||||
try {
|
||||
ProcessBuilder builder = new ProcessBuilder();
|
||||
builder.command("bash", "-c", ID_COMMAND_INVOCATION);
|
||||
builder.redirectErrorStream(true);
|
||||
|
||||
Process process = builder.start();
|
||||
InputStream is = process.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
||||
|
||||
String userLevel = null;
|
||||
if ((userLevel = reader.readLine()) != null) {
|
||||
// Root user in *ix systems -independently of the alias used to login- has a id value of 0. On top of being a user with root capabilities,
|
||||
// to support changing the priority the nice tool must be accessible from the current user
|
||||
return (userLevel.equals("0")) & checkNiceAvailability();
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.err.println(String.format("ERROR Mac::getSupportHighPriority Unable to execute id command. IOException %s", e.getMessage()));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean checkNiceAvailability() {
|
||||
ProcessBuilder builder = new ProcessBuilder();
|
||||
builder.command(NICE_BINARY_PATH);
|
||||
builder.redirectErrorStream(true);
|
||||
|
||||
Process process = null;
|
||||
boolean hasNiceBinary = false;
|
||||
try {
|
||||
process = builder.start();
|
||||
hasNiceBinary = true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
Log.getInstance(null).error("Failed to find low priority binary, will not launch renderer in normal priority (" + e + ")");
|
||||
}
|
||||
finally {
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
return hasNiceBinary;
|
||||
}
|
||||
|
||||
@Override public void shutdownComputer(int delayInMinutes) {
|
||||
try {
|
||||
// Shutdown the computer waiting delayInMinutes minutes to allow all SheepIt threads to close and exit the app
|
||||
ProcessBuilder builder = new ProcessBuilder("shutdown", "-h", String.valueOf(delayInMinutes));
|
||||
Process process = builder.inheritIO().start();
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.err.println(String.format("Mac::shutdownComputer Unable to execute the 'shutdown -h 1' command. Exception %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/main/java/com/sheepit/client/os/MacM1.java
Normal file
25
src/main/java/com/sheepit/client/os/MacM1.java
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.os;
|
||||
|
||||
public class MacM1 extends Mac {
|
||||
@Override public String name() {
|
||||
return "macm1";
|
||||
}
|
||||
}
|
||||
148
src/main/java/com/sheepit/client/os/OS.java
Normal file
148
src/main/java/com/sheepit/client/os/OS.java
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 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.os;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.CentralProcessor;
|
||||
import oshi.software.os.OperatingSystem;
|
||||
import oshi.hardware.HardwareAbstractionLayer;
|
||||
import com.sheepit.client.hardware.cpu.CPU;
|
||||
|
||||
public abstract class OS {
|
||||
private static SystemInfo systemInfo = new SystemInfo();
|
||||
|
||||
public static OperatingSystem operatingSystem = systemInfo.getOperatingSystem();
|
||||
|
||||
private static HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware();
|
||||
|
||||
private static OS instance = null;
|
||||
|
||||
public abstract String name();
|
||||
|
||||
public boolean isSupported() { return "64bit".equals(getCPU().arch()); }
|
||||
|
||||
/** Get the full version of the os.
|
||||
* For example windows, should give "windows 8.1"
|
||||
*/
|
||||
public String getVersion() {
|
||||
return (name() + " " + operatingSystem.getVersionInfo()).toLowerCase();
|
||||
}
|
||||
|
||||
public long getTotalMemory() {
|
||||
return hardwareAbstractionLayer.getMemory().getTotal() / 1024;
|
||||
}
|
||||
|
||||
public long getFreeMemory() {
|
||||
return hardwareAbstractionLayer.getMemory().getAvailable() / 1024;
|
||||
}
|
||||
|
||||
public abstract String getRenderBinaryPath();
|
||||
|
||||
public String getCUDALib() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract boolean getSupportHighPriority();
|
||||
|
||||
public abstract boolean checkNiceAvailability();
|
||||
|
||||
/**
|
||||
* Shutdown the computer waiting delayInMinutes minutes to allow all SheepIt threads to close and exit the app
|
||||
*/
|
||||
public abstract void shutdownComputer(int delayInMinutes);
|
||||
|
||||
public CPU getCPU() {
|
||||
CentralProcessor.ProcessorIdentifier cpuID = hardwareAbstractionLayer.getProcessor().getProcessorIdentifier();
|
||||
CPU ret = new CPU();
|
||||
ret.setName(cpuID.getName());
|
||||
ret.setModel(cpuID.getModel());
|
||||
ret.setFamily(cpuID.getFamily());
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Process exec(List<String> command, Map<String, String> env) throws IOException {
|
||||
ProcessBuilder builder = new ProcessBuilder(command);
|
||||
builder.redirectErrorStream(true);
|
||||
if (env != null) {
|
||||
builder.environment().putAll(env);
|
||||
}
|
||||
return builder.start();
|
||||
}
|
||||
|
||||
public boolean kill(Process proc) {
|
||||
if (proc != null) {
|
||||
proc.destroy();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static OS getOS() {
|
||||
if (instance == null) {
|
||||
switch (operatingSystem.getManufacturer()){
|
||||
case "Microsoft":
|
||||
instance = new Windows();
|
||||
break;
|
||||
case "Apple":
|
||||
if ("aarch64".equalsIgnoreCase(System.getProperty("os.arch"))) { // ARM arch ?
|
||||
instance = new MacM1();
|
||||
}
|
||||
else {
|
||||
instance = new Mac();
|
||||
}
|
||||
break;
|
||||
case "GNU/Linux":
|
||||
instance = new Linux();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public String getDefaultConfigFilePath() {
|
||||
String xdgConfigHome = System.getenv("XDG_CONFIG_HOME");
|
||||
String userHome = System.getProperty("user.home");
|
||||
// fallback xdg config home should be ~/.config/
|
||||
if (xdgConfigHome == null || xdgConfigHome.isEmpty()) {
|
||||
xdgConfigHome = userHome + File.separator + ".config";
|
||||
}
|
||||
// add the config folder to the path
|
||||
xdgConfigHome += File.separator + "sheepit";
|
||||
|
||||
// check if file already exists in ~/.config/sheepit/sheepit.conf
|
||||
File file = new File(xdgConfigHome + File.separator + "sheepit.conf");
|
||||
if (file.exists()) {
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
// if it doesn't exist, try $HOME/.sheepit.conf
|
||||
file = new File(userHome + File.separator + ".sheepit.conf");
|
||||
if (file.exists()) {
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
// if it doesn't exist, create the file in the XDG compliant location
|
||||
file = new File(xdgConfigHome);
|
||||
file.mkdirs();
|
||||
return file.getAbsolutePath() + File.separator + "sheepit.conf";
|
||||
}
|
||||
}
|
||||
183
src/main/java/com/sheepit/client/os/Windows.java
Normal file
183
src/main/java/com/sheepit/client/os/Windows.java
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 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.os;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sheepit.client.os.windows.Kernel32Lib;
|
||||
import com.sheepit.client.os.windows.WinProcess;
|
||||
import com.sun.jna.Native;
|
||||
|
||||
public class Windows extends OS {
|
||||
|
||||
private static final int MINIMUM_SUPPORTED_BUILD = 9600; //windows 8.1 and Server 2012 R2
|
||||
|
||||
@Override public String name() {
|
||||
return "windows";
|
||||
}
|
||||
|
||||
@Override public String getRenderBinaryPath() {
|
||||
return "rend.exe";
|
||||
}
|
||||
|
||||
@Override public String getCUDALib() {
|
||||
return "nvcuda";
|
||||
}
|
||||
|
||||
@Override public Process exec(List<String> command, Map<String, String> env) throws IOException {
|
||||
// disable a popup because the renderer might crash (seg fault)
|
||||
Kernel32Lib kernel32lib = null;
|
||||
try {
|
||||
kernel32lib = (Kernel32Lib) Native.load(Kernel32Lib.path, Kernel32Lib.class);
|
||||
kernel32lib.SetErrorMode(Kernel32Lib.SEM_NOGPFAULTERRORBOX);
|
||||
}
|
||||
catch (java.lang.UnsatisfiedLinkError e) {
|
||||
System.out.println("OS.Windows::exec failed to load kernel32lib " + e);
|
||||
}
|
||||
catch (java.lang.ExceptionInInitializerError e) {
|
||||
System.out.println("OS.Windows::exec failed to load kernel32lib " + e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
System.out.println("OS.Windows::exec failed to load kernel32lib " + e);
|
||||
}
|
||||
|
||||
ProcessBuilder builder = new ProcessBuilder(command);
|
||||
builder.redirectErrorStream(true);
|
||||
if (env != null) {
|
||||
builder.environment().putAll(env);
|
||||
}
|
||||
Process p = builder.start();
|
||||
WinProcess wproc = new WinProcess(p);
|
||||
if (env != null) {
|
||||
String priority = env.get("PRIORITY");
|
||||
wproc.setPriority(getPriorityClass(Integer.parseInt(priority)));
|
||||
}
|
||||
else {
|
||||
wproc.setPriority(WinProcess.PRIORITY_BELOW_NORMAL);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override public boolean isSupported() {
|
||||
long buildNumber = Long.MIN_VALUE;
|
||||
try {
|
||||
buildNumber = Long.parseLong(operatingSystem.getVersionInfo().getBuildNumber());
|
||||
}
|
||||
catch(NumberFormatException e) {
|
||||
System.err.println("Windows::isSupported Failed to extract Windows build number: " + e);
|
||||
}
|
||||
return super.isSupported() && buildNumber >= MINIMUM_SUPPORTED_BUILD;
|
||||
}
|
||||
|
||||
int getPriorityClass(int priority) {
|
||||
int process_class = WinProcess.PRIORITY_IDLE;
|
||||
switch (priority) {
|
||||
case 19:
|
||||
case 18:
|
||||
case 17:
|
||||
case 16:
|
||||
case 15:
|
||||
process_class = WinProcess.PRIORITY_IDLE;
|
||||
break;
|
||||
|
||||
case 14:
|
||||
case 13:
|
||||
case 12:
|
||||
case 11:
|
||||
case 10:
|
||||
case 9:
|
||||
case 8:
|
||||
case 7:
|
||||
case 6:
|
||||
case 5:
|
||||
process_class = WinProcess.PRIORITY_BELOW_NORMAL;
|
||||
break;
|
||||
case 4:
|
||||
case 3:
|
||||
case 2:
|
||||
case 1:
|
||||
case 0:
|
||||
case -1:
|
||||
case -2:
|
||||
case -3:
|
||||
process_class = WinProcess.PRIORITY_NORMAL;
|
||||
break;
|
||||
case -4:
|
||||
case -5:
|
||||
case -6:
|
||||
case -7:
|
||||
case -8:
|
||||
case -9:
|
||||
process_class = WinProcess.PRIORITY_ABOVE_NORMAL;
|
||||
break;
|
||||
case -10:
|
||||
case -11:
|
||||
case -12:
|
||||
case -13:
|
||||
case -14:
|
||||
process_class = WinProcess.PRIORITY_HIGH;
|
||||
break;
|
||||
case -15:
|
||||
case -16:
|
||||
case -17:
|
||||
case -18:
|
||||
case -19:
|
||||
process_class = WinProcess.PRIORITY_REALTIME;
|
||||
break;
|
||||
}
|
||||
return process_class;
|
||||
}
|
||||
|
||||
@Override public boolean kill(Process process) {
|
||||
if (process != null) {
|
||||
WinProcess wproc = new WinProcess(process);
|
||||
wproc.kill();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean getSupportHighPriority() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public boolean checkNiceAvailability() {
|
||||
// In windows, nice is not required and therefore we return always true to show the slider in the Settings GUI
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public void shutdownComputer(int delayInMinutes) {
|
||||
try {
|
||||
// Shutdown the computer, waiting delayInMinutes minutes, force app closure and on the shutdown screen indicate that was initiated by SheepIt app
|
||||
ProcessBuilder builder = new ProcessBuilder("shutdown", "/s", "/f", "/t", String.valueOf(delayInMinutes * 60), "/c", "\"SheepIt App has initiated this computer shutdown.\"");
|
||||
Process process = builder.inheritIO().start();
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.err.println(
|
||||
String.format("Windows::shutdownComputer Unable to execute the command 'shutdown /s /f /t 60...' command. Exception %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String getDefaultConfigFilePath() {
|
||||
return System.getProperty("user.home") + File.separator + ".sheepit.conf";
|
||||
}
|
||||
}
|
||||
216
src/main/java/com/sheepit/client/os/windows/Kernel32Lib.java
Normal file
216
src/main/java/com/sheepit/client/os/windows/Kernel32Lib.java
Normal file
@@ -0,0 +1,216 @@
|
||||
/* This file was originally taken from JNA project (https://github.com/twall/jna)
|
||||
* filename: contrib/platform/src/com/sun/jna/platform/win32/Tlhelp32.java
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.os.windows;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.sun.jna.Library;
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.Structure;
|
||||
import com.sun.jna.platform.win32.BaseTSD;
|
||||
import com.sun.jna.platform.win32.WinDef;
|
||||
import com.sun.jna.platform.win32.WinDef.DWORD;
|
||||
import com.sun.jna.platform.win32.WinNT;
|
||||
import com.sun.jna.platform.win32.WinNT.HANDLE;
|
||||
|
||||
public interface Kernel32Lib extends Library {
|
||||
public static final String path = "kernel32";
|
||||
|
||||
/**
|
||||
* Includes all heaps of the process specified in th32ProcessID in the snapshot. To enumerate the heaps, see
|
||||
* Heap32ListFirst.
|
||||
*/
|
||||
WinDef.DWORD TH32CS_SNAPHEAPLIST = new WinDef.DWORD(0x00000001);
|
||||
|
||||
/**
|
||||
* Includes all processes in the system in the snapshot. To enumerate the processes, see Process32First.
|
||||
*/
|
||||
WinDef.DWORD TH32CS_SNAPPROCESS = new WinDef.DWORD(0x00000002);
|
||||
|
||||
/**
|
||||
* Includes all threads in the system in the snapshot. To enumerate the threads, see Thread32First.
|
||||
*/
|
||||
WinDef.DWORD TH32CS_SNAPTHREAD = new WinDef.DWORD(0x00000004);
|
||||
|
||||
/**
|
||||
* Includes all modules of the process specified in th32ProcessID in the snapshot. To enumerate the modules, see
|
||||
* Module32First. If the function fails with ERROR_BAD_LENGTH, retry the function until it succeeds.
|
||||
*/
|
||||
WinDef.DWORD TH32CS_SNAPMODULE = new WinDef.DWORD(0x00000008);
|
||||
|
||||
/**
|
||||
* Includes all 32-bit modules of the process specified in th32ProcessID in the snapshot when called from a 64-bit
|
||||
* process. This flag can be combined with TH32CS_SNAPMODULE or TH32CS_SNAPALL. If the function fails with
|
||||
* ERROR_BAD_LENGTH, retry the function until it succeeds.
|
||||
*/
|
||||
WinDef.DWORD TH32CS_SNAPMODULE32 = new WinDef.DWORD(0x00000010);
|
||||
|
||||
/**
|
||||
* Includes all processes and threads in the system, plus the heaps and modules of the process specified in th32ProcessID.
|
||||
*/
|
||||
WinDef.DWORD TH32CS_SNAPALL = new WinDef.DWORD(
|
||||
(TH32CS_SNAPHEAPLIST.intValue() | TH32CS_SNAPPROCESS.intValue() | TH32CS_SNAPTHREAD.intValue() | TH32CS_SNAPMODULE.intValue()));
|
||||
|
||||
/**
|
||||
* Indicates that the snapshot handle is to be inheritable.
|
||||
*/
|
||||
WinDef.DWORD TH32CS_INHERIT = new WinDef.DWORD(0x80000000);
|
||||
|
||||
/**
|
||||
* The system does not display the Windows Error Reporting dialog.
|
||||
* See: http://msdn.microsoft.com/en-us/library/ms680621%28VS.85%29.aspx
|
||||
*/
|
||||
WinDef.DWORD SEM_NOGPFAULTERRORBOX = new WinDef.DWORD(0x0002);
|
||||
|
||||
/**
|
||||
* Describes an entry from a list of the processes residing in the system address space when a snapshot was taken.
|
||||
*/
|
||||
public static class PROCESSENTRY32 extends Structure {
|
||||
|
||||
public static class ByReference extends PROCESSENTRY32 implements Structure.ByReference {
|
||||
public ByReference() {
|
||||
}
|
||||
|
||||
public ByReference(Pointer memory) {
|
||||
super(memory);
|
||||
}
|
||||
}
|
||||
|
||||
public PROCESSENTRY32() {
|
||||
dwSize = new WinDef.DWORD(size());
|
||||
}
|
||||
|
||||
public PROCESSENTRY32(Pointer memory) {
|
||||
useMemory(memory);
|
||||
read();
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of the structure, in bytes. Before calling the Process32First function, set this member to
|
||||
* sizeof(PROCESSENTRY32). If you do not initialize dwSize, Process32First fails.
|
||||
*/
|
||||
public WinDef.DWORD dwSize;
|
||||
|
||||
/**
|
||||
* This member is no longer used and is always set to zero.
|
||||
*/
|
||||
public WinDef.DWORD cntUsage;
|
||||
|
||||
/**
|
||||
* The process identifier.
|
||||
*/
|
||||
public WinDef.DWORD th32ProcessID;
|
||||
|
||||
/**
|
||||
* This member is no longer used and is always set to zero.
|
||||
*/
|
||||
public BaseTSD.ULONG_PTR th32DefaultHeapID;
|
||||
|
||||
/**
|
||||
* This member is no longer used and is always set to zero.
|
||||
*/
|
||||
public WinDef.DWORD th32ModuleID;
|
||||
|
||||
/**
|
||||
* The number of execution threads started by the process.
|
||||
*/
|
||||
public WinDef.DWORD cntThreads;
|
||||
|
||||
/**
|
||||
* The identifier of the process that created this process (its parent process).
|
||||
*/
|
||||
public WinDef.DWORD th32ParentProcessID;
|
||||
|
||||
/**
|
||||
* The base priority of any threads created by this process.
|
||||
*/
|
||||
public WinDef.LONG pcPriClassBase;
|
||||
|
||||
/**
|
||||
* This member is no longer used, and is always set to zero.
|
||||
*/
|
||||
public WinDef.DWORD dwFlags;
|
||||
|
||||
/**
|
||||
* The name of the executable file for the process. To retrieve the full path to the executable file, call the
|
||||
* Module32First function and check the szExePath member of the MODULEENTRY32 structure that is returned.
|
||||
* However, if the calling process is a 32-bit process, you must call the QueryFullProcessImageName function to
|
||||
* retrieve the full path of the executable file for a 64-bit process.
|
||||
*/
|
||||
public char[] szExeFile = new char[WinDef.MAX_PATH];
|
||||
|
||||
@Override protected List<String> getFieldOrder() {
|
||||
return Arrays
|
||||
.asList("dwSize", "cntUsage", "th32ProcessID", "th32DefaultHeapID", "th32ModuleID", "cntThreads", "th32ParentProcessID", "pcPriClassBase",
|
||||
"dwFlags", "szExeFile");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a snapshot of the specified processes, as well as the heaps, modules, and threads used by these processes.
|
||||
*
|
||||
* @param dwFlags The portions of the system to be included in the snapshot.
|
||||
* @param th32ProcessID The process identifier of the process to be included in the snapshot. This parameter can be zero to indicate
|
||||
* the current process. This parameter is used when the TH32CS_SNAPHEAPLIST, TH32CS_SNAPMODULE,
|
||||
* TH32CS_SNAPMODULE32, or TH32CS_SNAPALL value is specified. Otherwise, it is ignored and all processes are
|
||||
* included in the snapshot.
|
||||
* <p/>
|
||||
* If the specified process is the Idle process or one of the CSRSS processes, this function fails and the last
|
||||
* error code is ERROR_ACCESS_DENIED because their access restrictions prevent user-level code from opening them.
|
||||
* <p/>
|
||||
* If the specified process is a 64-bit process and the caller is a 32-bit process, this function fails and the
|
||||
* last error code is ERROR_PARTIAL_COPY (299).
|
||||
* @return If the function succeeds, it returns an open handle to the specified snapshot.
|
||||
* <p/>
|
||||
* If the function fails, it returns INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.
|
||||
* Possible error codes include ERROR_BAD_LENGTH.
|
||||
*/
|
||||
public WinNT.HANDLE CreateToolhelp32Snapshot(WinDef.DWORD dwFlags, WinDef.DWORD th32ProcessID);
|
||||
|
||||
/**
|
||||
* Retrieves information about the first process encountered in a system snapshot.
|
||||
*
|
||||
* @param hSnapshot A handle to the snapshot returned from a previous call to the CreateToolhelp32Snapshot function.
|
||||
* @param lppe A pointer to a PROCESSENTRY32 structure. It contains process information such as the name of the
|
||||
* executable file, the process identifier, and the process identifier of the parent process.
|
||||
* @return Returns TRUE if the first entry of the process list has been copied to the buffer or FALSE otherwise. The
|
||||
* ERROR_NO_MORE_FILES error value is returned by the GetLastError function if no processes exist or the snapshot
|
||||
* does not contain process information.
|
||||
*/
|
||||
public boolean Process32First(WinNT.HANDLE hSnapshot, Kernel32Lib.PROCESSENTRY32.ByReference lppe);
|
||||
|
||||
/**
|
||||
* Retrieves information about the next process recorded in a system snapshot.
|
||||
*
|
||||
* @param hSnapshot A handle to the snapshot returned from a previous call to the CreateToolhelp32Snapshot function.
|
||||
* @param lppe A pointer to a PROCESSENTRY32 structure.
|
||||
* @return Returns TRUE if the next entry of the process list has been copied to the buffer or FALSE otherwise. The
|
||||
* ERROR_NO_MORE_FILES error value is returned by the GetLastError function if no processes exist or the snapshot
|
||||
* does not contain process information.
|
||||
*/
|
||||
public boolean Process32Next(WinNT.HANDLE hSnapshot, Kernel32Lib.PROCESSENTRY32.ByReference lppe);
|
||||
|
||||
public boolean SetPriorityClass(HANDLE hProcess, int dwPriorityClass);
|
||||
|
||||
/**
|
||||
* Controls whether the system will handle the specified types of serious errors or whether the process will handle them.
|
||||
* See: http://msdn.microsoft.com/en-us/library/ms680621%28VS.85%29.aspx
|
||||
*
|
||||
* @param uMode The process error mode. This parameter can be one or more of the following values.
|
||||
*/
|
||||
public int SetErrorMode(DWORD uMode);
|
||||
|
||||
}
|
||||
194
src/main/java/com/sheepit/client/os/windows/WinProcess.java
Normal file
194
src/main/java/com/sheepit/client/os/windows/WinProcess.java
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.os.windows;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.platform.win32.Kernel32;
|
||||
import com.sun.jna.platform.win32.Kernel32Util;
|
||||
import com.sun.jna.platform.win32.WinDef.DWORD;
|
||||
import com.sun.jna.platform.win32.WinNT;
|
||||
|
||||
public class WinProcess {
|
||||
public static final int PRIORITY_IDLE = 0x40;
|
||||
public static final int PRIORITY_BELOW_NORMAL = 0x4000;
|
||||
public static final int PRIORITY_NORMAL = 0x20;
|
||||
public static final int PRIORITY_ABOVE_NORMAL = 0x8000;
|
||||
public static final int PRIORITY_HIGH = 0x80;
|
||||
public static final int PRIORITY_REALTIME = 0x100;
|
||||
|
||||
private WinNT.HANDLE handle;
|
||||
private int pid;
|
||||
Kernel32Lib kernel32lib;
|
||||
|
||||
public WinProcess() {
|
||||
this.handle = null;
|
||||
this.pid = -1;
|
||||
this.kernel32lib = null;
|
||||
try {
|
||||
this.kernel32lib = (Kernel32Lib) Native.load(Kernel32Lib.path, Kernel32Lib.class);
|
||||
}
|
||||
catch (java.lang.UnsatisfiedLinkError e) {
|
||||
System.out.println("WinProcess::construct " + e);
|
||||
}
|
||||
catch (java.lang.ExceptionInInitializerError e) {
|
||||
System.out.println("WinProcess::construct " + e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
System.out.println("WinProcess::construct " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean processHasGetPid() {
|
||||
try {
|
||||
if (Process.class.getMethod("pid") != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (NoSuchMethodException | SecurityException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static long getPid(Process process) {
|
||||
try {
|
||||
return (long) Process.class.getMethod("pid").invoke(process);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static WinNT.HANDLE getHandleByPid(int pid_) throws IOException {
|
||||
WinNT.HANDLE handle = Kernel32.INSTANCE.OpenProcess(0x0400 | // PROCESS_QUERY_INFORMATION
|
||||
0x0800 | // PROCESS_SUSPEND_RESUME
|
||||
0x0001 | // PROCESS_TERMINATE
|
||||
0x0200 | // PROCESS_SET_INFORMATION
|
||||
0x00100000, // SYNCHRONIZE
|
||||
false, pid_);
|
||||
if (handle == null) {
|
||||
throw new IOException(
|
||||
"OpenProcess failed: " + Kernel32Util.formatMessageFromLastErrorCode(Kernel32.INSTANCE.GetLastError()) + " (pid: " + pid_ + ")");
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
public WinProcess(Process process) {
|
||||
this();
|
||||
if (processHasGetPid()) {
|
||||
int _pid = (int) getPid(process);
|
||||
try {
|
||||
this.handle = getHandleByPid(_pid);
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
this.pid = _pid;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
Field f = process.getClass().getDeclaredField("handle");
|
||||
f.setAccessible(true);
|
||||
long val = f.getLong(process);
|
||||
this.handle = new WinNT.HANDLE();
|
||||
this.handle.setPointer(Pointer.createConstant(val));
|
||||
this.pid = Kernel32.INSTANCE.GetProcessId(this.handle);
|
||||
}
|
||||
catch (NoSuchFieldException e) {
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WinProcess(int pid_) throws IOException {
|
||||
this();
|
||||
this.handle = getHandleByPid(pid_);
|
||||
this.pid = pid_;
|
||||
}
|
||||
|
||||
@Override protected void finalize() throws Throwable {
|
||||
if (this.handle != null) {
|
||||
// Kernel32.INSTANCE.CloseHandle(this.handle); // do not close the handle because the parent Process object might still be alive
|
||||
this.handle = null;
|
||||
}
|
||||
this.pid = -1;
|
||||
}
|
||||
|
||||
public boolean kill() {
|
||||
try {
|
||||
List<WinProcess> children = this.getChildren();
|
||||
this.terminate();
|
||||
for (WinProcess child : children) {
|
||||
child.kill();
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean setPriority(int priority) {
|
||||
return this.kernel32lib.SetPriorityClass(this.handle, priority);
|
||||
}
|
||||
|
||||
private void terminate() {
|
||||
Kernel32.INSTANCE.TerminateProcess(this.handle, 0);
|
||||
Kernel32.INSTANCE.CloseHandle(this.handle); // we are sure that the parent Process object is dead
|
||||
}
|
||||
|
||||
private List<WinProcess> getChildren() throws IOException {
|
||||
ArrayList<WinProcess> result = new ArrayList<WinProcess>();
|
||||
|
||||
WinNT.HANDLE hSnap = this.kernel32lib.CreateToolhelp32Snapshot(Kernel32Lib.TH32CS_SNAPPROCESS, new DWORD(0));
|
||||
Kernel32Lib.PROCESSENTRY32.ByReference ent = new Kernel32Lib.PROCESSENTRY32.ByReference();
|
||||
if (!this.kernel32lib.Process32First(hSnap, ent)) {
|
||||
return result;
|
||||
}
|
||||
do {
|
||||
if (ent.th32ParentProcessID.intValue() == this.pid) {
|
||||
try {
|
||||
result.add(new WinProcess(ent.th32ProcessID.intValue()));
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.err.println("WinProcess::getChildren, IOException " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (this.kernel32lib.Process32Next(hSnap, ent));
|
||||
|
||||
Kernel32.INSTANCE.CloseHandle(hSnap);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "WinProcess(pid: " + this.pid + ", handle " + this.handle + ")";
|
||||
}
|
||||
}
|
||||
501
src/main/java/com/sheepit/client/standalone/GuiSwing.java
Normal file
501
src/main/java/com/sheepit/client/standalone/GuiSwing.java
Normal file
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 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.standalone;
|
||||
|
||||
import com.formdev.flatlaf.FlatDarkLaf;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.FlatLightLaf;
|
||||
import com.sheepit.client.Client;
|
||||
import com.sheepit.client.Configuration;
|
||||
import com.sheepit.client.Gui;
|
||||
import com.sheepit.client.SettingsLoader;
|
||||
import com.sheepit.client.Stats;
|
||||
import com.sheepit.client.TransferStats;
|
||||
import com.sheepit.client.standalone.swing.activity.Settings;
|
||||
import com.sheepit.client.standalone.swing.activity.Working;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import java.awt.AWTException;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Image;
|
||||
import java.awt.MenuItem;
|
||||
import java.awt.PopupMenu;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.TrayIcon;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowStateListener;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URL;
|
||||
import java.util.Objects;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class GuiSwing extends JFrame implements Gui {
|
||||
public static final String type = "swing";
|
||||
private static final String logoPath = "/sheepit-logo.png";
|
||||
|
||||
public static void drawVersionStringOnImage(final BufferedImage image, String versionString) {
|
||||
var watermarkWidth = image.getWidth();
|
||||
var watermarkHeight = image.getHeight();
|
||||
|
||||
Graphics2D gph = (Graphics2D) image.getGraphics();
|
||||
// use anti aliasing to smooth version string
|
||||
gph.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
gph.setFont(gph.getFont().deriveFont(12f));
|
||||
|
||||
FontMetrics fontMetrics = gph.getFontMetrics();
|
||||
// move text to the very right of the image respecting its length
|
||||
int x = watermarkWidth - fontMetrics.stringWidth(versionString);
|
||||
// the bottom of the image, but give enough headroom for descending letters like g
|
||||
int y = watermarkHeight - fontMetrics.getMaxDescent();
|
||||
|
||||
gph.drawString(versionString, x, y);
|
||||
gph.dispose();
|
||||
}
|
||||
|
||||
@NotNull public static JLabel createLogoWithWatermark() {
|
||||
final URL logoURL = GuiSwing.class.getResource(logoPath);
|
||||
|
||||
// If logo cannot be found, return dummy image
|
||||
if (logoURL == null) {
|
||||
System.err.println("Error: Unable to find logo " + logoPath);
|
||||
return new JLabel();
|
||||
}
|
||||
|
||||
JLabel labelImage;
|
||||
try {
|
||||
// Include the version of the app as a watermark in the SheepIt logo.
|
||||
final BufferedImage watermark = ImageIO.read(logoURL);
|
||||
String versionString = "v" + Configuration.jarVersion;
|
||||
drawVersionStringOnImage(watermark, versionString);
|
||||
|
||||
labelImage = new JLabel(new ImageIcon(watermark));
|
||||
}
|
||||
catch (Exception e) {
|
||||
// If something fails, we just show the SheepIt logo (without any watermark)
|
||||
ImageIcon image = new ImageIcon(logoURL);
|
||||
labelImage = new JLabel(image);
|
||||
}
|
||||
return labelImage;
|
||||
}
|
||||
|
||||
public enum ActivityType {
|
||||
WORKING, SETTINGS
|
||||
}
|
||||
|
||||
private SystemTray sysTray;
|
||||
private JPanel panel;
|
||||
private Working activityWorking;
|
||||
private Settings activitySettings;
|
||||
private TrayIcon trayIcon;
|
||||
private boolean useSysTray;
|
||||
private String title;
|
||||
|
||||
private int framesRendered;
|
||||
|
||||
private boolean waitingForAuthentication;
|
||||
private Client client;
|
||||
|
||||
private BufferedImage iconSprites;
|
||||
private BufferedImage[] trayIconSprites;
|
||||
|
||||
@Getter @Setter private SettingsLoader settingsLoader;
|
||||
|
||||
private ThreadClient threadClient;
|
||||
|
||||
public GuiSwing(boolean useSysTray_, String title_) {
|
||||
framesRendered = 0;
|
||||
useSysTray = useSysTray_;
|
||||
title = title_;
|
||||
waitingForAuthentication = true;
|
||||
|
||||
new Timer().scheduleAtFixedRate(new TimerTask() {
|
||||
@Override public void run() {
|
||||
if (activityWorking != null) {
|
||||
activityWorking.updateTime();
|
||||
}
|
||||
}
|
||||
}, 2 * 1000, 2 * 1000);
|
||||
}
|
||||
|
||||
@Override public void start() {
|
||||
if (useSysTray) {
|
||||
try {
|
||||
sysTray = SystemTray.getSystemTray();
|
||||
if (SystemTray.isSupported()) {
|
||||
addWindowStateListener(new WindowStateListener() {
|
||||
public void windowStateChanged(WindowEvent e) {
|
||||
if (e.getNewState() == ICONIFIED) {
|
||||
hideToTray();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
sysTray = null;
|
||||
}
|
||||
}
|
||||
|
||||
// load the images sprite and split into individual images
|
||||
URL spriteSequenceUrl = getClass().getResource("/icon-sprites.png");
|
||||
|
||||
if (spriteSequenceUrl != null) {
|
||||
try {
|
||||
iconSprites = ImageIO.read(spriteSequenceUrl);
|
||||
trayIconSprites = new BufferedImage[101 * 1]; // sprite sheet has 101 images in 1 column
|
||||
|
||||
setIconImage(extractImageFromSprite(-1)); // sprite 0 is standard Sheep It! icon
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
setTitle(title);
|
||||
setSize(520, 760);
|
||||
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
panel = new JPanel();
|
||||
panel.setLayout(new GridBagLayout());
|
||||
Dimension panelPrefSize = new Dimension(this.getSize().width - 20, this.getSize().height);
|
||||
panel.setPreferredSize(panelPrefSize);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(panel);
|
||||
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
|
||||
|
||||
//adjust scrollbar thickness
|
||||
scrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(13, 0));
|
||||
scrollPane.getHorizontalScrollBar().setPreferredSize(new Dimension(0, 13));
|
||||
|
||||
//adjust the speed of scrolling
|
||||
scrollPane.getVerticalScrollBar().setUnitIncrement(50);
|
||||
scrollPane.getHorizontalScrollBar().setUnitIncrement(50);
|
||||
|
||||
setContentPane(scrollPane);
|
||||
panel.setBorder(new EmptyBorder(20, 20, 20, 20));
|
||||
|
||||
activityWorking = new Working(this);
|
||||
activitySettings = new Settings(this);
|
||||
|
||||
this.showActivity(ActivityType.SETTINGS);
|
||||
|
||||
try {
|
||||
if (client.getConfiguration().getTheme().equals("light")) {
|
||||
UIManager.setLookAndFeel(new FlatLightLaf());
|
||||
}
|
||||
else if (client.getConfiguration().getTheme().equals("dark")) {
|
||||
UIManager.setLookAndFeel(new FlatDarkLaf());
|
||||
}
|
||||
|
||||
// Apply the selected theme to swing components
|
||||
SwingUtilities.invokeAndWait(() -> FlatLaf.updateUI());
|
||||
}
|
||||
catch (UnsupportedLookAndFeelException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
while (waitingForAuthentication) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
wait();
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@Override public void status(String msg_) {
|
||||
status(msg_, false);
|
||||
}
|
||||
|
||||
@Override public void status(String msg_, boolean overwriteSuspendedMsg) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.setStatus(msg_, overwriteSuspendedMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void status(String msg, int progress) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.setStatus(String.format("%s %d%%", msg, progress));
|
||||
}
|
||||
}
|
||||
|
||||
@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_);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void error(String msg_) {
|
||||
status(msg_, true);
|
||||
}
|
||||
|
||||
@Override public void setRemainingTime(String time_) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.setRemainingTime(time_);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void setRenderingTime(String time_) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.setRenderingTime(time_);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public synchronized void displayTransferStats(TransferStats downloads, TransferStats uploads) {
|
||||
this.activityWorking.displayTransferStats(downloads, uploads);
|
||||
}
|
||||
|
||||
@Override public void AddFrameRendered() {
|
||||
framesRendered++;
|
||||
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.setRenderedFrame(framesRendered);
|
||||
}
|
||||
else {
|
||||
System.out.println("GuiSwing::AddFrameRendered() error: no working activity");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void displayStats(Stats stats) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.displayStats(stats);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void displayUploadQueueStats(int queueSize, long queueVolume) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.displayUploadQueueStats(queueSize, queueVolume);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override public void setClient(Client cli) {
|
||||
client = cli;
|
||||
}
|
||||
|
||||
@Override public void setComputeMethod(String computeMethod) {
|
||||
this.activityWorking.setComputeMethod(computeMethod);
|
||||
}
|
||||
|
||||
public Configuration getConfiguration() {
|
||||
return client.getConfiguration();
|
||||
}
|
||||
|
||||
@Override public void successfulAuthenticationEvent(String publickey) {
|
||||
if (settingsLoader != null) {
|
||||
if (publickey != null && settingsLoader.getLogin().isLaunchCommand() == false) {
|
||||
settingsLoader.getPassword().setValue(publickey);
|
||||
}
|
||||
settingsLoader.saveFile();
|
||||
}
|
||||
}
|
||||
|
||||
public void setCredentials(String contentLogin, String contentPassword) {
|
||||
client.getConfiguration().setLogin(contentLogin);
|
||||
client.getConfiguration().setPassword(contentPassword);
|
||||
|
||||
waitingForAuthentication = false;
|
||||
synchronized (this) {
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
if (threadClient == null || threadClient.isAlive() == false) {
|
||||
threadClient = new ThreadClient();
|
||||
threadClient.start();
|
||||
}
|
||||
|
||||
showActivity(ActivityType.WORKING);
|
||||
}
|
||||
|
||||
public void showActivity(ActivityType type) {
|
||||
panel.removeAll();
|
||||
panel.doLayout();
|
||||
|
||||
if (type == ActivityType.WORKING) {
|
||||
activityWorking.show();
|
||||
}
|
||||
else if (type == ActivityType.SETTINGS) {
|
||||
activitySettings.show();
|
||||
}
|
||||
|
||||
setVisible(true);
|
||||
panel.repaint();
|
||||
}
|
||||
|
||||
public void hideToTray() {
|
||||
if (sysTray == null || SystemTray.isSupported() == false) {
|
||||
System.out.println("GuiSwing::hideToTray SystemTray not supported!");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
trayIcon = getTrayIcon();
|
||||
sysTray.add(trayIcon);
|
||||
}
|
||||
catch (AWTException e) {
|
||||
System.out.println("GuiSwing::hideToTray an error occured while trying to add system tray icon (exception: " + e + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
|
||||
}
|
||||
|
||||
public void restoreFromTray() {
|
||||
if (sysTray != null && SystemTray.isSupported()) {
|
||||
sysTray.remove(trayIcon);
|
||||
setVisible(true);
|
||||
setExtendedState(getExtendedState() & ~JFrame.ICONIFIED & JFrame.NORMAL); // for toFront and requestFocus to actually work
|
||||
toFront();
|
||||
requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
public TrayIcon getTrayIcon() {
|
||||
final PopupMenu trayMenu = new PopupMenu();
|
||||
|
||||
// on start, show the base icon
|
||||
Image img = extractImageFromSprite(-1);
|
||||
final TrayIcon icon = new TrayIcon(img);
|
||||
|
||||
MenuItem exit = new MenuItem("Exit");
|
||||
exit.addActionListener(new ActionListener() {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
trayMenu.add(exit);
|
||||
|
||||
MenuItem open = new MenuItem("Open...");
|
||||
open.addActionListener(new ActionListener() {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
restoreFromTray();
|
||||
}
|
||||
});
|
||||
trayMenu.add(open);
|
||||
|
||||
MenuItem settings = new MenuItem("Settings...");
|
||||
settings.addActionListener(new ActionListener() {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
restoreFromTray();
|
||||
showActivity(ActivityType.SETTINGS);
|
||||
}
|
||||
});
|
||||
trayMenu.add(settings);
|
||||
|
||||
icon.setPopupMenu(trayMenu);
|
||||
icon.setImageAutoSize(true);
|
||||
icon.setToolTip("SheepIt! Client");
|
||||
|
||||
icon.addActionListener(new ActionListener() {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
restoreFromTray();
|
||||
}
|
||||
});
|
||||
|
||||
return icon;
|
||||
|
||||
}
|
||||
|
||||
public JPanel getContentPanel() {
|
||||
return this.panel;
|
||||
}
|
||||
|
||||
private Image extractImageFromSprite(int spriteNumber) {
|
||||
// Sprite structure
|
||||
// Image 0: base sprite
|
||||
// Images 1-101: progress bar percentage from 0 to 100
|
||||
//
|
||||
// Always add +1 to the icon requested.
|
||||
// -1 turns into 0 (base sprite with no progress bar)
|
||||
// 0 to 101 turns into 1 to 101 (progress sequence starts in sprite 1 and ends on sprite 101)
|
||||
ImageIcon img = new ImageIcon(iconSprites.getSubimage(0, (spriteNumber + 1) * 114, 114, 114));
|
||||
|
||||
return img.getImage();
|
||||
}
|
||||
|
||||
@Override public void updateTrayIcon(Integer percentage) {
|
||||
// update the app icon on the app bar
|
||||
Image img = extractImageFromSprite(percentage);
|
||||
setIconImage(img);
|
||||
|
||||
// if the app supports the system tray, update as well
|
||||
if (sysTray != null && SystemTray.isSupported()) {
|
||||
if (trayIcon != null) {
|
||||
trayIcon.setImage(img);
|
||||
trayIcon.setImageAutoSize(true); // use this method to ensure that icon is refreshed when on
|
||||
// the tray
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ThreadClient extends Thread {
|
||||
@Override public void run() {
|
||||
if (GuiSwing.this.client != null) {
|
||||
GuiSwing.this.client.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
220
src/main/java/com/sheepit/client/standalone/GuiText.java
Normal file
220
src/main/java/com/sheepit/client/standalone/GuiText.java
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 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.standalone;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
import com.sheepit.client.Gui;
|
||||
import com.sheepit.client.Log;
|
||||
import com.sheepit.client.Stats;
|
||||
import com.sheepit.client.TransferStats;
|
||||
import com.sheepit.client.standalone.text.CLIInputActionHandler;
|
||||
import com.sheepit.client.standalone.text.CLIInputObserver;
|
||||
|
||||
import sun.misc.Signal;
|
||||
import sun.misc.SignalHandler;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
public class GuiText implements Gui {
|
||||
public static final String type = "text";
|
||||
|
||||
private int framesRendered;
|
||||
|
||||
private int sigIntCount = 0;
|
||||
private Log log;
|
||||
private DateFormat df;
|
||||
private String eta;
|
||||
|
||||
private Client client;
|
||||
|
||||
public GuiText() {
|
||||
this.framesRendered = 0;
|
||||
this.log = Log.getInstance(null);
|
||||
this.df = new SimpleDateFormat("MMM dd HH:mm:ss");
|
||||
this.eta = "";
|
||||
}
|
||||
|
||||
@Override public void start() {
|
||||
if (client != null) {
|
||||
|
||||
CLIInputObserver cli_input_observer = new CLIInputObserver(client);
|
||||
cli_input_observer.addListener(new CLIInputActionHandler());
|
||||
Thread cli_input_observer_thread = new Thread(cli_input_observer);
|
||||
cli_input_observer_thread.start();
|
||||
|
||||
Signal.handle(new Signal("INT"), new SignalHandler() {
|
||||
@Override public void handle(Signal signal) {
|
||||
sigIntCount++;
|
||||
|
||||
if (sigIntCount == 4) {
|
||||
// This is only for ugly issues that might occur
|
||||
System.out.println("WARNING: Hitting Ctrl-C again will force close the application.");
|
||||
}
|
||||
else if (sigIntCount == 5) {
|
||||
Signal.raise(new Signal("INT"));
|
||||
Runtime.getRuntime().halt(0);
|
||||
}
|
||||
else if (client.isRunning() && client.isSuspended() == false) {
|
||||
client.askForStop();
|
||||
System.out.println("Will exit after current frame... Press Ctrl+C again to exit now.");
|
||||
}
|
||||
else {
|
||||
client.stop();
|
||||
GuiText.this.stop();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
client.run();
|
||||
client.stop();
|
||||
}
|
||||
// stop if there is no client or client has been stopped
|
||||
this.stop();
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
Runtime.getRuntime().halt(0);
|
||||
}
|
||||
|
||||
@Override public void updateTrayIcon(Integer percentage) {
|
||||
}
|
||||
|
||||
@Override public void status(String msg_) {
|
||||
status(msg_, false);
|
||||
}
|
||||
|
||||
@Override public void status(String msg_, boolean overwriteSuspendedMsg) {
|
||||
log.debug("GUI " + msg_);
|
||||
|
||||
if (client != null && client.isSuspended()) {
|
||||
if (overwriteSuspendedMsg) {
|
||||
System.out.println(String.format("%s %s", this.df.format(new Date()), msg_));
|
||||
}
|
||||
}
|
||||
else {
|
||||
System.out.println(String.format("%s %s", this.df.format(new Date()), msg_));
|
||||
}
|
||||
}
|
||||
|
||||
@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)));
|
||||
}
|
||||
|
||||
@Override public void error(String err_) {
|
||||
System.out.println(String.format("ERROR: %s %s", this.df.format(new Date()), err_));
|
||||
log.error("Error " + err_);
|
||||
}
|
||||
|
||||
@Override public void AddFrameRendered() {
|
||||
this.framesRendered += 1;
|
||||
System.out.println(String.format("%s Frames rendered: %d", this.df.format(new Date()), this.framesRendered));
|
||||
}
|
||||
|
||||
@Override public synchronized void displayTransferStats(TransferStats downloads, TransferStats uploads) {
|
||||
System.out.println(String
|
||||
.format("%s Session downloads: %s @ %s/s / Uploads: %s @ %s/s", this.df.format(new Date()), downloads.getSessionTraffic(),
|
||||
downloads.getAverageSessionSpeed(), uploads.getSessionTraffic(), uploads.getAverageSessionSpeed()));
|
||||
}
|
||||
|
||||
@Override public void displayStats(Stats stats) {
|
||||
System.out.println(String.format("%s Frames remaining: %d", this.df.format(new Date()), stats.getRemainingFrame()));
|
||||
System.out.println(String.format("%s Credits earned: %d", this.df.format(new Date()), stats.getCreditsEarnedDuringSession()));
|
||||
}
|
||||
|
||||
@Override public void displayUploadQueueStats(int queueSize, long queueVolume) {
|
||||
// No need to check if the queue is not empty to show the volume bc this line is always shown at the end
|
||||
// of the render process in text GUI (unless an error occurred, where the file is uploaded synchronously)
|
||||
System.out.println(String.format("%s Queued uploads: %d (%.2fMB)", this.df.format(new Date()), queueSize, (queueVolume / 1024.0 / 1024.0)));
|
||||
}
|
||||
|
||||
@Override public void setRenderingProjectName(String name_) {
|
||||
if (name_ != null && name_.isEmpty() == false) {
|
||||
System.out.println(String.format("%s Rendering project \"%s\"", this.df.format(new Date()), name_));
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void setRemainingTime(String time_) {
|
||||
this.eta = time_;
|
||||
}
|
||||
|
||||
@Override public void setRenderingTime(String time_) {
|
||||
System.out.println(String.format("%s Rendering %s", this.df.format(new Date()), time_));
|
||||
}
|
||||
|
||||
@Override public void setClient(Client cli) {
|
||||
client = cli;
|
||||
}
|
||||
|
||||
@Override public void setComputeMethod(String computeMethod) {
|
||||
System.out.println(String.format("%s Compute method: %s", this.df.format(new Date()), computeMethod));
|
||||
}
|
||||
|
||||
@Override public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override public void successfulAuthenticationEvent(String publickey) {
|
||||
|
||||
}
|
||||
|
||||
private String showProgress(String message, int progress, long size) {
|
||||
StringBuilder progressBar = new StringBuilder(140);
|
||||
|
||||
if (progress < 100) {
|
||||
progressBar
|
||||
.append(message)
|
||||
.append(" ")
|
||||
.append(String.join("", Collections.nCopies(progress == 0 ? 2 : 2 - (int) (Math.log10(progress)), " ")))
|
||||
.append(String.format("%d%% [", progress))
|
||||
.append(String.join("", Collections.nCopies((int)(progress/5), "=")))
|
||||
.append('>')
|
||||
.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));
|
||||
}
|
||||
|
||||
progressBar.append(String.join("", Collections.nCopies(60 - progressBar.length(), " ")));
|
||||
}
|
||||
// If progress has reached 100%
|
||||
else {
|
||||
progressBar
|
||||
.append(message)
|
||||
.append(" done")
|
||||
.append(String.join("", Collections.nCopies(60 - progressBar.length(), " ")))
|
||||
.append("\n");
|
||||
}
|
||||
|
||||
return progressBar.toString();
|
||||
}
|
||||
}
|
||||
248
src/main/java/com/sheepit/client/standalone/GuiTextOneLine.java
Normal file
248
src/main/java/com/sheepit/client/standalone/GuiTextOneLine.java
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.standalone;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
import com.sheepit.client.Gui;
|
||||
import com.sheepit.client.Stats;
|
||||
import com.sheepit.client.TransferStats;
|
||||
import com.sheepit.client.standalone.text.CLIInputActionHandler;
|
||||
import com.sheepit.client.standalone.text.CLIInputObserver;
|
||||
|
||||
import sun.misc.Signal;
|
||||
import sun.misc.SignalHandler;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
public class GuiTextOneLine implements Gui {
|
||||
public static final String type = "oneLine";
|
||||
|
||||
private String project;
|
||||
private int rendered;
|
||||
private int remaining;
|
||||
private String creditsEarned;
|
||||
private int sigIntCount = 0;
|
||||
private DateFormat df;
|
||||
|
||||
private String computeMethod;
|
||||
private String status;
|
||||
private String line;
|
||||
private String eta;
|
||||
|
||||
private int uploadQueueSize;
|
||||
private long uploadQueueVolume;
|
||||
|
||||
private boolean exiting = false;
|
||||
|
||||
private Client client;
|
||||
|
||||
public GuiTextOneLine() {
|
||||
project = "";
|
||||
rendered = 0;
|
||||
remaining = 0;
|
||||
creditsEarned = null;
|
||||
status = "";
|
||||
computeMethod = "";
|
||||
line = "";
|
||||
uploadQueueSize = 0;
|
||||
uploadQueueVolume = 0;
|
||||
df = new SimpleDateFormat("MMM dd HH:mm:ss");
|
||||
eta = "";
|
||||
}
|
||||
|
||||
@Override public void start() {
|
||||
if (client != null) {
|
||||
|
||||
CLIInputObserver cli_input_observer = new CLIInputObserver(client);
|
||||
cli_input_observer.addListener(new CLIInputActionHandler());
|
||||
Thread cli_input_observer_thread = new Thread(cli_input_observer);
|
||||
cli_input_observer_thread.start();
|
||||
|
||||
Signal.handle(new Signal("INT"), new SignalHandler() {
|
||||
@Override public void handle(Signal signal) {
|
||||
sigIntCount++;
|
||||
|
||||
if (sigIntCount == 5) {
|
||||
Signal.raise(new Signal("INT"));
|
||||
Runtime.getRuntime().halt(0);
|
||||
}
|
||||
else if (client.isRunning() && client.isSuspended() == false) {
|
||||
client.askForStop();
|
||||
exiting = true;
|
||||
}
|
||||
else {
|
||||
client.stop();
|
||||
GuiTextOneLine.this.stop();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
client.run();
|
||||
client.stop();
|
||||
}
|
||||
// stop if there is no client or client has been stopped
|
||||
this.stop();
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
Runtime.getRuntime().halt(0);
|
||||
}
|
||||
|
||||
@Override public void updateTrayIcon(Integer percentage) {
|
||||
}
|
||||
|
||||
@Override public void status(String msg_) {
|
||||
status(msg_, false);
|
||||
}
|
||||
|
||||
@Override public void status(String msg_, boolean overwriteSuspendedMsg) {
|
||||
if (client != null && client.isSuspended()) {
|
||||
if (overwriteSuspendedMsg) {
|
||||
status = msg_;
|
||||
updateLine();
|
||||
}
|
||||
}
|
||||
else {
|
||||
status = msg_;
|
||||
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);
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public void setRenderingProjectName(String name_) {
|
||||
if (name_ == null || name_.isEmpty()) {
|
||||
project = "";
|
||||
}
|
||||
else {
|
||||
project = name_ + " |";
|
||||
}
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public void error(String msg_) {
|
||||
status = "Error " + msg_;
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public void AddFrameRendered() {
|
||||
rendered += 1;
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public synchronized void displayTransferStats(TransferStats downloads, TransferStats uploads) {
|
||||
// Session traffic stats not shown in the 1 line UI to avoid increasing the line length above 120 chars
|
||||
}
|
||||
|
||||
@Override public void displayStats(Stats stats) {
|
||||
remaining = stats.getRemainingFrame();
|
||||
creditsEarned = String.valueOf(stats.getCreditsEarnedDuringSession());
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public void displayUploadQueueStats(int queueSize, long queueVolume) {
|
||||
this.uploadQueueSize = queueSize;
|
||||
this.uploadQueueVolume = queueVolume;
|
||||
}
|
||||
|
||||
@Override public void setRemainingTime(String time_) {
|
||||
this.eta = time_;
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public void setRenderingTime(String time_) {
|
||||
status = "Rendering " + time_;
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public void setClient(Client cli) {
|
||||
client = cli;
|
||||
}
|
||||
|
||||
@Override public void setComputeMethod(String computeMethod_) {
|
||||
computeMethod = computeMethod_;
|
||||
}
|
||||
|
||||
@Override public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override public void successfulAuthenticationEvent(String publickey) {
|
||||
|
||||
}
|
||||
|
||||
private void updateLine() {
|
||||
int charToRemove = line.length();
|
||||
|
||||
System.out.print("\r");
|
||||
|
||||
line = String.format("%s Frames: %d Points: %s | Upload Queue: %d%s | %%s %s %s", df.format(new Date()), rendered,
|
||||
creditsEarned != null ? creditsEarned : "unknown", this.uploadQueueSize,
|
||||
(this.uploadQueueSize > 0 ? String.format(" (%.2fMB)", (this.uploadQueueVolume / 1024.0 / 1024.0)) : ""), computeMethod,
|
||||
status + (exiting ? " (Exiting after all frames are uploaded)" : ""));
|
||||
|
||||
if (line.length() + project.length() > 120) {
|
||||
// If the line without the project name is already >120 characters (might happen if the user has thousands of frames and millions of points in the
|
||||
// session + is exiting after all frames are uploaded) then set the line to 117c to avoid a negative number exception in substring function
|
||||
int lineLength = Math.min(line.length(), 117);
|
||||
line = String.format(line, project.substring(0, 117 - lineLength) + "...");
|
||||
}
|
||||
else {
|
||||
line = String.format(line, project);
|
||||
}
|
||||
|
||||
System.out.print(line);
|
||||
for (int i = line.length(); i <= charToRemove; i++) {
|
||||
System.out.print(" ");
|
||||
}
|
||||
}
|
||||
|
||||
private String showProgress(String message, int progress, long size) {
|
||||
StringBuilder progressBar = new StringBuilder(140);
|
||||
progressBar
|
||||
.append(message)
|
||||
.append(String.join("", Collections.nCopies(progress == 0 ? 2 : 2 - (int) (Math.log10(progress)), " ")))
|
||||
.append(String.format(" %d%%%% [", progress))
|
||||
.append(String.join("", Collections.nCopies((int) (progress / 10), "=")))
|
||||
.append('>')
|
||||
.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));
|
||||
}
|
||||
|
||||
return progressBar.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.standalone;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.kohsuke.args4j.CmdLineException;
|
||||
import org.kohsuke.args4j.CmdLineParser;
|
||||
import org.kohsuke.args4j.OptionDef;
|
||||
import org.kohsuke.args4j.spi.OptionHandler;
|
||||
import org.kohsuke.args4j.spi.Parameters;
|
||||
import org.kohsuke.args4j.spi.Setter;
|
||||
|
||||
import com.sheepit.client.Configuration;
|
||||
import com.sheepit.client.hardware.gpu.GPU;
|
||||
import com.sheepit.client.hardware.gpu.GPUDevice;
|
||||
|
||||
public class ListGpuParameterHandler<T> extends OptionHandler<T> {
|
||||
public ListGpuParameterHandler(CmdLineParser parser, OptionDef option, Setter<? super T> setter) {
|
||||
super(parser, option, setter);
|
||||
}
|
||||
|
||||
@Override public int parseArguments(Parameters params) throws CmdLineException {
|
||||
List<GPUDevice> gpus = GPU.listDevices(new Configuration(null, null, null));
|
||||
if (gpus != null) {
|
||||
for (GPUDevice gpu : gpus) {
|
||||
System.out.println("GPU_ID : " + gpu.getOldId());
|
||||
System.out.println("Long ID : " + gpu.getId());
|
||||
System.out.println("Model : " + gpu.getModel());
|
||||
System.out.println("Memory, MB: " + (int) (gpu.getMemory() / (1024 * 1024)));
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public String getDefaultMetaVariable() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.standalone;
|
||||
|
||||
import org.kohsuke.args4j.CmdLineException;
|
||||
import org.kohsuke.args4j.CmdLineParser;
|
||||
import org.kohsuke.args4j.OptionDef;
|
||||
import org.kohsuke.args4j.spi.OptionHandler;
|
||||
import org.kohsuke.args4j.spi.Parameters;
|
||||
import org.kohsuke.args4j.spi.Setter;
|
||||
|
||||
import com.sheepit.client.Configuration;
|
||||
|
||||
public class VersionParameterHandler<T> extends OptionHandler<T> {
|
||||
public VersionParameterHandler(CmdLineParser parser, OptionDef option, Setter<? super T> setter) {
|
||||
super(parser, option, setter);
|
||||
}
|
||||
|
||||
@Override public int parseArguments(Parameters params) throws CmdLineException {
|
||||
Configuration config = new Configuration(null, "", "");
|
||||
System.out.println("Version: " + Configuration.jarVersion);
|
||||
System.exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public String getDefaultMetaVariable() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
514
src/main/java/com/sheepit/client/standalone/Worker.java
Normal file
514
src/main/java/com/sheepit/client/standalone/Worker.java
Normal file
@@ -0,0 +1,514 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 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.standalone;
|
||||
|
||||
import org.kohsuke.args4j.CmdLineException;
|
||||
import org.kohsuke.args4j.CmdLineParser;
|
||||
import org.kohsuke.args4j.Option;
|
||||
import org.kohsuke.args4j.OptionHandlerFilter;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Calendar;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
import com.sheepit.client.Configuration;
|
||||
import com.sheepit.client.Configuration.ComputeType;
|
||||
import com.sheepit.client.Error;
|
||||
import com.sheepit.client.Gui;
|
||||
import com.sheepit.client.Log;
|
||||
import com.sheepit.client.Pair;
|
||||
import com.sheepit.client.SettingsLoader;
|
||||
import com.sheepit.client.ShutdownHook;
|
||||
import com.sheepit.client.Utils;
|
||||
import com.sheepit.client.hardware.gpu.GPU;
|
||||
import com.sheepit.client.hardware.gpu.GPUDevice;
|
||||
import com.sheepit.client.hardware.gpu.nvidia.Nvidia;
|
||||
import com.sheepit.client.hardware.gpu.opencl.OpenCL;
|
||||
import com.sheepit.client.network.Proxy;
|
||||
import com.sheepit.client.os.OS;
|
||||
|
||||
public class Worker {
|
||||
@Option(name = SettingsLoader.ARG_SERVER, usage = "Render-farm server, default https://client.sheepit-renderfarm.com", metaVar = "URL", required = false) private String server = "https://client.sheepit-renderfarm.com";
|
||||
|
||||
@Option(name = SettingsLoader.ARG_LOGIN, usage = "User's login", metaVar = "LOGIN", required = false) private String login = "";
|
||||
|
||||
@Option(name = SettingsLoader.ARG_PASSWORD, usage = "User's password or public key (accessible under the Keys tab of the profile page)", metaVar = "PASSWORD", required = false) private String password = "";
|
||||
|
||||
@Option(name = SettingsLoader.ARG_CACHE_DIR, usage = "Cache/Working directory. Caution, everything in it not related to the render-farm will be removed", metaVar = "/tmp/cache", required = false) private String cache_dir = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_SHARED_ZIP, usage = "Shared directory for downloaded binaries and scenes. Useful when running two or more clients in the same computer/network to download once and render many times. IMPORTANT: This option and value must be identical in ALL clients sharing the directory.", required = false) private String sharedDownloadsDir = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_GPU, usage = "Name of the GPU used for the render, for example CUDA_0 for Nvidia or OPENCL_0 for AMD/Intel card", metaVar = "CUDA_0", required = false) private String gpu_device = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_NO_GPU, usage = "Don't detect GPUs", required = false) private boolean no_gpu_detection = false;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_COMPUTE_METHOD, usage = "CPU: only use cpu, GPU: only use gpu, CPU_GPU: can use cpu and gpu (not at the same time) if -gpu is not use it will not use the gpu", metaVar = "CPU", required = false) private String method = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_CORES, usage = "Number of cores/threads to use for the render. The minimum is two cores unless your system only has one", metaVar = "3", required = false) private int nb_cores = -1;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_MEMORY, usage = "Maximum memory allow to be used by renderer, number with unit (800M, 2G, ...)", required = false) private String max_ram = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_RENDERTIME, usage = "Maximum time allow for each frame (in minutes)", required = false) private int max_rendertime = -1;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_VERBOSE, usage = "Display log", required = false) private boolean print_log = false;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_REQUEST_TIME, usage = "H1:M1-H2:M2,H3:M3-H4:M4 Use the 24h format. For example to request job between 2am-8.30am and 5pm-11pm you should do --request-time 2:00-8:30,17:00-23:00 Caution, it's the requesting job time to get a project, not the working time", metaVar = "2:00-8:30,17:00-23:00", required = false) private String request_time = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_SHUTDOWN, usage = "Specify when the client will close and the host computer will shut down in a proper way. The time argument can have two different formats: an absolute date and time in the format yyyy-mm-ddThh:mm:ss (24h format) or a relative time in the format +m where m is the number of minutes from now.", metaVar = "DATETIME or +N", required = false) private String shutdown = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_SHUTDOWN_MODE, usage = "Indicates if the shutdown process waits for the upload queue to finish (wait) or interrupt all the pending tasks immediately (hard). The default shutdown mode is wait.", metaVar = "MODE", required = false) private String shutdownMode = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_PROXY, usage = "URL of the proxy", metaVar = "http://login:password@host:port", required = false) private String proxy = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_EXTRAS, usage = "Extras data push on the authentication request", required = false) private String extras = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_UI, usage = "Specify the user interface to use, default '" + GuiSwing.type + "', available '" + GuiTextOneLine.type + "', '"
|
||||
+ GuiText.type + "', '" + GuiSwing.type + "' (graphical)", required = false) private String ui_type = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_CONFIG, usage = "Specify the configuration file", required = false) private String config_file = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_VERSION, usage = "Display application version", required = false, handler = VersionParameterHandler.class) private VersionParameterHandler versionHandler;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_SHOW_GPU, usage = "Print available GPU devices and exit", required = false, handler = ListGpuParameterHandler.class) private ListGpuParameterHandler listGpuParameterHandler;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_NO_SYSTRAY, usage = "Don't use SysTray", required = false) private boolean useSysTray = false;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_PRIORITY, usage = "Set render process priority (19 lowest to -19 highest)", required = false) private int priority = 19;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_TITLE, usage = "Custom title for the GUI Client", required = false) private String title = "SheepIt Render Farm";
|
||||
|
||||
@Option(name = SettingsLoader.ARG_THEME, usage = "Specify the theme to use for the graphical client, default 'light', available 'light', 'dark'", required = false) private String theme = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_HOSTNAME, usage = "Set a custom hostname name (name change will be lost when client is closed)", required = false) private String hostname = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_HEADLESS, usage = "Mark your client manually as headless to block Eevee projects", required = false) private boolean headless = java.awt.GraphicsEnvironment.isHeadless();
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (OS.getOS() == null) {
|
||||
System.err.println(Error.humanString(Error.Type.OS_NOT_SUPPORTED));
|
||||
System.exit(1);
|
||||
}
|
||||
new Worker().doMain(args);
|
||||
}
|
||||
|
||||
public void doMain(String[] args) {
|
||||
CmdLineParser parser = new CmdLineParser(this);
|
||||
try {
|
||||
parser.parseArgument(args);
|
||||
}
|
||||
catch (CmdLineException e) {
|
||||
System.err.println(e.getMessage());
|
||||
System.err.println("Usage: ");
|
||||
parser.printUsage(System.err);
|
||||
System.err.println();
|
||||
System.err.println("Example: java " + this.getClass().getName() + " " + parser.printExample(OptionHandlerFilter.REQUIRED));
|
||||
return;
|
||||
}
|
||||
|
||||
ComputeType compute_method = null;
|
||||
Configuration config = new Configuration(null, login, password);
|
||||
config.setPrintLog(print_log);
|
||||
config.setUsePriority(priority);
|
||||
config.setDetectGPUs(!no_gpu_detection);
|
||||
|
||||
if (sharedDownloadsDir != null) {
|
||||
File dir = new File(sharedDownloadsDir);
|
||||
if (dir.exists() == false && dir.mkdirs()) {
|
||||
Log.getInstance(config).debug("created shared-zip directory " + dir);
|
||||
}
|
||||
else if (dir.exists() == false) {
|
||||
System.err.println("ERROR: The shared-zip directory " + dir + " does not exist and cannot be automatically created");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dir.canWrite() == false) {
|
||||
System.err.println("ERROR: The shared-zip directory " + dir + " must be writeable");
|
||||
return;
|
||||
}
|
||||
config.setSharedDownloadsDirectory(dir);
|
||||
}
|
||||
|
||||
if (cache_dir != null) {
|
||||
Pattern cache_dirValidator = Pattern.compile("^(\\/|\\\\|[a-z]:)?[a-z0-9\\/\\\\\\s-_.]+$",Pattern.CASE_INSENSITIVE);
|
||||
Matcher cache_dirCandidate = cache_dirValidator.matcher(cache_dir);
|
||||
|
||||
if (cache_dirCandidate.find()) {
|
||||
|
||||
File a_dir = new File(cache_dir);
|
||||
a_dir.mkdirs();
|
||||
if (a_dir.isDirectory() && a_dir.canWrite()) {
|
||||
config.setCacheDir(a_dir);
|
||||
}
|
||||
else {
|
||||
System.err.println("ERROR: The entered cache path is either not a directory or is not writable!");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
System.err.println("ERROR: The entered cache path (-cache-dir parameter) contains invalid characters. Allowed characters are a-z, A-Z, 0-9, /, \\, ., - and _");
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
// We set a hard limit of 3 concurrent uploads. As the server doesn't allocate more than 3 concurrent jobs to
|
||||
// a single session to avoid any client taking too many frames and not validating them, we throttle the uploads.
|
||||
// If we don't set this limit, in a computer with slow uploads the server will return a "no job available" when
|
||||
// the 4th concurrent job is requested and that will put the client in "wait" mode for some random time. To
|
||||
// avoid that situation we set this limit.
|
||||
config.setMaxUploadingJob(3);
|
||||
|
||||
// Store the SysTray preference from the user. Please note that we must ! the value of the variable because the way args4j works. If the --no-systray
|
||||
// parameter is detected, args4j will store (boolean)true in the useSysTray variable but we want to store (boolean)false in the configuration class
|
||||
// for further checks.
|
||||
config.setUseSysTray(!useSysTray);
|
||||
|
||||
config.setHeadless(headless);
|
||||
|
||||
if (gpu_device != null) {
|
||||
if (gpu_device.startsWith(Nvidia.TYPE) == false && gpu_device.startsWith(OpenCL.TYPE) == false) {
|
||||
System.err.println("ERROR: The entered GPU_ID is invalid. The GPU_ID should look like '" + Nvidia.TYPE + "_#' or '" + OpenCL.TYPE
|
||||
+ "_#'. Please use the proper GPU_ID from the GPU list below\n");
|
||||
showGPUList(parser);
|
||||
}
|
||||
|
||||
GPUDevice gpu = GPU.getGPUDevice(gpu_device);
|
||||
if (gpu == null) {
|
||||
System.err.println("ERROR: The entered GPU_ID is invalid. Please use the proper GPU_ID from the GPU list below\n");
|
||||
showGPUList(parser);
|
||||
}
|
||||
config.setGPUDevice(gpu);
|
||||
}
|
||||
|
||||
if (request_time != null) {
|
||||
String[] intervals = request_time.split(",");
|
||||
if (intervals != null) {
|
||||
config.setRequestTime(new LinkedList<Pair<Calendar, Calendar>>());
|
||||
|
||||
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm");
|
||||
for (String interval : intervals) {
|
||||
String[] times = interval.split("-");
|
||||
if (times != null && times.length == 2) {
|
||||
Calendar start = Calendar.getInstance();
|
||||
Calendar end = Calendar.getInstance();
|
||||
|
||||
try {
|
||||
start.setTime(timeFormat.parse(times[0]));
|
||||
end.setTime(timeFormat.parse(times[1]));
|
||||
}
|
||||
catch (ParseException e) {
|
||||
System.err.println(String.format(
|
||||
"ERROR: The entered time slot (-request-time parameter) doesn't seem to be valid. Please check the format is correct [%s]",
|
||||
e.getMessage()));
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
if (start.before(end)) {
|
||||
config.getRequestTime().add(new Pair<Calendar, Calendar>(start, end));
|
||||
}
|
||||
else {
|
||||
System.err.println(String.format("ERROR: The start (%s) time must be earlier than the finish (%s) time", times[0], times[1]));
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nb_cores < -1 || nb_cores == 0) { // -1 is the default
|
||||
System.err.println("ERROR: The entered number of CPU cores (-cores parameter) is not valid. Please enter a number greater than zero");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
config.setNbCores(nb_cores);
|
||||
}
|
||||
|
||||
if (max_ram != null) {
|
||||
try {
|
||||
config.setMaxAllowedMemory(Utils.parseNumber(max_ram) / 1024); // internal value is in KiB
|
||||
}
|
||||
catch (java.lang.IllegalStateException e) {
|
||||
System.err.println(
|
||||
String.format("ERROR: The entered value of maximum memory (-memory parameter) doesn't seem to be a valid number [%s]", e.getMessage()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (max_rendertime > 0) {
|
||||
config.setMaxRenderTime(max_rendertime * 60);
|
||||
}
|
||||
|
||||
if (method != null) {
|
||||
try {
|
||||
compute_method = ComputeType.valueOf(method);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
System.err.println(String.format(
|
||||
"ERROR: The entered compute method (-compute-method parameter) is not valid. Available values are CPU, GPU or CPU_GPU [%s]",
|
||||
e.getMessage()));
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (config.getGPUDevice() != null) {
|
||||
compute_method = ComputeType.GPU;
|
||||
}
|
||||
}
|
||||
|
||||
if (proxy != null) {
|
||||
try {
|
||||
Proxy.set(proxy);
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
System.err.println(String.format("ERROR: The entered proxy URL (-proxy parameter) doesn't seem to have the right format. Please check it [%s]",
|
||||
e.getMessage()));
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (extras != null) {
|
||||
config.setExtras(extras);
|
||||
}
|
||||
|
||||
if (compute_method != null) {
|
||||
if (compute_method == ComputeType.CPU && config.getGPUDevice() != null) {
|
||||
System.err.println(
|
||||
"ERROR: The compute method is set to use CPU only, but a GPU has also been specified. Change the compute method to CPU_GPU or remove the GPU");
|
||||
System.exit(2);
|
||||
}
|
||||
else if (compute_method == ComputeType.CPU_GPU && config.getGPUDevice() == null) {
|
||||
System.err.println(
|
||||
"ERROR: The compute method is set to use both CPU and GPU, but no GPU has been specified. Change the compute method to CPU or add a GPU (via -gpu parameter)");
|
||||
System.exit(2);
|
||||
}
|
||||
else if (compute_method == ComputeType.GPU && config.getGPUDevice() == null) {
|
||||
System.err.println("ERROR: The compute method is set to use GPU only, but not GPU has been specified. Please add a GPU (via -gpu parameter)");
|
||||
System.exit(2);
|
||||
}
|
||||
else if (compute_method == ComputeType.CPU) {
|
||||
config.setGPUDevice(null); // remove the GPU
|
||||
}
|
||||
}
|
||||
|
||||
config.setComputeMethod(compute_method);
|
||||
|
||||
if (ui_type != null) {
|
||||
config.setUIType(ui_type);
|
||||
}
|
||||
|
||||
if (theme != null) {
|
||||
if (!theme.equals("light") && !theme.equals("dark")) {
|
||||
System.err.println("ERROR: The entered theme (-theme parameter) doesn't exist. Please choose either 'light' or 'dark'");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
config.setTheme(this.theme);
|
||||
}
|
||||
|
||||
// Shutdown process block
|
||||
if (shutdown != null) {
|
||||
Pattern absoluteTimePattern = Pattern.compile("^([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))T([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$");
|
||||
Pattern relativeTimePattern = Pattern.compile("^\\+([0-9]{2,4})$");
|
||||
LocalDateTime shutdownTime = null;
|
||||
|
||||
Matcher timeAbsolute = absoluteTimePattern.matcher(shutdown);
|
||||
Matcher timeRelative = relativeTimePattern.matcher(shutdown);
|
||||
|
||||
if (timeAbsolute.find()) {
|
||||
if ((shutdownTime = shutdownTimeParse(shutdown)) != null) {
|
||||
long diffInMillies = ChronoUnit.MILLIS.between(LocalDateTime.now(), shutdownTime);
|
||||
|
||||
if (diffInMillies < 0) {
|
||||
System.err.println(String
|
||||
.format("\nERROR: The entered shutdown time (%s) is a date on the past. Shutdown time must be at least 30 minutes from now",
|
||||
shutdown));
|
||||
System.err.println("Aborting");
|
||||
System.exit(2);
|
||||
}
|
||||
else if (diffInMillies < 10 * 60 * 1000) { // 10 minutes
|
||||
System.err.println(String.format(
|
||||
"\nERROR: The specified shutdown time (%s) is expected to happen in less than 10 minutes. Shutdown time must be at least 30 minutes from now",
|
||||
shutdown));
|
||||
System.err.println("Aborting");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
config.setShutdownTime(diffInMillies);
|
||||
}
|
||||
else {
|
||||
System.err.println(String.format(
|
||||
"\nERROR: The format of the entered shutdown time (%s) is not correct.\nThe time argument can have two different formats: an absolute date and time in the format yyyy-mm-ddThh:mm:ss (24h format) or a relative time in the format +m where m is the number of minutes from now (min. +10 minutes, max. +9999 minutes)",
|
||||
shutdown));
|
||||
System.err.println("Aborting");
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
else if (timeRelative.find()) {
|
||||
int minutesUntilShutdown = Integer.parseInt(timeRelative.group(1));
|
||||
config.setShutdownTime(minutesUntilShutdown * 60 * 1000);
|
||||
shutdownTime = LocalDateTime.now().plusMinutes(minutesUntilShutdown);
|
||||
}
|
||||
else {
|
||||
System.err.println(String.format(
|
||||
"\nERROR: The time especified (%s) is less than 10 minutes or the format is not correct.\nThe time argument can have two different formats: an absolute date and time in the format yyyy-mm-ddThh:mm:ss (24h format) or a relative time in the format +m where m is the number of minutes from now (min. +10 minutes, max. +9999 minutes)",
|
||||
shutdown));
|
||||
System.err.println("Aborting");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
if (shutdownMode != null) {
|
||||
if (shutdownMode.toLowerCase().equals("wait") || shutdownMode.toLowerCase().equals("hard")) {
|
||||
config.setShutdownMode(shutdownMode.toLowerCase());
|
||||
}
|
||||
else {
|
||||
System.err
|
||||
.println(String.format("ERROR: The entered shutdown-mode (%s) is invalid. Please enter wait or hard shutdown mode.", shutdownMode));
|
||||
System.err.println(" - Wait: the shutdown process is initiated once the current job and all the queued uploads are finished.");
|
||||
System.err
|
||||
.println(" - Hard: Then shutdown process is executed immediately. Any ongoing rendering process or upload queues will be cancelled.");
|
||||
System.err.println("Aborting");
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// if no shutdown mode specified, then set "wait" mode by default
|
||||
config.setShutdownMode("wait");
|
||||
}
|
||||
|
||||
System.out.println("==============================================================================");
|
||||
if (config.getShutdownMode().equals("wait")) {
|
||||
System.out.println(String.format(
|
||||
"WARNING!\n\nThe client will stop requesting new jobs at %s.\nTHE EFFECTIVE SHUTDOWN MIGHT OCCUR LATER THAN THE REQUESTED TIME AS THE UPLOAD\nQUEUE MUST BE FULLY UPLOADED BEFORE THE SHUTDOWN PROCESS STARTS.\n\nIf you want to shutdown the computer sharp at the specified time, please\ninclude the '-shutdown-mode hard' parameter in the application call",
|
||||
shutdownTime));
|
||||
}
|
||||
else {
|
||||
System.out.println(String.format(
|
||||
"WARNING!\n\nThe client will initiate the shutdown process at %s.\nALL RENDERS IN PROGRESS AND UPLOAD QUEUES WILL BE CANCELED.\n\nIf you prefer to shutdown the computer once the pending jobs are completed,\nplease include the '-shutdown-mode wait' parameter in the application call",
|
||||
shutdownTime));
|
||||
}
|
||||
System.out.println("==============================================================================\n");
|
||||
}
|
||||
else if (shutdown == null && shutdownMode != null) {
|
||||
System.err.println(
|
||||
"ERROR: The shutdown-mode parameter cannot be entered alone. Please make sure that you also enter a valid shutdown time (using -shutdown parameter)");
|
||||
System.err.println("Aborting");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
if (config_file != null) {
|
||||
if (new File(config_file).exists() == false) {
|
||||
System.err.println(
|
||||
"ERROR: The entered configuration file (-config parameter) cannot be loaded. Please check that you've entered an existing filename");
|
||||
System.exit(2);
|
||||
}
|
||||
config.setConfigFilePath(config_file);
|
||||
}
|
||||
|
||||
SettingsLoader settingsLoader = new SettingsLoader(config_file);
|
||||
settingsLoader.merge(config, true);
|
||||
|
||||
if (args.length > 0) {
|
||||
settingsLoader.markLaunchSettings(List.of(args));
|
||||
}
|
||||
|
||||
Log.getInstance(config).debug("client version " + Configuration.jarVersion);
|
||||
|
||||
// Hostname change will overwrite the existing one (default or read from configuration file) but changes will be lost when the client closes
|
||||
if (hostname != null) {
|
||||
Pattern hostnameValidator = Pattern.compile("[^a-z0-9-_]", Pattern.CASE_INSENSITIVE);
|
||||
Matcher hostnameCandidate = hostnameValidator.matcher(hostname);
|
||||
|
||||
if (hostnameCandidate.find()) {
|
||||
System.err.println(
|
||||
"ERROR: The entered hostname (-hostname parameter) contains invalid characters. Allowed hostname characters are a-z, A-Z, 0-9, - and _");
|
||||
System.exit(2);
|
||||
}
|
||||
else {
|
||||
config.setHostname(hostname);
|
||||
}
|
||||
}
|
||||
|
||||
Gui gui;
|
||||
String type = config.getUIType();
|
||||
if (type == null) {
|
||||
type = "swing";
|
||||
}
|
||||
switch (type) {
|
||||
case GuiTextOneLine.type:
|
||||
if (config.isPrintLog()) {
|
||||
System.err.println(
|
||||
"ERROR: The oneLine UI and the --verbose parameter cannot be used at the same time. Please either change the ui to text or remove the verbose mode");
|
||||
System.exit(2);
|
||||
}
|
||||
gui = new GuiTextOneLine();
|
||||
break;
|
||||
case GuiText.type:
|
||||
gui = new GuiText();
|
||||
break;
|
||||
default:
|
||||
case GuiSwing.type:
|
||||
if (java.awt.GraphicsEnvironment.isHeadless()) {
|
||||
System.err.println("ERROR: Your current configuration doesn't support graphical UI.");
|
||||
System.err.println("Please use one of the text-based UIs provided (using -ui " + GuiTextOneLine.type + " or -ui " + GuiText.type + ")");
|
||||
System.exit(3);
|
||||
}
|
||||
gui = new GuiSwing(config.isUseSysTray(), title);
|
||||
((GuiSwing) gui).setSettingsLoader(settingsLoader);
|
||||
break;
|
||||
}
|
||||
Client cli = new Client(gui, config, server);
|
||||
gui.setClient(cli);
|
||||
ShutdownHook hook = new ShutdownHook(cli);
|
||||
hook.attachShutDownHook();
|
||||
|
||||
gui.start();
|
||||
}
|
||||
|
||||
private void showGPUList(CmdLineParser parser) {
|
||||
try {
|
||||
parser.parseArgument("--show-gpu");
|
||||
}
|
||||
catch (CmdLineException e) {
|
||||
System.err.println(String.format("ERROR: Unable to parse the provided parameter [%s]", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDateTime shutdownTimeParse(String shutdownTime) {
|
||||
try {
|
||||
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
|
||||
return LocalDateTime.parse(shutdownTime, df);
|
||||
}
|
||||
catch (DateTimeParseException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.sheepit.client.standalone.swing;
|
||||
|
||||
public enum SwingTooltips {
|
||||
WORKING_DIRECTORY(
|
||||
"Where Sheepit stores things like downloaded projects, finished frames etc. Unless you want to free up your C: drive, Auto detected will do just fine."),
|
||||
|
||||
COMPUTE_DEVICES("What Sheepit will use to render. Note that only one device can be active at a time, e.g. sometimes you get jobs for your \n"
|
||||
+ "GPU so your CPU takes a break, sometimes it's the other way around. The only way to always use 100% of your system \n"
|
||||
+ "is to setup 2 clients, but for that you need to use the command line to give them different configs."),
|
||||
|
||||
CPU_CORES("How many (logical) cores of your CPU, often called threads, Sheepit may use. This doesn't apply to GPU-jobs.\n"
|
||||
+ "(Note that GPU jobs will also use CPU cores for scene building and feeding the GPU)\n"),
|
||||
|
||||
MEMORY("How much RAM Sheepit may use. This isn't a 100% safe limit, since Blender can erroneously use more, \n"
|
||||
+ "but Sheepit will try its best to give you jobs that require less than what you've entered here."),
|
||||
|
||||
PRIORITY("Which priority in your system the rendering process should have."),
|
||||
|
||||
MINIMIZE_TO_SYSTRAY("Whether Sheepit should vanish into your system tray (the icons next to the clock in the bottom right) when you minimize the window."),
|
||||
|
||||
PROXY("If you don't know what this does, you don't need it. Useful for example if you're in a company network with restricted access."),
|
||||
|
||||
COMPUTER_NAME("What this machine will be displayed as on your Sheepit profile page. Only you and admins can see this."),
|
||||
|
||||
MAX_TIME_PER_FRAME("How much time a frame should take at most. Sheepit will try to assign jobs to your machine that take less time to compute.");
|
||||
|
||||
|
||||
private final String explanation;
|
||||
|
||||
private SwingTooltips(String explanation) {
|
||||
this.explanation = explanation;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return explanation;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.standalone.swing.activity;
|
||||
|
||||
public interface Activity {
|
||||
|
||||
public void show();
|
||||
|
||||
public void resizeWindow();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,833 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.standalone.swing.activity;
|
||||
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Hashtable;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPasswordField;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.FlatLightLaf;
|
||||
import com.formdev.flatlaf.FlatDarkLaf;
|
||||
|
||||
import com.sheepit.client.Configuration;
|
||||
import com.sheepit.client.Configuration.ComputeType;
|
||||
import com.sheepit.client.hardware.cpu.CPU;
|
||||
import com.sheepit.client.hardware.gpu.GPU;
|
||||
import com.sheepit.client.hardware.gpu.GPUDevice;
|
||||
import com.sheepit.client.hardware.gpu.GPULister;
|
||||
import com.sheepit.client.hardware.gpu.nvidia.Nvidia;
|
||||
import com.sheepit.client.hardware.gpu.opencl.OpenCL;
|
||||
import com.sheepit.client.network.Proxy;
|
||||
import com.sheepit.client.os.OS;
|
||||
import com.sheepit.client.standalone.GuiSwing;
|
||||
import com.sheepit.client.standalone.swing.SwingTooltips;
|
||||
import com.sheepit.client.standalone.swing.components.CollapsibleJPanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class Settings implements Activity {
|
||||
private static final String DUMMY_CACHE_DIR = "Auto detected";
|
||||
|
||||
private GuiSwing parent;
|
||||
|
||||
private JTextField login;
|
||||
private JPasswordField password;
|
||||
private JLabel cacheDirText;
|
||||
private File cacheDir;
|
||||
private JFileChooser cacheDirChooser;
|
||||
private JCheckBox useCPU;
|
||||
private List<JCheckBoxGPU> useGPUs;
|
||||
private JCheckBox useSysTray;
|
||||
private JCheckBox headlessCheckbox;
|
||||
private JSlider cpuCores;
|
||||
private JSlider ram;
|
||||
private JSpinner renderTime;
|
||||
private JSlider priority;
|
||||
private JTextField proxy;
|
||||
private JTextField hostname;
|
||||
|
||||
private ButtonGroup themeOptionsGroup;
|
||||
private JRadioButton lightMode;
|
||||
private JRadioButton darkMode;
|
||||
|
||||
private JCheckBox saveFile;
|
||||
private JCheckBox autoSignIn;
|
||||
JButton saveButton;
|
||||
|
||||
private boolean haveAutoStarted;
|
||||
private boolean useSysTrayPrevState;
|
||||
private boolean isHeadlessPrevState;
|
||||
private boolean sessionStarted; //indicates whether the settings activity gets shown on launch or mid-session.
|
||||
// it should only be false when the settings activity gets shown right after launch
|
||||
|
||||
public Settings(GuiSwing parent_) {
|
||||
parent = parent_;
|
||||
cacheDir = null;
|
||||
useGPUs = new LinkedList<JCheckBoxGPU>();
|
||||
haveAutoStarted = false;
|
||||
sessionStarted = false;
|
||||
}
|
||||
|
||||
@Override public void show() {
|
||||
Configuration config = parent.getConfiguration();
|
||||
parent.getSettingsLoader().merge(config, false);
|
||||
useSysTrayPrevState = config.isUseSysTray();
|
||||
isHeadlessPrevState = config.isHeadless();
|
||||
|
||||
applyTheme(config.getTheme()); // apply the proper theme (light/dark)
|
||||
|
||||
List<GPUDevice> gpus = GPU.listDevices(config);
|
||||
useGPUs.clear(); // Empty the auxiliary list (used in the list of checkboxes)
|
||||
|
||||
GridBagConstraints constraints = new GridBagConstraints();
|
||||
int currentRow = 0;
|
||||
|
||||
JLabel labelImage = GuiSwing.createLogoWithWatermark();
|
||||
|
||||
constraints.fill = GridBagConstraints.CENTER;
|
||||
|
||||
constraints.gridwidth = 2;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
parent.getContentPanel().add(labelImage, constraints);
|
||||
|
||||
++currentRow;
|
||||
|
||||
constraints.gridy = currentRow;
|
||||
parent.getContentPanel().add(new JLabel(" "), constraints); // Add a separator between logo and first panel
|
||||
|
||||
currentRow++;
|
||||
|
||||
// authentication
|
||||
CollapsibleJPanel authentication_panel = new CollapsibleJPanel(new GridLayout(2, 2), this);
|
||||
authentication_panel.setBorder(BorderFactory.createTitledBorder("Authentication"));
|
||||
|
||||
JLabel loginLabel = new JLabel("Username:");
|
||||
login = new JTextField();
|
||||
login.setText(parent.getConfiguration().getLogin());
|
||||
login.setColumns(20);
|
||||
login.addKeyListener(new CheckCanStart());
|
||||
JLabel passwordLabel = new JLabel("Password:");
|
||||
password = new JPasswordField();
|
||||
password.setText(parent.getConfiguration().getPassword());
|
||||
password.setColumns(10);
|
||||
password.addKeyListener(new CheckCanStart());
|
||||
|
||||
authentication_panel.add(loginLabel);
|
||||
authentication_panel.add(login);
|
||||
|
||||
authentication_panel.add(passwordLabel);
|
||||
authentication_panel.add(password);
|
||||
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||
parent.getContentPanel().add(authentication_panel, constraints);
|
||||
|
||||
// Theme selection panel
|
||||
CollapsibleJPanel themePanel = new CollapsibleJPanel(new GridLayout(1, 3), this);
|
||||
themePanel.setBorder(BorderFactory.createTitledBorder("Theme"));
|
||||
|
||||
themeOptionsGroup = new ButtonGroup();
|
||||
|
||||
lightMode = new JRadioButton("Light");
|
||||
lightMode.setActionCommand("light");
|
||||
lightMode.setSelected(config.getTheme().equals("light"));
|
||||
lightMode.addActionListener(new ApplyThemeAction());
|
||||
|
||||
darkMode = new JRadioButton("Dark");
|
||||
darkMode.setActionCommand("dark");
|
||||
darkMode.setSelected(config.getTheme().equals("dark"));
|
||||
darkMode.addActionListener(new ApplyThemeAction());
|
||||
|
||||
themePanel.add(lightMode);
|
||||
themePanel.add(darkMode);
|
||||
|
||||
// Group both radio buttons to allow only one selected
|
||||
themeOptionsGroup.add(lightMode);
|
||||
themeOptionsGroup.add(darkMode);
|
||||
|
||||
currentRow++;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
constraints.gridwidth = 2;
|
||||
|
||||
parent.getContentPanel().add(themePanel, constraints);
|
||||
|
||||
// directory
|
||||
CollapsibleJPanel directory_panel = new CollapsibleJPanel(new GridLayout(1, 3), this);
|
||||
directory_panel.setBorder(BorderFactory.createTitledBorder("Cache"));
|
||||
JLabel cacheLabel = new JLabel("Working directory:");
|
||||
cacheLabel.setToolTipText(SwingTooltips.WORKING_DIRECTORY.getText());
|
||||
directory_panel.add(cacheLabel);
|
||||
String destination = DUMMY_CACHE_DIR;
|
||||
if (config.isUserHasSpecifiedACacheDir()) {
|
||||
destination = config.getCacheDirForSettings().getName();
|
||||
}
|
||||
|
||||
JPanel cacheDirWrapper = new JPanel();
|
||||
cacheDirWrapper.setLayout(new BoxLayout(cacheDirWrapper, BoxLayout.LINE_AXIS));
|
||||
cacheDirText = new JLabel(destination);
|
||||
cacheDirWrapper.add(cacheDirText);
|
||||
|
||||
cacheDirWrapper.add(Box.createHorizontalGlue());
|
||||
|
||||
cacheDirChooser = new JFileChooser();
|
||||
cacheDirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
JButton openButton = new JButton("...");
|
||||
openButton.addActionListener(new ChooseFileAction());
|
||||
cacheDirWrapper.add(openButton);
|
||||
|
||||
directory_panel.add(cacheDirWrapper);
|
||||
|
||||
currentRow++;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
constraints.gridwidth = 2;
|
||||
|
||||
parent.getContentPanel().add(directory_panel, constraints);
|
||||
|
||||
// compute devices
|
||||
GridBagLayout gridbag = new GridBagLayout();
|
||||
GridBagConstraints compute_devices_constraints = new GridBagConstraints();
|
||||
CollapsibleJPanel compute_devices_panel = new CollapsibleJPanel(gridbag, this);
|
||||
|
||||
compute_devices_panel.setBorder(BorderFactory.createTitledBorder("Compute devices"));
|
||||
compute_devices_panel.setToolTipText(SwingTooltips.COMPUTE_DEVICES.getText());
|
||||
|
||||
ComputeType method = config.getComputeMethod();
|
||||
useCPU = new JCheckBox("CPU");
|
||||
boolean gpuChecked = false;
|
||||
|
||||
if (method == ComputeType.CPU_GPU) {
|
||||
useCPU.setSelected(true);
|
||||
gpuChecked = true;
|
||||
}
|
||||
else if (method == ComputeType.CPU) {
|
||||
useCPU.setSelected(true);
|
||||
gpuChecked = false;
|
||||
}
|
||||
else if (method == ComputeType.GPU) {
|
||||
useCPU.setSelected(false);
|
||||
gpuChecked = true;
|
||||
}
|
||||
useCPU.addActionListener(new CpuChangeAction());
|
||||
|
||||
compute_devices_constraints.gridx = 1;
|
||||
compute_devices_constraints.gridy = 0;
|
||||
compute_devices_constraints.fill = GridBagConstraints.BOTH;
|
||||
compute_devices_constraints.weightx = 1.0;
|
||||
compute_devices_constraints.weighty = 1.0;
|
||||
|
||||
gridbag.setConstraints(useCPU, compute_devices_constraints);
|
||||
compute_devices_panel.add(useCPU);
|
||||
|
||||
if (gpus.size() > 0) {
|
||||
for (GPUDevice gpu : gpus) {
|
||||
JCheckBoxGPU gpuCheckBox = new JCheckBoxGPU(gpu);
|
||||
gpuCheckBox.setToolTipText(gpu.getId());
|
||||
if (gpuChecked) {
|
||||
GPUDevice config_gpu = config.getGPUDevice();
|
||||
if (config_gpu != null && config_gpu.getId().equals(gpu.getId())) {
|
||||
gpuCheckBox.setSelected(gpuChecked);
|
||||
}
|
||||
}
|
||||
gpuCheckBox.addActionListener(new GpuChangeAction());
|
||||
|
||||
compute_devices_constraints.gridy++;
|
||||
gridbag.setConstraints(gpuCheckBox, compute_devices_constraints);
|
||||
compute_devices_panel.add(gpuCheckBox);
|
||||
useGPUs.add(gpuCheckBox);
|
||||
}
|
||||
|
||||
//When replacing gpus it can happen that the client can't find the one specified in the config anymore in which case config.getGPUDevice()
|
||||
//returns null
|
||||
if ((config.getComputeMethod() == ComputeType.GPU || config.getComputeMethod() == ComputeType.CPU_GPU) && config.getGPUDevice() != null) {
|
||||
GPULister gpu;
|
||||
|
||||
if (config.getGPUDevice().getType().equals("CUDA")) {
|
||||
gpu = new Nvidia();
|
||||
}
|
||||
else if (config.getGPUDevice().getType().equals("OPENCL")) {
|
||||
gpu = new OpenCL();
|
||||
}
|
||||
}
|
||||
|
||||
compute_devices_constraints.gridx = 1;
|
||||
compute_devices_constraints.weightx = 1.0;
|
||||
|
||||
compute_devices_panel.add(new JLabel(" "), compute_devices_constraints); // Add a space between lines
|
||||
}
|
||||
|
||||
CPU cpu = new CPU();
|
||||
if (cpu.cores() > 1) { // if only one core is available, no need to show the choice
|
||||
double step = 1;
|
||||
double display = (double) cpu.cores() / step;
|
||||
while (display > 10) {
|
||||
step += 1.0;
|
||||
display = (double) cpu.cores() / step;
|
||||
}
|
||||
|
||||
cpuCores = new JSlider(CPU.MIN_CORES, cpu.cores());
|
||||
cpuCores.setMajorTickSpacing((int) (step));
|
||||
cpuCores.setMinorTickSpacing(1);
|
||||
cpuCores.setPaintTicks(true);
|
||||
cpuCores.setSnapToTicks(true);
|
||||
cpuCores.addChangeListener(new ChangeListener() {
|
||||
@Override public void stateChanged(ChangeEvent e) {
|
||||
cpuCores.setToolTipText(String.valueOf(cpuCores.getValue()));
|
||||
}
|
||||
});
|
||||
cpuCores.setPaintLabels(true);
|
||||
cpuCores.setValue(config.getNbCores() != -1 ? (Math.max(config.getNbCores(), CPU.MIN_CORES)): cpuCores.getMaximum());
|
||||
JLabel coreLabel = new JLabel("CPU cores:");
|
||||
coreLabel.setToolTipText(SwingTooltips.CPU_CORES.getText());
|
||||
|
||||
compute_devices_constraints.weightx = 1.0 / gpus.size();
|
||||
compute_devices_constraints.gridx = 0;
|
||||
compute_devices_constraints.gridy++;
|
||||
|
||||
gridbag.setConstraints(coreLabel, compute_devices_constraints);
|
||||
compute_devices_panel.add(coreLabel);
|
||||
|
||||
compute_devices_constraints.gridx = 1;
|
||||
compute_devices_constraints.weightx = 1.0;
|
||||
|
||||
gridbag.setConstraints(cpuCores, compute_devices_constraints);
|
||||
compute_devices_panel.add(new JLabel(" "), compute_devices_constraints); // Add a space between lines
|
||||
compute_devices_panel.add(cpuCores);
|
||||
}
|
||||
|
||||
// max ram allowed to render
|
||||
OS os = OS.getOS();
|
||||
int all_ram = (int) os.getTotalMemory();
|
||||
ram = new JSlider(0, all_ram);
|
||||
int step = 1000000;
|
||||
double display = (double) all_ram / (double) step;
|
||||
while (display > 10) {
|
||||
step += 1000000;
|
||||
display = (double) all_ram / (double) step;
|
||||
}
|
||||
Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>();
|
||||
for (int g = 0; g < all_ram; g += step) {
|
||||
labelTable.put(g, new JLabel("" + (g / 1000000)));
|
||||
}
|
||||
ram.setMajorTickSpacing(step);
|
||||
ram.setLabelTable(labelTable);
|
||||
ram.setPaintTicks(true);
|
||||
ram.addChangeListener(new ChangeListener() {
|
||||
@Override public void stateChanged(ChangeEvent e) {
|
||||
ram.setToolTipText(String.format("%,d MiB", (ram.getValue() / 1024)));
|
||||
}
|
||||
});
|
||||
ram.setPaintLabels(true);
|
||||
ram.setValue((int) (config.getMaxAllowedMemory() != -1 ? config.getMaxAllowedMemory() : os.getTotalMemory()));
|
||||
JLabel ramLabel = new JLabel("Memory:");
|
||||
ramLabel.setToolTipText(SwingTooltips.MEMORY.getText());
|
||||
|
||||
compute_devices_constraints.weightx = 1.0 / gpus.size();
|
||||
compute_devices_constraints.gridx = 0;
|
||||
compute_devices_constraints.gridy++;
|
||||
|
||||
gridbag.setConstraints(ramLabel, compute_devices_constraints);
|
||||
compute_devices_panel.add(ramLabel);
|
||||
|
||||
compute_devices_constraints.gridx = 1;
|
||||
compute_devices_constraints.weightx = 1.0;
|
||||
|
||||
gridbag.setConstraints(ram, compute_devices_constraints);
|
||||
compute_devices_panel.add(new JLabel(" "), compute_devices_constraints); // Add a space between lines
|
||||
compute_devices_panel.add(ram);
|
||||
|
||||
parent.getContentPanel().add(compute_devices_panel, constraints);
|
||||
|
||||
// priority
|
||||
// ui display low -> high but the actual values are reversed
|
||||
// -19 is highest priority
|
||||
boolean high_priority_support = os.getSupportHighPriority();
|
||||
priority = new JSlider(high_priority_support ? -19 : 0, 19);
|
||||
Hashtable<Integer, JLabel> labelTablePriority = new Hashtable<>();
|
||||
labelTablePriority.put(high_priority_support ? -19 : 0, new JLabel("Low"));
|
||||
labelTablePriority.put(19, new JLabel("High"));
|
||||
priority.setLabelTable(labelTablePriority);
|
||||
|
||||
priority.setMinorTickSpacing(1);
|
||||
priority.setPaintTicks(true);
|
||||
priority.setSnapToTicks(true);
|
||||
priority.addChangeListener(new ChangeListener() {
|
||||
@Override public void stateChanged(ChangeEvent e) {
|
||||
priority.setToolTipText(String.valueOf(priority.getValue()));
|
||||
}
|
||||
});
|
||||
priority.setPaintLabels(true);
|
||||
if (high_priority_support) {
|
||||
// inverse
|
||||
priority.setValue(-1 * config.getPriority());
|
||||
}
|
||||
else {
|
||||
priority.setValue(19 - config.getPriority());
|
||||
}
|
||||
JLabel priorityLabel = new JLabel("Priority");
|
||||
priorityLabel.setToolTipText(SwingTooltips.PRIORITY.getText());
|
||||
|
||||
boolean showPrioritySlider = os.checkNiceAvailability();
|
||||
priority.setVisible(showPrioritySlider);
|
||||
priorityLabel.setVisible(showPrioritySlider);
|
||||
|
||||
compute_devices_constraints.weightx = 1.0 / gpus.size();
|
||||
compute_devices_constraints.gridx = 0;
|
||||
compute_devices_constraints.gridy++;
|
||||
|
||||
gridbag.setConstraints(priorityLabel, compute_devices_constraints);
|
||||
compute_devices_panel.add(priorityLabel);
|
||||
|
||||
compute_devices_constraints.gridx = 1;
|
||||
compute_devices_constraints.weightx = 1.0;
|
||||
|
||||
gridbag.setConstraints(priority, compute_devices_constraints);
|
||||
compute_devices_panel.add(priority);
|
||||
|
||||
currentRow++;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
constraints.gridwidth = 2;
|
||||
parent.getContentPanel().add(compute_devices_panel, constraints);
|
||||
|
||||
// other
|
||||
CollapsibleJPanel advanced_panel = new CollapsibleJPanel(new GridLayout(5, 2), this);
|
||||
advanced_panel.setBorder(BorderFactory.createTitledBorder("Advanced options"));
|
||||
|
||||
JLabel useSysTrayLabel = new JLabel("Minimize to SysTray");
|
||||
useSysTrayLabel.setToolTipText(SwingTooltips.MINIMIZE_TO_SYSTRAY.getText());
|
||||
|
||||
useSysTray = new JCheckBox();
|
||||
useSysTray.setSelected(config.isUseSysTray());
|
||||
advanced_panel.add(useSysTrayLabel);
|
||||
advanced_panel.add(useSysTray);
|
||||
|
||||
JLabel headlessLabel = new JLabel("Block Eevee projects");
|
||||
headlessCheckbox = new JCheckBox();
|
||||
headlessCheckbox.setSelected(config.isHeadless());
|
||||
advanced_panel.add(headlessLabel);
|
||||
advanced_panel.add(headlessCheckbox);
|
||||
|
||||
JLabel proxyLabel = new JLabel("Proxy:");
|
||||
proxyLabel.setToolTipText("http://login:password@host:port\n" + SwingTooltips.PROXY.getText());
|
||||
proxy = new JTextField();
|
||||
proxy.setToolTipText("http://login:password@host:port");
|
||||
proxy.setText(parent.getConfiguration().getProxy());
|
||||
proxy.addKeyListener(new CheckCanStart());
|
||||
|
||||
advanced_panel.add(proxyLabel);
|
||||
advanced_panel.add(proxy);
|
||||
|
||||
JLabel hostnameLabel = new JLabel("Computer name:");
|
||||
hostnameLabel.setToolTipText(SwingTooltips.COMPUTER_NAME.getText());
|
||||
hostname = new JTextField();
|
||||
hostname.setText(parent.getConfiguration().getHostname());
|
||||
|
||||
advanced_panel.add(hostnameLabel);
|
||||
advanced_panel.add(hostname);
|
||||
|
||||
JLabel renderTimeLabel = new JLabel("Max time per frame (in minute):");
|
||||
renderTimeLabel.setToolTipText(SwingTooltips.MAX_TIME_PER_FRAME.getText());
|
||||
int val = 0;
|
||||
if (parent.getConfiguration().getMaxRenderTime() > 0) {
|
||||
val = parent.getConfiguration().getMaxRenderTime() / 60;
|
||||
}
|
||||
renderTime = new JSpinner(new SpinnerNumberModel(val, 0, 1000, 1));
|
||||
|
||||
advanced_panel.add(renderTimeLabel);
|
||||
advanced_panel.add(renderTime);
|
||||
|
||||
currentRow++;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
constraints.gridwidth = 2;
|
||||
parent.getContentPanel().add(advanced_panel, constraints);
|
||||
|
||||
// general settings
|
||||
JPanel general_panel = new JPanel(new GridLayout(1, 2));
|
||||
|
||||
saveFile = new JCheckBox("Save settings", true);
|
||||
general_panel.add(saveFile);
|
||||
|
||||
autoSignIn = new JCheckBox("Auto sign in", config.isAutoSignIn());
|
||||
autoSignIn.addActionListener(new AutoSignInChangeAction());
|
||||
general_panel.add(autoSignIn);
|
||||
|
||||
currentRow++;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
constraints.gridwidth = 2;
|
||||
parent.getContentPanel().add(general_panel, constraints);
|
||||
|
||||
currentRow++;
|
||||
constraints.gridy = currentRow;
|
||||
parent.getContentPanel().add(new JLabel(" "), constraints); // Add a separator between last checkboxes and button
|
||||
|
||||
currentRow++;
|
||||
String buttonText = "Start";
|
||||
if (parent.getClient() != null) {
|
||||
if (parent.getClient().isRunning()) {
|
||||
buttonText = "Save";
|
||||
}
|
||||
}
|
||||
saveButton = new JButton(buttonText);
|
||||
checkDisplaySaveButton();
|
||||
saveButton.addActionListener(new SaveAction());
|
||||
currentRow++;
|
||||
constraints.gridwidth = 2;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
parent.getContentPanel().add(saveButton, constraints);
|
||||
|
||||
// Increase the size of the app Window to ensure it shows all the information with the Advanced Options panel opened.
|
||||
parent.setSize(520,850);
|
||||
|
||||
if (haveAutoStarted == false && config.isAutoSignIn() && checkDisplaySaveButton()) {
|
||||
// auto start
|
||||
haveAutoStarted = true;
|
||||
new SaveAction().actionPerformed(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void resizeWindow() {}
|
||||
|
||||
public boolean checkDisplaySaveButton() {
|
||||
boolean selected = useCPU.isSelected();
|
||||
for (JCheckBoxGPU box : useGPUs) {
|
||||
if (box.isSelected()) {
|
||||
selected = true;
|
||||
}
|
||||
}
|
||||
if (login.getText().isEmpty() || password.getPassword().length == 0 || Proxy.isValidURL(proxy.getText()) == false) {
|
||||
selected = false;
|
||||
}
|
||||
|
||||
saveButton.setEnabled(selected);
|
||||
return selected;
|
||||
}
|
||||
|
||||
private void applyTheme(String theme_) {
|
||||
try {
|
||||
if (theme_.equals("light")) {
|
||||
UIManager.setLookAndFeel(new FlatLightLaf());
|
||||
}
|
||||
else if (theme_.equals("dark")) {
|
||||
UIManager.setLookAndFeel(new FlatDarkLaf());
|
||||
}
|
||||
|
||||
// Apply the new theme
|
||||
FlatLaf.updateUI();
|
||||
}
|
||||
catch (UnsupportedLookAndFeelException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
class ChooseFileAction implements ActionListener {
|
||||
|
||||
@Override public void actionPerformed(ActionEvent arg0) {
|
||||
JOptionPane.showMessageDialog(parent.getContentPane(),
|
||||
"<html>The working directory has to be dedicated directory. <br />Caution, everything not related to SheepIt-Renderfarm will be removed.<br />You should create a directory specifically for it.</html>",
|
||||
"Warning: files will be removed!", JOptionPane.WARNING_MESSAGE);
|
||||
int returnVal = cacheDirChooser.showOpenDialog(parent.getContentPane());
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
File file = cacheDirChooser.getSelectedFile();
|
||||
cacheDir = file;
|
||||
cacheDirText.setText(cacheDir.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CpuChangeAction implements ActionListener {
|
||||
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
checkDisplaySaveButton();
|
||||
}
|
||||
}
|
||||
|
||||
class GpuChangeAction implements ActionListener {
|
||||
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
int counter = 0;
|
||||
for (JCheckBox box : useGPUs) {
|
||||
if (!box.isSelected()) {
|
||||
box.setSelected(false);
|
||||
}
|
||||
else {
|
||||
GPULister gpu;
|
||||
if (useGPUs.get(counter).getGPUDevice().getType().equals("CUDA")) {
|
||||
gpu = new Nvidia();
|
||||
}
|
||||
else if (useGPUs.get(counter).getGPUDevice().getType().equals("OPENCL")) {
|
||||
gpu = new OpenCL();
|
||||
}
|
||||
}
|
||||
|
||||
// Simulate a radio button behavior with check buttons while only 1 GPU
|
||||
// can be selected at a time
|
||||
if (box.equals(e.getSource()) == false) {
|
||||
box.setSelected(false);
|
||||
}
|
||||
|
||||
counter++;
|
||||
}
|
||||
checkDisplaySaveButton();
|
||||
}
|
||||
}
|
||||
|
||||
class AutoSignInChangeAction implements ActionListener {
|
||||
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
if (autoSignIn.isSelected()) {
|
||||
saveFile.setSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ApplyThemeAction implements ActionListener {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
applyTheme(themeOptionsGroup.getSelection().getActionCommand());
|
||||
}
|
||||
}
|
||||
|
||||
class SaveAction implements ActionListener {
|
||||
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Configuration config = parent.getConfiguration();
|
||||
if (config == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (themeOptionsGroup.getSelection().getActionCommand() != null)
|
||||
config.setTheme(themeOptionsGroup.getSelection().getActionCommand());
|
||||
|
||||
if (cacheDir != null) {
|
||||
File fromConfig = config.getStorageDir();
|
||||
if (fromConfig != null && fromConfig.getAbsolutePath().equals(cacheDir.getAbsolutePath()) == false) {
|
||||
config.setCacheDir(cacheDir);
|
||||
}
|
||||
else {
|
||||
// do nothing because the directory is the same as before
|
||||
}
|
||||
}
|
||||
|
||||
GPUDevice selected_gpu = null;
|
||||
for (JCheckBoxGPU box : useGPUs) {
|
||||
if (box.isSelected()) {
|
||||
selected_gpu = box.getGPUDevice();
|
||||
}
|
||||
}
|
||||
|
||||
ComputeType method = getComputeType(selected_gpu);
|
||||
config.setComputeMethod(method);
|
||||
|
||||
if (selected_gpu != null) {
|
||||
config.setGPUDevice(selected_gpu);
|
||||
}
|
||||
|
||||
int cpu_cores = -1;
|
||||
if (cpuCores != null) {
|
||||
cpu_cores = cpuCores.getValue();
|
||||
}
|
||||
|
||||
if (cpu_cores > 0) {
|
||||
config.setNbCores(cpu_cores);
|
||||
}
|
||||
|
||||
long max_ram = -1;
|
||||
if (ram != null) {
|
||||
max_ram = ram.getValue();
|
||||
}
|
||||
|
||||
if (max_ram > 0) {
|
||||
config.setMaxAllowedMemory(max_ram);
|
||||
}
|
||||
|
||||
int max_rendertime = -1;
|
||||
if (renderTime != null) {
|
||||
max_rendertime = (Integer) renderTime.getValue() * 60;
|
||||
config.setMaxRenderTime(max_rendertime);
|
||||
}
|
||||
|
||||
// ui display low -> high but the actual values are reversed
|
||||
// -19 is highest priority
|
||||
OS os = OS.getOS();
|
||||
boolean high_priority_support = os.getSupportHighPriority();
|
||||
if (high_priority_support) {
|
||||
// inverse
|
||||
config.setUsePriority(-1 * priority.getValue());
|
||||
}
|
||||
else {
|
||||
config.setUsePriority(19 - priority.getValue());
|
||||
}
|
||||
|
||||
config.setHeadless(headlessCheckbox.isSelected());
|
||||
|
||||
String proxyText = null;
|
||||
if (proxy != null) {
|
||||
try {
|
||||
Proxy.set(proxy.getText());
|
||||
proxyText = proxy.getText();
|
||||
}
|
||||
catch (MalformedURLException e1) {
|
||||
System.err.println("Error: wrong url for proxy");
|
||||
System.err.println(e1);
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
config.setProxy(proxyText);
|
||||
|
||||
parent.setCredentials(login.getText(), new String(password.getPassword()));
|
||||
|
||||
String hostnameText = hostname.getText();
|
||||
if (hostnameText == null || hostnameText.isEmpty()) {
|
||||
hostnameText = parent.getConfiguration().getHostname();
|
||||
}
|
||||
config.setHostname(hostnameText);
|
||||
config.setAutoSignIn(autoSignIn.isSelected());
|
||||
config.setUseSysTray(useSysTray.isSelected());
|
||||
// config.setUIType(GuiSwing.type);
|
||||
|
||||
|
||||
|
||||
if (saveFile.isSelected()) {
|
||||
parent.getSettingsLoader()
|
||||
.setSettings(config.getConfigFilePath(), login.getText(), new String(password.getPassword()), proxyText, hostnameText, method,
|
||||
selected_gpu, cpu_cores, max_ram, max_rendertime, getCachePath(config), autoSignIn.isSelected(), useSysTray.isSelected(),
|
||||
headlessCheckbox.isSelected(), GuiSwing.type, themeOptionsGroup.getSelection().getActionCommand(), config.getPriority());
|
||||
|
||||
// wait for successful authentication (to store the public key)
|
||||
// or do we already have one?
|
||||
if (parent.getClient().getServer().getServerConfig() != null && parent.getClient().getServer().getServerConfig().getPublickey() != null) {
|
||||
parent.getSettingsLoader().saveFile();
|
||||
sessionStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean sysTrayChanged = useSysTray.isSelected() != useSysTrayPrevState;
|
||||
boolean headlessChanged = headlessCheckbox.isSelected() != isHeadlessPrevState;
|
||||
boolean restartRequired = sysTrayChanged || headlessChanged;
|
||||
if (restartRequired) {
|
||||
String warning = "The following changes require a restart to take effect: %s";
|
||||
List<String> changes = new LinkedList<>();
|
||||
|
||||
if (sysTrayChanged) {
|
||||
changes.add("Minimize to SysTray");
|
||||
}
|
||||
|
||||
if (headlessChanged && sessionStarted) { //only show this when the setting gets changed in the middle of a session, not on launch
|
||||
changes.add("Block Eevee Projects");
|
||||
}
|
||||
|
||||
if (changes.size() > 0) {
|
||||
warning = String.format(warning, String.join(", ", changes));
|
||||
JOptionPane.showMessageDialog(null, warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull private ComputeType getComputeType(GPUDevice selected_gpu) {
|
||||
ComputeType method = ComputeType.CPU;
|
||||
if (useCPU.isSelected() && selected_gpu == null) {
|
||||
method = ComputeType.CPU;
|
||||
}
|
||||
else if (useCPU.isSelected() == false && selected_gpu != null) {
|
||||
method = ComputeType.GPU;
|
||||
}
|
||||
else if (useCPU.isSelected() && selected_gpu != null) {
|
||||
method = ComputeType.CPU_GPU;
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
private String getCachePath(Configuration config) {
|
||||
String cachePath = null;
|
||||
if (config.isUserHasSpecifiedACacheDir() && config.getCacheDirForSettings() != null) {
|
||||
cachePath = config.getCacheDirForSettings().getAbsolutePath();
|
||||
}
|
||||
return cachePath;
|
||||
}
|
||||
}
|
||||
|
||||
class JCheckBoxGPU extends JCheckBox {
|
||||
private GPUDevice gpu;
|
||||
|
||||
public JCheckBoxGPU(GPUDevice gpu) {
|
||||
super(gpu.getModel());
|
||||
this.gpu = gpu;
|
||||
}
|
||||
|
||||
public GPUDevice getGPUDevice() {
|
||||
return gpu;
|
||||
}
|
||||
}
|
||||
|
||||
public class CheckCanStart implements KeyListener {
|
||||
|
||||
@Override public void keyPressed(KeyEvent arg0) {
|
||||
}
|
||||
|
||||
@Override public void keyReleased(KeyEvent arg0) {
|
||||
checkDisplaySaveButton();
|
||||
}
|
||||
|
||||
@Override public void keyTyped(KeyEvent arg0) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.standalone.swing.activity;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.Image;
|
||||
import java.io.File;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.Spring;
|
||||
import javax.swing.SpringLayout;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
import com.sheepit.client.Job;
|
||||
import com.sheepit.client.Log;
|
||||
import com.sheepit.client.Stats;
|
||||
import com.sheepit.client.TransferStats;
|
||||
import com.sheepit.client.Utils;
|
||||
import com.sheepit.client.standalone.GuiSwing;
|
||||
import com.sheepit.client.standalone.GuiSwing.ActivityType;
|
||||
|
||||
import com.sheepit.client.standalone.swing.components.CollapsibleJPanel;
|
||||
|
||||
public class Working implements Activity {
|
||||
private GuiSwing parent;
|
||||
|
||||
private CollapsibleJPanel session_info_panel;
|
||||
private CollapsibleJPanel global_stats_panel;
|
||||
private CollapsibleJPanel last_frame_panel;
|
||||
private JLabel statusContent;
|
||||
private String previousStatus;
|
||||
private JLabel renderedFrameContent;
|
||||
private JLabel remainingFrameContent;
|
||||
private JLabel lastRenderTime;
|
||||
private JLabel lastRender;
|
||||
private JLabel creditEarned;
|
||||
private JButton pauseButton;
|
||||
private JButton exitAfterFrame;
|
||||
private JLabel current_project_name_value;
|
||||
private JLabel current_project_duration_value;
|
||||
private JLabel currrent_project_progression_value;
|
||||
private JLabel current_project_compute_method_value;
|
||||
private JLabel user_info_points_total_value;
|
||||
private JLabel renderable_projects_value;
|
||||
private JLabel waiting_projects_value;
|
||||
private JLabel connected_machines_value;
|
||||
private JLabel user_info_total_rendertime_this_session_value;
|
||||
private JLabel userInfoQueuedUploadsAndSizeValue;
|
||||
private JLabel sessionDownloadsStatsValue;
|
||||
private JLabel sessionUploadsStatsValue;
|
||||
private String currentTheme;
|
||||
private Log log;
|
||||
|
||||
public Working(GuiSwing parent_) {
|
||||
parent = parent_;
|
||||
|
||||
statusContent = new JLabel("Init");
|
||||
renderedFrameContent = new JLabel("0");
|
||||
remainingFrameContent = new JLabel("");
|
||||
creditEarned = new JLabel("");
|
||||
current_project_name_value = new JLabel("");
|
||||
current_project_duration_value = new JLabel("");
|
||||
currrent_project_progression_value = new JLabel("");
|
||||
current_project_compute_method_value = new JLabel("");
|
||||
user_info_points_total_value = new JLabel("");
|
||||
renderable_projects_value = new JLabel("");
|
||||
waiting_projects_value = new JLabel("");
|
||||
connected_machines_value = new JLabel("");
|
||||
user_info_total_rendertime_this_session_value = new JLabel("");
|
||||
lastRenderTime = new JLabel(" "); // Insert a space to ensure the component reserves the space in the screen (for the window size calculation)
|
||||
lastRender = new JLabel("");
|
||||
userInfoQueuedUploadsAndSizeValue = new JLabel("0");
|
||||
sessionDownloadsStatsValue = new JLabel("0KB");
|
||||
sessionUploadsStatsValue = new JLabel("0KB");
|
||||
currentTheme = UIManager.getLookAndFeel().getName(); // Capture the theme on component instantiation
|
||||
previousStatus = "";
|
||||
log = Log.getInstance(parent_.getConfiguration());
|
||||
}
|
||||
|
||||
@Override public void show() {
|
||||
// If the stored theme and the UIManager's theme doesn't match is bc the user has changed it
|
||||
if (!currentTheme.equals(UIManager.getLookAndFeel().getName())) {
|
||||
// And, as the user has changed the theme, then we must recreate all the UI elements with session data
|
||||
// Reason being they are defined as class variables and therefore created once when the object
|
||||
// is created the first time.
|
||||
// As the Java swing engine applies the "look & feel" at creation time, we need to "re-create" the
|
||||
// objects to ensure they have the right theme colors.
|
||||
statusContent = new JLabel(statusContent.getText());
|
||||
renderedFrameContent = new JLabel(renderedFrameContent.getText());
|
||||
remainingFrameContent = new JLabel(remainingFrameContent.getText());
|
||||
creditEarned = new JLabel(creditEarned.getText());
|
||||
current_project_name_value = new JLabel(current_project_name_value.getText());
|
||||
current_project_duration_value = new JLabel(current_project_duration_value.getText());
|
||||
currrent_project_progression_value = new JLabel(currrent_project_progression_value.getText());
|
||||
current_project_compute_method_value = new JLabel(current_project_compute_method_value.getText());
|
||||
user_info_points_total_value = new JLabel(user_info_points_total_value.getText());
|
||||
renderable_projects_value = new JLabel(renderable_projects_value.getText());
|
||||
waiting_projects_value = new JLabel(waiting_projects_value.getText());
|
||||
connected_machines_value = new JLabel(connected_machines_value.getText());
|
||||
user_info_total_rendertime_this_session_value = new JLabel(user_info_total_rendertime_this_session_value.getText());
|
||||
lastRenderTime = new JLabel(lastRenderTime.getText());
|
||||
lastRender = new JLabel(lastRender.getText());
|
||||
userInfoQueuedUploadsAndSizeValue = new JLabel(userInfoQueuedUploadsAndSizeValue.getText());
|
||||
sessionDownloadsStatsValue = new JLabel(sessionDownloadsStatsValue.getText());
|
||||
sessionUploadsStatsValue = new JLabel(sessionUploadsStatsValue.getText());
|
||||
|
||||
// set the new theme as the current one
|
||||
currentTheme = UIManager.getLookAndFeel().getName();
|
||||
}
|
||||
|
||||
// current project
|
||||
JPanel current_project_panel = new JPanel(new GridLayout(5, 2));
|
||||
current_project_panel.setBorder(BorderFactory.createTitledBorder("Project"));
|
||||
|
||||
JLabel current_project_status = new JLabel("Status: ", JLabel.TRAILING);
|
||||
JLabel current_project_name = new JLabel("Name: ", JLabel.TRAILING);
|
||||
JLabel current_project_duration = new JLabel("Rendering for: ", JLabel.TRAILING);
|
||||
JLabel current_project_progression = new JLabel("Remaining: ", JLabel.TRAILING);
|
||||
JLabel current_project_compute_method_label = new JLabel("Compute method: ", JLabel.TRAILING);
|
||||
|
||||
current_project_panel.add(current_project_status);
|
||||
current_project_panel.add(statusContent);
|
||||
|
||||
current_project_panel.add(current_project_name);
|
||||
current_project_panel.add(current_project_name_value);
|
||||
|
||||
current_project_panel.add(current_project_duration);
|
||||
current_project_panel.add(current_project_duration_value);
|
||||
|
||||
current_project_panel.add(current_project_progression);
|
||||
current_project_panel.add(currrent_project_progression_value);
|
||||
|
||||
current_project_panel.add(current_project_compute_method_label);
|
||||
current_project_panel.add(current_project_compute_method_value);
|
||||
|
||||
// user info
|
||||
session_info_panel = new CollapsibleJPanel(new GridLayout(7, 2), this);
|
||||
session_info_panel.setBorder(BorderFactory.createTitledBorder("Session infos"));
|
||||
|
||||
JLabel user_info_credits_this_session = new JLabel("Points earned: ", JLabel.TRAILING);
|
||||
JLabel user_info_total_rendertime_this_session = new JLabel("Duration: ", JLabel.TRAILING);
|
||||
JLabel user_info_pending_uploads_and_size = new JLabel("Queued uploads: ", JLabel.TRAILING);
|
||||
JLabel session_download_stats = new JLabel("Total Downloads: ", JLabel.TRAILING);
|
||||
JLabel session_upload_stats = new JLabel("Total Uploads: ", JLabel.TRAILING);
|
||||
JLabel user_info_rendered_frame_this_session = new JLabel("Rendered frames: ", JLabel.TRAILING);
|
||||
JLabel global_static_renderable_project = new JLabel("Renderable projects: ", JLabel.TRAILING);
|
||||
|
||||
session_info_panel.add(user_info_credits_this_session);
|
||||
session_info_panel.add(creditEarned);
|
||||
|
||||
session_info_panel.add(user_info_rendered_frame_this_session);
|
||||
session_info_panel.add(renderedFrameContent);
|
||||
|
||||
session_info_panel.add(user_info_pending_uploads_and_size);
|
||||
session_info_panel.add(userInfoQueuedUploadsAndSizeValue);
|
||||
|
||||
session_info_panel.add(session_download_stats);
|
||||
session_info_panel.add(sessionDownloadsStatsValue);
|
||||
|
||||
session_info_panel.add(session_upload_stats);
|
||||
session_info_panel.add(sessionUploadsStatsValue);
|
||||
|
||||
session_info_panel.add(global_static_renderable_project);
|
||||
session_info_panel.add(renderable_projects_value);
|
||||
|
||||
session_info_panel.add(user_info_total_rendertime_this_session);
|
||||
session_info_panel.add(user_info_total_rendertime_this_session_value);
|
||||
|
||||
// global stats
|
||||
global_stats_panel = new CollapsibleJPanel(new GridLayout(4, 2), this);
|
||||
global_stats_panel.setBorder(BorderFactory.createTitledBorder("Global stats"));
|
||||
|
||||
JLabel global_stats_machine_connected = new JLabel("Machines connected: ", JLabel.TRAILING);
|
||||
JLabel global_stats_remaining_frame = new JLabel("Remaining frames: ", JLabel.TRAILING);
|
||||
JLabel global_stats_waiting_project = new JLabel("Active projects: ", JLabel.TRAILING);
|
||||
JLabel global_stats_user_points = new JLabel("User's points: ", JLabel.TRAILING);
|
||||
|
||||
global_stats_panel.add(global_stats_waiting_project);
|
||||
global_stats_panel.add(waiting_projects_value);
|
||||
|
||||
global_stats_panel.add(global_stats_machine_connected);
|
||||
global_stats_panel.add(connected_machines_value);
|
||||
|
||||
global_stats_panel.add(global_stats_remaining_frame);
|
||||
global_stats_panel.add(remainingFrameContent);
|
||||
|
||||
global_stats_panel.add(global_stats_user_points);
|
||||
global_stats_panel.add(user_info_points_total_value);
|
||||
|
||||
// last frame
|
||||
last_frame_panel = new CollapsibleJPanel(new GridLayout(2, 2), this);
|
||||
last_frame_panel.setLayout(new BoxLayout(last_frame_panel, BoxLayout.Y_AXIS));
|
||||
last_frame_panel.setBorder(BorderFactory.createTitledBorder("Last uploaded frame"));
|
||||
lastRender.setIcon(new ImageIcon(new BufferedImage(200, 120, BufferedImage.TYPE_INT_ARGB)));
|
||||
lastRender.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
lastRenderTime.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
last_frame_panel.add(lastRenderTime);
|
||||
last_frame_panel.add(lastRender);
|
||||
|
||||
JLabel labelImage = GuiSwing.createLogoWithWatermark();
|
||||
labelImage.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
parent.getContentPanel().add(labelImage);
|
||||
|
||||
JPanel buttonsPanel = new JPanel(new GridLayout(2, 2));
|
||||
|
||||
JButton settingsButton = new JButton("Settings");
|
||||
settingsButton.addActionListener(new SettingsAction());
|
||||
|
||||
pauseButton = new JButton("Pause getting new jobs");
|
||||
Client client = parent.getClient();
|
||||
if (client != null && client.isSuspended()) {
|
||||
pauseButton.setText("Resume getting jobs");
|
||||
}
|
||||
|
||||
pauseButton.addActionListener(new PauseAction());
|
||||
|
||||
JButton blockJob = new JButton("Block this project");
|
||||
blockJob.addActionListener(new blockJobAction());
|
||||
|
||||
exitAfterFrame = new JButton("Exit");
|
||||
exitAfterFrame.addActionListener(new ExitAfterAction());
|
||||
|
||||
buttonsPanel.add(settingsButton);
|
||||
buttonsPanel.add(pauseButton);
|
||||
buttonsPanel.add(blockJob);
|
||||
buttonsPanel.add(exitAfterFrame);
|
||||
|
||||
parent.getContentPanel().setLayout(new GridBagLayout());
|
||||
GridBagConstraints global_constraints = new GridBagConstraints();
|
||||
global_constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||
global_constraints.weightx = 1;
|
||||
global_constraints.gridx = 0;
|
||||
|
||||
parent.getContentPanel().add(new JLabel(" "), global_constraints); // Add a separator between logo and first panel
|
||||
parent.getContentPanel().add(current_project_panel, global_constraints);
|
||||
parent.getContentPanel().add(global_stats_panel, global_constraints);
|
||||
parent.getContentPanel().add(session_info_panel, global_constraints);
|
||||
parent.getContentPanel().add(last_frame_panel, global_constraints);
|
||||
parent.getContentPanel().add(new JLabel(" "), global_constraints); // Add a separator between last panel and buttons
|
||||
parent.getContentPanel().add(buttonsPanel, global_constraints);
|
||||
|
||||
// Set the proper size for the Working (if coming from Settings screen, the window size will be too big for the content!)
|
||||
parent.setSize(520, 820);
|
||||
}
|
||||
|
||||
public void resizeWindow() {
|
||||
parent.revalidate();
|
||||
parent.repaint();
|
||||
|
||||
// Calculate the proper size based on the status of the panels (opened/collapsed)
|
||||
parent.setSize(520, (int) (session_info_panel.getHeight() + global_stats_panel.getHeight() + last_frame_panel.getHeight()) + 400);
|
||||
}
|
||||
|
||||
public void setStatus(String msg_) {
|
||||
setStatus(msg_, false);
|
||||
}
|
||||
|
||||
public void setStatus(String msg_, boolean overwriteSuspendedMsg) {
|
||||
Client client = parent.getClient();
|
||||
|
||||
if (!msg_.equals("")) {
|
||||
this.previousStatus = msg_;
|
||||
}
|
||||
|
||||
if (client != null && client.isSuspended()) {
|
||||
if (overwriteSuspendedMsg) {
|
||||
statusContent.setText("<html>" + msg_ + "</html>");
|
||||
}
|
||||
}
|
||||
else {
|
||||
statusContent.setText("<html>" + msg_ + "</html>");
|
||||
}
|
||||
}
|
||||
|
||||
public void setRenderingProjectName(String msg_) {
|
||||
current_project_name_value.setText("<html>" + (msg_.length() > 26 ? msg_.substring(0, 26) : msg_) + "</html>");
|
||||
}
|
||||
|
||||
public void setRemainingTime(String time_) {
|
||||
currrent_project_progression_value.setText("<html>" + time_ + "</html>");
|
||||
}
|
||||
|
||||
public void setRenderingTime(String time_) {
|
||||
current_project_duration_value.setText("<html>" + time_ + "</html>");
|
||||
}
|
||||
|
||||
public void setComputeMethod(String computeMethod_) {
|
||||
this.current_project_compute_method_value.setText(computeMethod_);
|
||||
}
|
||||
|
||||
public void displayTransferStats(TransferStats downloads, TransferStats uploads) {
|
||||
sessionDownloadsStatsValue.setText(String.format("%s @ %s/s", downloads.getSessionTraffic(), downloads.getAverageSessionSpeed()));
|
||||
sessionUploadsStatsValue.setText(String.format("%s @ %s/s", uploads.getSessionTraffic(), uploads.getAverageSessionSpeed()));
|
||||
updateTime();
|
||||
}
|
||||
|
||||
public void displayStats(Stats stats) {
|
||||
DecimalFormat df = new DecimalFormat("##,##,##,##,##,##,##0");
|
||||
remainingFrameContent.setText(df.format(stats.getRemainingFrame()));
|
||||
creditEarned.setText(df.format(stats.getCreditsEarnedDuringSession()));
|
||||
user_info_points_total_value.setText(df.format(stats.getCreditsEarned()));
|
||||
renderable_projects_value.setText(df.format(stats.getRenderableProject()));
|
||||
waiting_projects_value.setText(df.format(stats.getWaitingProject()));
|
||||
connected_machines_value.setText(df.format(stats.getConnectedMachine()));
|
||||
|
||||
updateTime();
|
||||
}
|
||||
|
||||
public void displayUploadQueueStats(int queueSize, long queueVolume) {
|
||||
userInfoQueuedUploadsAndSizeValue.setText(
|
||||
String.format("%d%s%s", queueSize, (queueSize > 0 ? String.format(" (%.2fMB) ", (queueVolume / 1024.0 / 1024.0)) : ""),
|
||||
(queueSize == this.parent.getConfiguration().getMaxUploadingJob() ? "- Queue full!" : "")));
|
||||
|
||||
// If the user has requested to exit, then we need to update the JButton with the queue size
|
||||
if (this.exitAfterFrame.getText().startsWith("Cancel")) {
|
||||
Client client = parent.getClient();
|
||||
|
||||
if (client != null) {
|
||||
if (client.isRunning()) {
|
||||
queueSize++;
|
||||
}
|
||||
}
|
||||
|
||||
exitAfterFrame.setText(String.format("Cancel exit (%s frame%s to go)", queueSize, (queueSize > 1 ? "s" : "")));
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void updateTime() {
|
||||
if (this.parent.getClient().getStartTime() != 0) {
|
||||
user_info_total_rendertime_this_session_value
|
||||
.setText(Utils.humanDuration(new Date((new Date().getTime() - this.parent.getClient().getStartTime()))));
|
||||
}
|
||||
Job job = this.parent.getClient().getRenderingJob();
|
||||
if (job != null && job.getProcessRender() != null && job.getProcessRender().getStartTime() > 0) {
|
||||
current_project_duration_value
|
||||
.setText("<html>" + Utils.humanDuration(new Date((new Date().getTime() - job.getProcessRender().getStartTime()))) + "</html>");
|
||||
}
|
||||
else {
|
||||
current_project_duration_value.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
public void setRenderedFrame(int n) {
|
||||
renderedFrameContent.setText(String.valueOf(n));
|
||||
showLastRender();
|
||||
}
|
||||
|
||||
public void showLastRender() {
|
||||
Client client = parent.getClient();
|
||||
if (client != null) {
|
||||
Job lastJob = client.getPreviousJob();
|
||||
if (lastJob != null) {
|
||||
ImageIcon icon = null;
|
||||
int idInt = Integer.parseInt(lastJob.getId());
|
||||
if (idInt == 1) {
|
||||
icon = new ImageIcon(getClass().getResource("/frame_compute_method.jpg"));
|
||||
}
|
||||
else if (idInt < 20) {
|
||||
icon = new ImageIcon(getClass().getResource("/frame_power_detection.jpg"));
|
||||
}
|
||||
else {
|
||||
try {
|
||||
String path = lastJob.getOutputImagePath();
|
||||
|
||||
BufferedImage img = ImageIO.read(new File(path));
|
||||
float width = img.getWidth();
|
||||
float height = img.getHeight();
|
||||
float factor = 1.0f;
|
||||
if (height > 200) {
|
||||
factor = 200f / height;
|
||||
}
|
||||
if (width * factor > 200) {
|
||||
factor = Math.min(factor, 200f / width);
|
||||
}
|
||||
icon = new ImageIcon(img.getScaledInstance((int) (width * factor), (int) (height * factor), Image.SCALE_FAST));
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error(String.format("Working::showLastRender() Unable to load/preview rendered frame [%s]. Exception %s",
|
||||
lastJob.getOutputImagePath(), e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
if (icon != null) {
|
||||
lastRender.setIcon(icon);
|
||||
// don't use lastJob.getProcessRender().getDuration() due to timezone
|
||||
if (lastJob.getProcessRender().getDuration() > 1) {
|
||||
lastRenderTime.setText("Render time : " + Utils
|
||||
.humanDuration(new Date(lastJob.getProcessRender().getEndTime() - lastJob.getProcessRender().getStartTime())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void alignPanel(Container parent, int rows, int cols, Spring width) {
|
||||
SpringLayout layout;
|
||||
try {
|
||||
layout = (SpringLayout) parent.getLayout();
|
||||
}
|
||||
catch (ClassCastException exc) {
|
||||
System.err.println("The first argument to makeCompactGrid must use SpringLayout.");
|
||||
return;
|
||||
}
|
||||
|
||||
Spring x = Spring.constant(0);
|
||||
for (int c = 0; c < cols; c++) {
|
||||
for (int r = 0; r < rows; r++) {
|
||||
SpringLayout.Constraints constraints = getConstraintsForCell(r, c, parent, cols);
|
||||
constraints.setX(x);
|
||||
constraints.setWidth(width);
|
||||
}
|
||||
x = Spring.sum(x, width);
|
||||
}
|
||||
|
||||
Spring y = Spring.constant(0);
|
||||
for (int r = 0; r < rows; r++) {
|
||||
Spring height = Spring.constant(0);
|
||||
for (int c = 0; c < cols; c++) {
|
||||
height = Spring.max(height, getConstraintsForCell(r, c, parent, cols).getHeight());
|
||||
}
|
||||
for (int c = 0; c < cols; c++) {
|
||||
SpringLayout.Constraints constraints = getConstraintsForCell(r, c, parent, cols);
|
||||
constraints.setY(y);
|
||||
constraints.setHeight(height);
|
||||
}
|
||||
y = Spring.sum(y, height);
|
||||
}
|
||||
|
||||
SpringLayout.Constraints pCons = layout.getConstraints(parent);
|
||||
pCons.setConstraint(SpringLayout.SOUTH, y);
|
||||
pCons.setConstraint(SpringLayout.EAST, x);
|
||||
|
||||
}
|
||||
|
||||
private Spring getBestWidth(Container parent, int rows, int cols) {
|
||||
Spring x = Spring.constant(0);
|
||||
Spring width = Spring.constant(0);
|
||||
for (int c = 0; c < cols; c++) {
|
||||
|
||||
for (int r = 0; r < rows; r++) {
|
||||
width = Spring.max(width, getConstraintsForCell(r, c, parent, cols).getWidth());
|
||||
}
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
private SpringLayout.Constraints getConstraintsForCell(int row, int col, Container parent, int cols) {
|
||||
SpringLayout layout = (SpringLayout) parent.getLayout();
|
||||
Component c = parent.getComponent(row * cols + col);
|
||||
return layout.getConstraints(c);
|
||||
}
|
||||
|
||||
class PauseAction implements ActionListener {
|
||||
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
Client client = parent.getClient();
|
||||
if (client != null) {
|
||||
if (client.isSuspended()) {
|
||||
pauseButton.setText("Pause getting new jobs");
|
||||
client.resume();
|
||||
setStatus(previousStatus);
|
||||
}
|
||||
else {
|
||||
pauseButton.setText("Resume getting jobs");
|
||||
client.suspend();
|
||||
setStatus("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsAction implements ActionListener {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
if (parent != null) {
|
||||
parent.showActivity(ActivityType.SETTINGS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExitAfterAction implements ActionListener {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
Client client = parent.getClient();
|
||||
if (client != null) {
|
||||
if (client.isRunning()) {
|
||||
String[] exitJobOptions = { "Exit after current Jobs", "Exit Immediately", "Do Nothing" };
|
||||
int jobsQueueSize = client.getUploadQueueSize() + (client.isRunning() ? 1 : 0);
|
||||
|
||||
int userDecision = JOptionPane.showOptionDialog(null, String.format(
|
||||
"<html>You have <strong>%d frame%s</strong> being uploaded or rendered. Do you want to finish the jobs or exit now?.\n\n",
|
||||
jobsQueueSize, // Add the current frame to the total count ONLY if the client is running
|
||||
(jobsQueueSize > 1 ? "s" : ""), (jobsQueueSize > 1 ? (jobsQueueSize + " ") : ""), (jobsQueueSize > 1 ? "s" : "")),
|
||||
"Exit Now or Later", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, exitJobOptions,
|
||||
exitJobOptions[2]); // Make the "Do nothing" button the default one to avoid mistakes
|
||||
|
||||
if (userDecision == 0) {
|
||||
exitAfterFrame.setText(String.format("Cancel exit (%s frame%s to go)", jobsQueueSize, (jobsQueueSize > 1 ? "s" : "")));
|
||||
|
||||
client.askForStop();
|
||||
}
|
||||
else if (userDecision == 1) {
|
||||
client.stop();
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
exitAfterFrame.setText("Exit");
|
||||
client.cancelStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class blockJobAction implements ActionListener {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
Client client = parent.getClient();
|
||||
if (client != null) {
|
||||
Job job = client.getRenderingJob();
|
||||
if (job != null) {
|
||||
job.block();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Laurent CLOUET
|
||||
*
|
||||
* 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.standalone.swing.components;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.LayoutManager;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import javax.swing.BorderFactory;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.TitledBorder;
|
||||
|
||||
import com.sheepit.client.standalone.swing.activity.Activity;
|
||||
|
||||
public class CollapsibleJPanel extends JPanel {
|
||||
|
||||
private boolean isCompnentsVisible = true;
|
||||
private int originalHeight;
|
||||
private String borderTitle = "";
|
||||
private int COLLAPSED_HEIGHT = 22;
|
||||
private boolean[] originalVisibilty;
|
||||
private Activity parent;
|
||||
|
||||
public CollapsibleJPanel(LayoutManager layoutManager, Activity parent) {
|
||||
setLayout(layoutManager);
|
||||
addMouseListener(new onClickHandler());
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void setCollapsed(boolean aFlag) {
|
||||
if (aFlag) {
|
||||
hideComponents();
|
||||
}
|
||||
else {
|
||||
showComponents();
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleCollapsed() {
|
||||
if (isCompnentsVisible) {
|
||||
setCollapsed(true);
|
||||
}
|
||||
else {
|
||||
setCollapsed(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void hideComponents() {
|
||||
|
||||
Component[] components = getComponents();
|
||||
|
||||
originalVisibilty = new boolean[components.length];
|
||||
|
||||
// Hide all componens on panel
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
originalVisibilty[i] = components[i].isVisible();
|
||||
components[i].setVisible(false);
|
||||
}
|
||||
|
||||
setHeight(COLLAPSED_HEIGHT);
|
||||
|
||||
// Add '+' char to end of border title
|
||||
//setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), borderTitle + " + "));
|
||||
setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " + " + borderTitle));
|
||||
|
||||
// Update flag
|
||||
isCompnentsVisible = false;
|
||||
}
|
||||
|
||||
private void showComponents() {
|
||||
|
||||
Component[] components = getComponents();
|
||||
|
||||
// Set all components in panel to visible
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
components[i].setVisible(originalVisibilty[i]);
|
||||
}
|
||||
|
||||
setHeight(originalHeight);
|
||||
|
||||
// Add '-' char to end of border title
|
||||
setBorder(BorderFactory.createTitledBorder(" - " + borderTitle));
|
||||
|
||||
// Update flag
|
||||
isCompnentsVisible = true;
|
||||
}
|
||||
|
||||
private void setHeight(int height) {
|
||||
setPreferredSize(new Dimension(getPreferredSize().width, height));
|
||||
setMinimumSize(new Dimension(getMinimumSize().width, height));
|
||||
setMaximumSize(new Dimension(getMaximumSize().width, height));
|
||||
}
|
||||
|
||||
@Override public Component add(Component component) { // Need this to get the original height of panel
|
||||
|
||||
Component returnComponent = super.add(component);
|
||||
|
||||
originalHeight = getPreferredSize().height;
|
||||
|
||||
return returnComponent;
|
||||
}
|
||||
|
||||
@Override public void setBorder(Border border) { // Need this to get the border title
|
||||
|
||||
if (border instanceof TitledBorder && (borderTitle == "")) {
|
||||
borderTitle = ((TitledBorder) border).getTitle();
|
||||
|
||||
((TitledBorder) border).setTitle(" - " + borderTitle);
|
||||
}
|
||||
|
||||
super.setBorder(border);
|
||||
}
|
||||
|
||||
public class onClickHandler implements MouseListener {
|
||||
|
||||
@Override public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override public void mousePressed(MouseEvent e) {
|
||||
if (e.getPoint().y < COLLAPSED_HEIGHT) { // Only if click is on top of panel
|
||||
((CollapsibleJPanel) e.getComponent()).toggleCollapsed();
|
||||
}
|
||||
|
||||
// Recalculate the proper window size
|
||||
parent.resizeWindow();
|
||||
}
|
||||
|
||||
@Override public void mouseEntered(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override public void mouseExited(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override public void mouseReleased(MouseEvent e) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Laurent CLOUET
|
||||
* Author Rolf Aretz Lap <rolf.aretz@ottogroup.com>
|
||||
*
|
||||
* 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.standalone.text;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
import com.sheepit.client.Configuration;
|
||||
import com.sheepit.client.Job;
|
||||
|
||||
public class CLIInputActionHandler implements CLIInputListener {
|
||||
|
||||
@Override public void commandEntered(Client client, String command) {
|
||||
int priorityLength = "priority".length();
|
||||
|
||||
//prevent Null Pointer at next step
|
||||
if (command == null) {
|
||||
return;
|
||||
}
|
||||
if (client == null) {
|
||||
return;
|
||||
}
|
||||
if (command.equalsIgnoreCase("block")) {
|
||||
Job job = client.getRenderingJob();
|
||||
if (job != null) {
|
||||
job.block();
|
||||
}
|
||||
}
|
||||
else if (command.equalsIgnoreCase("resume")) {
|
||||
client.resume();
|
||||
}
|
||||
else if (command.equalsIgnoreCase("pause")) {
|
||||
client.suspend();
|
||||
}
|
||||
else if (command.equalsIgnoreCase("stop")) {
|
||||
client.askForStop();
|
||||
}
|
||||
else if (command.equalsIgnoreCase("status")) {
|
||||
displayStatus(client);
|
||||
}
|
||||
else if (command.equalsIgnoreCase("cancel")) {
|
||||
client.cancelStop();
|
||||
}
|
||||
else if (command.equalsIgnoreCase("quit")) {
|
||||
client.stop();
|
||||
System.exit(0);
|
||||
}
|
||||
else if ((command.length() > priorityLength) && (command.substring(0, priorityLength).equalsIgnoreCase("priority"))) {
|
||||
changePriority(client, command.substring(priorityLength));
|
||||
}
|
||||
else {
|
||||
System.out.println("Unknown command: " + command);
|
||||
System.out.println("status: display client status");
|
||||
System.out.println("priority <n>: set the priority for the next renderjob");
|
||||
System.out.println("block: block project");
|
||||
System.out.println("pause: pause client requesting new jobs");
|
||||
System.out.println("resume: resume after client was paused");
|
||||
System.out.println("stop: exit after frame was finished");
|
||||
System.out.println("cancel: cancel exit");
|
||||
System.out.println("quit: exit now");
|
||||
}
|
||||
}
|
||||
|
||||
void changePriority(Client client, String newPriority) {
|
||||
Configuration config = client.getConfiguration();
|
||||
if (config != null) {
|
||||
try {
|
||||
config.setUsePriority(Integer.parseInt(newPriority.trim()));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
System.out.println("Invalid priority: " + newPriority);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void displayStatus(Client client) {
|
||||
if (client.isSuspended()) {
|
||||
System.out.println("Status: paused");
|
||||
}
|
||||
else if (client.isRunning()) {
|
||||
System.out.println("Status: running");
|
||||
}
|
||||
else {
|
||||
System.out.println("Status: will exit after the current frame");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Laurent CLOUET
|
||||
* Author Rolf Aretz Lap <rolf.aretz@ottogroup.com>
|
||||
*
|
||||
* 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.standalone.text;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
|
||||
public interface CLIInputListener {
|
||||
public void commandEntered(Client client, String command);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Laurent CLOUET
|
||||
* Author Rolf Aretz Lap <rolf.aretz@ottogroup.com>
|
||||
*
|
||||
* 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.standalone.text;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
|
||||
public class CLIInputObserver implements Runnable {
|
||||
private BufferedReader in;
|
||||
private Client client;
|
||||
|
||||
public CLIInputObserver(Client client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
private List<CLIInputListener> listeners = new ArrayList<CLIInputListener>();
|
||||
|
||||
public void addListener(CLIInputListener toAdd) {
|
||||
listeners.add(toAdd);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
in = new BufferedReader(new InputStreamReader(System.in));
|
||||
String line = "";
|
||||
|
||||
while ((line != null) && (line.equalsIgnoreCase("quit") == false)) {
|
||||
try {
|
||||
line = in.readLine();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// if the program has been launched into the background (e.g. with nohup), input is not valid
|
||||
client.getLog().info("Unable to read user input, ignoring all further inputs");
|
||||
break;
|
||||
}
|
||||
for (CLIInputListener cliil : listeners)
|
||||
cliil.commandEntered(client, line);
|
||||
}
|
||||
try {
|
||||
in.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// TODO: handle exception
|
||||
client.getLog().error("FIXME: Unhandled exception while closing InputStreamReader(): " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user