This commit is contained in:
Omer
2019-12-09 21:38:37 +02:00
8 changed files with 154 additions and 69 deletions

View File

@@ -3,6 +3,7 @@ package com.chameleonvision;
import com.chameleonvision.config.ConfigManager;
import com.chameleonvision.network.NetworkManager;
import com.chameleonvision.util.Platform;
import com.chameleonvision.util.ShellExec;
import com.chameleonvision.util.Utilities;
import com.chameleonvision.vision.VisionManager;
import com.chameleonvision.web.Server;

View File

@@ -1,5 +1,6 @@
package com.chameleonvision.config;
import com.chameleonvision.util.FileHelper;
import com.chameleonvision.util.JacksonHelper;
import com.chameleonvision.vision.pipeline.CVPipelineSettings;
@@ -17,12 +18,22 @@ public class CameraConfig {
private final String cameraConfigName;
private final CameraJsonConfig preliminaryConfig;
private final Path configFolderPath;
private final Path configPath;
private final Path driverModePath;
final Path pipelineFolderPath;
public final PipelineConfig pipelineConfig;
CameraConfig(CameraJsonConfig config) {
preliminaryConfig = config;
cameraConfigName = preliminaryConfig.name.replace(' ', '_');
pipelineConfig = new PipelineConfig(this);
configFolderPath = Paths.get(camerasConfigFolderPath.toString(), cameraConfigName);
configPath = Paths.get(configFolderPath.toString(), "camera.json");
driverModePath = Paths.get(configFolderPath.toString(), "drivermode.json");
pipelineFolderPath = Paths.get(configFolderPath.toString(), "pipelines");
}
public FullCameraConfiguration load() {
@@ -37,9 +48,9 @@ public class CameraConfig {
private CameraJsonConfig loadConfig() {
CameraJsonConfig config = preliminaryConfig;
try {
config = JacksonHelper.deserializer(getConfigPath(), CameraJsonConfig.class);
config = JacksonHelper.deserializer(configPath, CameraJsonConfig.class);
} catch (IOException e) {
System.err.printf("Failed to load camera config: %s - using default.\n", getConfigPath().toString());
System.err.printf("Failed to load camera config: %s - using default.\n", configPath.toString());
}
return config;
}
@@ -48,18 +59,19 @@ public class CameraConfig {
CVPipelineSettings driverMode = new CVPipelineSettings();
driverMode.nickname = "DRIVERMODE";
try {
driverMode = JacksonHelper.deserializer(getDriverModePath(), CVPipelineSettings.class);
driverMode = JacksonHelper.deserializer(driverModePath, CVPipelineSettings.class);
} catch (IOException e) {
System.err.println("Failed to load camera drivermode: " + getDriverModePath().toString());
System.err.println("Failed to load camera drivermode: " + driverModePath.toString());
}
return driverMode;
}
void saveConfig(CameraJsonConfig config) {
try {
JacksonHelper.serializer(getConfigPath(), config);
JacksonHelper.serializer(configPath, config);
FileHelper.setFilePerms(configPath);
} catch (IOException e) {
System.err.println("Failed to save camera config file: " + getConfigPath().toString());
System.err.println("Failed to save camera config file: " + configPath.toString());
}
}
@@ -69,20 +81,22 @@ public class CameraConfig {
public void saveDriverMode(CVPipelineSettings driverMode) {
try {
JacksonHelper.serializer(getDriverModePath(), driverMode);
JacksonHelper.serializer(driverModePath, driverMode);
FileHelper.setFilePerms(driverModePath);
} catch (IOException e) {
System.err.println("Failed to save camera drivermode file: " + getDriverModePath().toString());
System.err.println("Failed to save camera drivermode file: " + driverModePath.toString());
}
}
void checkFolder() {
if (!getConfigFolderExists()) {
try {
if (!(new File(getConfigFolderPath().toUri()).mkdirs())) {
System.err.println("Failed to create camera config folder: " + getConfigFolderPath().toString());
if (!(new File(configFolderPath.toUri()).mkdirs())) {
System.err.println("Failed to create camera config folder: " + configFolderPath.toString());
}
FileHelper.setFilePerms(configFolderPath);
} catch(Exception e) {
System.err.println("Failed to create camera config folder: " + getConfigFolderPath().toString());
System.err.println("Failed to create camera config folder: " + configFolderPath.toString());
}
}
}
@@ -90,9 +104,10 @@ public class CameraConfig {
private void checkConfig() {
if (!configExists()) {
try {
JacksonHelper.serializer(getConfigPath(), preliminaryConfig);
JacksonHelper.serializer(configPath, preliminaryConfig);
FileHelper.setFilePerms(configPath);
} catch (IOException e) {
System.err.println("Failed to create camera config file: " + getConfigPath().toString());
System.err.println("Failed to create camera config file: " + configPath.toString());
}
}
}
@@ -102,38 +117,23 @@ public class CameraConfig {
try {
CVPipelineSettings newDriverModeSettings = new CVPipelineSettings();
newDriverModeSettings.nickname = "DRIVERMODE";
JacksonHelper.serializer(getDriverModePath(), newDriverModeSettings);
JacksonHelper.serializer(driverModePath, newDriverModeSettings);
FileHelper.setFilePerms(driverModePath);
} catch (IOException e) {
System.err.println("Failed to create camera drivermode file: " + getDriverModePath().toString());
System.err.println("Failed to create camera drivermode file: " + driverModePath.toString());
}
}
}
private Path getConfigFolderPath() {
return Paths.get(camerasConfigFolderPath.toString(), cameraConfigName);
}
private Path getConfigPath() {
return Paths.get(getConfigFolderPath().toString(), "camera.json");
}
private Path getDriverModePath() {
return Paths.get(getConfigFolderPath().toString(), "drivermode.json");
}
private boolean getConfigFolderExists() {
return Files.exists(getConfigFolderPath());
}
Path getPipelineFolderPath() {
return Paths.get(getConfigFolderPath().toString(), "pipelines");
return Files.exists(configFolderPath);
}
private boolean configExists() {
return getConfigFolderExists() && Files.exists(getConfigPath());
return getConfigFolderExists() && Files.exists(configPath);
}
private boolean driverModeExists() {
return getConfigFolderExists() && Files.exists(getDriverModePath());
return getConfigFolderExists() && Files.exists(driverModePath);
}
}

View File

@@ -1,7 +1,7 @@
package com.chameleonvision.config;
import com.chameleonvision.util.ProgramDirectoryUtilities;
import com.chameleonvision.util.JacksonHelper;
import com.chameleonvision.Main;
import com.chameleonvision.util.*;
import com.chameleonvision.vision.pipeline.CVPipelineSettings;
import java.io.File;
@@ -16,7 +16,7 @@ import java.util.List;
public class ConfigManager {
private ConfigManager() {}
static final Path SettingsPath = Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), "settings");
public static final Path SettingsPath = Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), "settings");
private static final Path settingsFilePath = Paths.get(SettingsPath.toString(), "settings.json");
private static final LinkedHashMap<String, CameraConfig> cameraConfigs = new LinkedHashMap<>();
@@ -33,6 +33,9 @@ public class ConfigManager {
System.err.println("Failed to create settings folder: " + SettingsPath.toString());
}
Files.createDirectory(SettingsPath);
if (!Platform.CurrentPlatform.isWindows()) {
new ShellExec().executeBashCommand("sudo chmod -R 0777 " + SettingsPath.toString());
}
} catch (IOException e) {
if(!(e instanceof java.nio.file.FileAlreadyExistsException))
e.printStackTrace();
@@ -45,6 +48,7 @@ public class ConfigManager {
if (settingsFileEmpty || !settingsFileExists()) {
try {
JacksonHelper.serializer(settingsFilePath, settings);
FileHelper.setFilePerms(settingsFilePath);
} catch (IOException e) {
e.printStackTrace();
}
@@ -66,6 +70,7 @@ public class ConfigManager {
private static void saveSettingsFile() {
try {
JacksonHelper.serializer(settingsFilePath, settings);
FileHelper.setFilePerms(settingsFilePath);
} catch (IOException e) {
System.err.println("Failed to save settings.json!");
}

View File

@@ -1,5 +1,6 @@
package com.chameleonvision.config;
import com.chameleonvision.util.FileHelper;
import com.chameleonvision.util.JacksonHelper;
import com.chameleonvision.vision.pipeline.*;
import com.chameleonvision.vision.pipeline.impl.CVPipeline2dSettings;
@@ -29,15 +30,20 @@ public class PipelineConfig {
}
private void checkFolder() {
if ( !(new File(cameraConfig.getPipelineFolderPath().toUri()).mkdirs())) {
if (Files.notExists(cameraConfig.getPipelineFolderPath())) {
if ( !(new File(cameraConfig.pipelineFolderPath.toUri()).mkdirs())) {
if (Files.notExists(cameraConfig.pipelineFolderPath)) {
System.err.println("Failed to create pipelines folder.");
}
}
try {
FileHelper.setFilePerms(cameraConfig.pipelineFolderPath);
} catch (IOException e) {
// ignored
}
}
private File[] getPipelineFiles() {
return new File(cameraConfig.getPipelineFolderPath().toUri()).listFiles();
return new File(cameraConfig.pipelineFolderPath.toUri()).listFiles();
}
private boolean folderHasPipelines() {
@@ -59,7 +65,7 @@ public class PipelineConfig {
String pipelineName = setting.nickname.replace(' ', '_');
String prefix = ((setting instanceof CVPipeline2dSettings) ? CVPipeline2DPrefix : CVPipeline3DPrefix) + "-";
String fullFileName = prefix + pipelineName + ".json";
return Path.of(cameraConfig.getPipelineFolderPath().toString(), fullFileName);
return Path.of(cameraConfig.pipelineFolderPath.toString(), fullFileName);
}
private boolean pipelineExists(CVPipelineSettings setting) {
@@ -73,12 +79,14 @@ public class PipelineConfig {
if (settings instanceof CVPipeline3dSettings) {
try {
JacksonHelper.serializer(path, settings);
FileHelper.setFilePerms(path);
} catch (IOException e) {
e.printStackTrace();
}
} else if (settings instanceof CVPipeline2dSettings) {
try {
JacksonHelper.serializer(path, settings);
FileHelper.setFilePerms(path);
} catch (IOException e) {
e.printStackTrace();
}

View File

@@ -0,0 +1,26 @@
package com.chameleonvision.util;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class FileHelper {
private FileHelper() {}
private static final Set<PosixFilePermission> allReadWriteExecutePerms = new HashSet<>(Arrays.asList(PosixFilePermission.values()));
public static void setFilePerms(Path path) throws IOException {
if (!Platform.CurrentPlatform.isWindows()) {
Set<PosixFilePermission> perms = Files.readAttributes(path, PosixFileAttributes.class).permissions();
if (!perms.equals(allReadWriteExecutePerms)) {
Files.setPosixFilePermissions(path, perms);
}
}
}
}

View File

@@ -69,7 +69,7 @@ public enum Platform {
}
}
public static Platform getCurrentPlatform() {
private static Platform getCurrentPlatform() {
if (OS_NAME.contains("Windows")) {
if (OS_ARCH.equals("amd64")) return Platform.WINDOWS_64;
}

View File

@@ -19,6 +19,43 @@ public class ShellExec {
this.readError = readError;
}
/**
* Execute a bash command. We can handle complex bash commands including
* multiple executions (; | && ||), quotes, expansions ($), escapes (\), e.g.:
* "cd /abc/def; mv ghi 'older ghi '$(whoami)"
* @param command
* @return true if bash got started, but your command may have failed.
*/
public int executeBashCommand(String command) throws IOException {
boolean wait = true;
boolean success = false;
Runtime r = Runtime.getRuntime();
// Use bash -c so we can handle things like multi commands separated by ; and
// things like quotes, $, |, and \. My tests show that command comes as
// one argument to bash, so we do not need to quote it to make it one thing.
// Also, exec may object if it does not have an executable file as the first thing,
// so having bash here makes it happy provided bash is installed and in path.
String[] commands = {"bash", "-c", command};
Process process = r.exec(commands);
// Consume streams, older jvm's had a memory leak if streams were not read,
// some other jvm+OS combinations may block unless streams are consumed.
errorGobbler = new StreamGobbler(process.getErrorStream(), readError);
outputGobbler = new StreamGobbler(process.getInputStream(), readOutput);
errorGobbler.start();
outputGobbler.start();
exitCode = 0;
if (wait) {
try {
process.waitFor();
exitCode = process.exitValue();
} catch (InterruptedException ignored) { }
}
return exitCode;
}
/**
* Execute a command in current folder, and wait for process to end
* @param command command ("c:/some/folder/script.bat" or "some/folder/script.sh")

View File

@@ -51,6 +51,8 @@ public class VisionProcess {
private NetworkTableEntry ntValidEntry;
private ObjectMapper objectMapper = new ObjectMapper();
private long lastUIUpdateMs = 0;
VisionProcess(USBCameraCapture cameraCapture, String name, List<CVPipelineSettings> loadedPipelineSettings) {
this.cameraCapture = cameraCapture;
@@ -144,22 +146,34 @@ public class VisionProcess {
}
private void updateUI(CVPipelineResult data) {
if(cameraCapture.getProperties().name.equals(ConfigManager.settings.currentCamera)) {
HashMap<String, Object> WebSend = new HashMap<>();
HashMap<String, Object> point = new HashMap<>();
HashMap<String, Object> calculated = new HashMap<>();
List<Double> center = new ArrayList<>();
if (data.hasTarget) {
if(data instanceof CVPipeline2d.CVPipeline2dResult) {
CVPipeline2d.CVPipeline2dResult result = (CVPipeline2d.CVPipeline2dResult) data;
CVPipeline2d.Target2d bestTarget = result.targets.get(0);
center.add(bestTarget.rawPoint.center.x);
center.add(bestTarget.rawPoint.center.y);
calculated.put("pitch", bestTarget.pitch);
calculated.put("yaw", bestTarget.yaw);
calculated.put("area", bestTarget.area);
} else if (data instanceof CVPipeline3d.CVPipeline3dResult) {
// TODO: (2.1) 3d stuff in UI
// 30 "FPS" update rate
long currentMillis = System.currentTimeMillis();
if (currentMillis - lastUIUpdateMs > 1000/30) {
lastUIUpdateMs = currentMillis;
if(cameraCapture.getProperties().name.equals(ConfigManager.settings.currentCamera)) {
HashMap<String, Object> WebSend = new HashMap<>();
HashMap<String, Object> point = new HashMap<>();
HashMap<String, Object> calculated = new HashMap<>();
List<Double> center = new ArrayList<>();
if (data.hasTarget) {
if(data instanceof CVPipeline2d.CVPipeline2dResult) {
CVPipeline2d.CVPipeline2dResult result = (CVPipeline2d.CVPipeline2dResult) data;
CVPipeline2d.Target2d bestTarget = result.targets.get(0);
center.add(bestTarget.rawPoint.center.x);
center.add(bestTarget.rawPoint.center.y);
calculated.put("pitch", bestTarget.pitch);
calculated.put("yaw", bestTarget.yaw);
calculated.put("area", bestTarget.area);
} else if (data instanceof CVPipeline3d.CVPipeline3dResult) {
// TODO: (2.1) 3d stuff in UI
} else {
center.add(null);
center.add(null);
calculated.put("pitch", null);
calculated.put("yaw", null);
calculated.put("area", null);
}
} else {
center.add(null);
center.add(null);
@@ -167,18 +181,12 @@ public class VisionProcess {
calculated.put("yaw", null);
calculated.put("area", null);
}
} else {
center.add(null);
center.add(null);
calculated.put("pitch", null);
calculated.put("yaw", null);
calculated.put("area", null);
point.put("fps", visionRunnable.fps);
point.put("calculated", calculated);
point.put("rawPoint", center);
WebSend.put("point", point);
SocketHandler.broadcastMessage(WebSend);
}
point.put("fps", visionRunnable.fps);
point.put("calculated", calculated);
point.put("rawPoint", center);
WebSend.put("point", point);
SocketHandler.broadcastMessage(WebSend);
}
}